2009-07-28 [colin] 3.7.2cvs14
[claws.git] / src / ldapquery.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2009 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 /*
21  * Functions necessary to define and perform LDAP queries.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #ifdef USE_LDAP
29
30 #include <glib.h>
31 #include <sys/time.h>
32 #include <string.h>
33 #include <lber.h>
34
35 #include "defs.h"
36 #include "ldaputil.h"
37 #include "ldapquery.h"
38 #include "ldapctrl.h"
39 #include "mgutils.h"
40
41 #include "addritem.h"
42 #include "addrcache.h"
43 #include "common/utils.h"
44
45 /*
46  * Key for thread specific data.
47  */
48 static pthread_key_t _queryThreadKey_;
49 static gboolean _queryThreadInit_ = FALSE;
50
51 static gboolean callbackend (gpointer data)
52 {
53         LdapQuery *qry = (LdapQuery *)data;
54         qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
55         return FALSE;
56 }
57
58
59 /**
60  * Create new LDAP query object.
61  * \return Initialized query object.
62  */
63 LdapQuery *ldapqry_create( void ) {
64         LdapQuery *qry;
65
66         qry = g_new0( LdapQuery, 1 );
67         ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
68         ADDRQUERY_ID(qry) = 0;
69         ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
70         ADDRQUERY_NAME(qry) = NULL;
71         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
72         ADDRQUERY_FOLDER(qry) = NULL;
73         ADDRQUERY_SEARCHVALUE(qry) = NULL;
74         qry->control = NULL;
75         qry->server = NULL;
76         qry->entriesRead = 0;
77         qry->elapsedTime = 0;
78         qry->stopFlag = FALSE;
79         qry->busyFlag = FALSE;
80         qry->agedFlag = FALSE;
81         qry->completed = FALSE;
82         qry->thread = NULL;
83         qry->callBackEntry = NULL;
84         qry->callBackEnd = NULL;
85         qry->ldap = NULL;
86         qry->data = NULL;
87
88         /* Mutex to protect stop and busy flags */
89         qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
90         pthread_mutex_init( qry->mutexStop, NULL );
91         qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
92         pthread_mutex_init( qry->mutexBusy, NULL );
93
94         /* Mutex to protect critical section */
95         qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
96         pthread_mutex_init( qry->mutexEntry, NULL );
97
98         return qry;
99 }
100
101 /**
102  * Specify the reference to control data that will be used for the query. The calling
103  * module should be responsible for creating and destroying this control object.
104  * \param qry Query object.
105  * \param ctl Control object.
106  */
107 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
108         cm_return_if_fail( qry != NULL );
109         qry->control = ctl;
110 }
111
112 /**
113  * Specify query name to be used.
114  * \param qry   Query object.
115  * \param value Name.
116  */
117 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
118         cm_return_if_fail( qry != NULL );
119         ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
120         if (ADDRQUERY_NAME(qry) == NULL)
121                 return;
122         g_strstrip( ADDRQUERY_NAME(qry) );
123         debug_print("set name: %s\n", ADDRQUERY_NAME(qry));
124 }
125
126 /**
127  * Specify search value to be used.
128  * \param qry Query object.
129  * \param value 
130  */
131 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
132         cm_return_if_fail( qry != NULL );
133         ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
134         if (ADDRQUERY_SEARCHVALUE(qry) == NULL)
135                 return;
136         g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
137         debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry));
138 }
139
140 /**
141  * Specify query type.
142  * \param qry Query object.
143  * \param value Query type, either:
144  * <ul>
145  * <li><code>LDAPQUERY_NONE</code></li>
146  * <li><code>LDAPQUERY_STATIC</code></li>
147  * <li><code>LDAPQUERY_DYNAMIC</code></li>
148  * </ul>
149  */
150 /*
151 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
152         ADDRQUERY_TYPE(qry) = value;
153 }
154 */
155
156 /**
157  * Specify search type.
158  * \param qry   Query object.
159  * \param value Type.
160  */
161 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
162         cm_return_if_fail( qry != NULL );
163         ADDRQUERY_SEARCHTYPE(qry) = value;
164 }
165
166 /**
167  * Specify query ID.
168  * \param qry Query object.
169  * \param value ID for the query.
170  */
171 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
172         cm_return_if_fail( qry != NULL );
173         ADDRQUERY_ID(qry) = value;
174 }
175
176 /**
177  * Register a callback function that will be executed when each entry
178  * has been read and processed. When called, the function will be passed
179  * this query object and a GList of ItemEMail objects as arguments. An
180  * example of typical usage is shown below.
181  *
182  * <pre>
183  * ------------------------------------------------------------
184  * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
185  *   GList *node;
186  *
187  *   node = listEMail;
188  *   while( node ) {
189  *     ItemEMail *email = node->data;
190  *     ... process email object ...
191  *     node = g_list_next( node );
192  *   }
193  *   g_list_free( listEMail );
194  * }
195  * ...
196  * ...
197  * ldapqry_set_callback_entry( qry, myCallbackEntry );
198  * ------------------------------------------------------------
199  * </pre>
200  *
201  * \param qry Query object.
202  * \param func Function.
203  */
204 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
205         pthread_mutex_lock( qry->mutexEntry );
206         qry->callBackEntry = func;
207         pthread_mutex_unlock( qry->mutexEntry );
208 }
209
210 /**
211  * Register a callback function that will be executed when the search
212  * is complete. When called, the function will be passed this query
213  * object as an argument.
214  * \param qry Query object.
215  * \param func Function.
216  */
217 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
218         qry->callBackEnd = func;
219 }
220
221 /**
222  * Notify query to start/stop executing. This method should be called with a
223  * value if <i>TRUE</i> to terminate an existing running query.
224  *
225  * \param qry Query object.
226  * \param value Value of stop flag.
227  */
228 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
229         cm_return_if_fail( qry != NULL );
230
231         pthread_mutex_lock( qry->mutexStop );
232         qry->stopFlag = value;
233         pthread_mutex_unlock( qry->mutexStop );
234 }
235
236 /**
237  * Test value of stop flag. This method should be used to determine whether a
238  * query has stopped running.
239  * \param qry Query object.
240  * \return Value of stop flag.
241  */
242 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
243         gboolean value;
244         cm_return_val_if_fail( qry != NULL, TRUE );
245
246         pthread_mutex_lock( qry->mutexStop );
247         value = qry->stopFlag;
248         pthread_mutex_unlock( qry->mutexStop );
249         return value;
250 }
251
252 /**
253  * Set busy flag.
254  * \param qry Query object.
255  * \param value Value of busy flag.
256  */
257 static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
258         cm_return_if_fail( qry != NULL );
259         if (qry->mutexBusy == NULL)
260                 return; /* exiting, mutex already freed */
261
262         pthread_mutex_lock( qry->mutexBusy );
263         qry->busyFlag = value;
264         pthread_mutex_unlock( qry->mutexBusy );
265 }
266
267 /**
268  * Test value of busy flag. This method will return a value of <i>FALSE</i>
269  * when a query has completed running.
270  * \param qry Query object.
271  * \return Value of busy flag.
272  */
273 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
274         gboolean value;
275         cm_return_val_if_fail( qry != NULL, FALSE );
276
277         pthread_mutex_lock( qry->mutexBusy );
278         value = qry->busyFlag;
279         pthread_mutex_unlock( qry->mutexBusy );
280         return value;
281 }
282
283 /**
284  * Set query aged flag.
285  * \param qry Query object.
286  * \param value Value of aged flag.
287  */
288 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
289         cm_return_if_fail( qry != NULL );
290         qry->agedFlag = value;
291 }
292
293 /**
294  * Clear LDAP query member variables.
295  * \param qry Query object.
296  */
297 static void ldapqry_clear( LdapQuery *qry ) {
298         cm_return_if_fail( qry != NULL );
299
300         /* Free internal stuff */
301         g_free( ADDRQUERY_NAME(qry) );
302         g_free( ADDRQUERY_SEARCHVALUE(qry) );
303
304         /* Clear pointers and value */
305         ADDRQUERY_NAME(qry) = NULL;
306         ADDRQUERY_SEARCHVALUE(qry) = NULL;
307         ADDRQUERY_ID(qry) = 0;
308         ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
309         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
310         qry->entriesRead = 0;
311         qry->elapsedTime = 0;
312         qry->stopFlag = FALSE;
313         qry->busyFlag = FALSE;
314         qry->agedFlag = FALSE;
315         qry->completed = FALSE;
316         qry->callBackEntry = NULL;
317         qry->callBackEnd = NULL;
318         qry->ldap = NULL;
319         qry->data = NULL;
320 }
321
322 /**
323  * Free up LDAP query object by releasing internal memory. Note that
324  * the thread object will be freed by the OS.
325  * \param qry Query object to process.
326  */
327 void ldapqry_free( LdapQuery *qry ) {
328         cm_return_if_fail( qry != NULL );
329
330         /* Clear out internal members */
331         ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
332         ldapqry_clear( qry );
333
334         /* Free the mutex */
335         pthread_mutex_destroy( qry->mutexStop );
336         pthread_mutex_destroy( qry->mutexBusy );
337         pthread_mutex_destroy( qry->mutexEntry );
338         g_free( qry->mutexStop );
339         g_free( qry->mutexBusy );
340         g_free( qry->mutexEntry );
341         qry->mutexEntry = NULL;
342         qry->mutexBusy = NULL;
343         qry->mutexStop = NULL;
344
345         /* Do not free folder - parent server object should free */     
346         ADDRQUERY_FOLDER(qry) = NULL;
347
348         /* Do not free thread - thread should be terminated before freeing */
349         qry->thread = NULL;
350
351         /* Do not free LDAP control - should be destroyed before freeing */
352         qry->control = NULL;
353
354         /* Now release object */
355         g_free( qry );
356 }
357
358 /**
359  * Free linked lists of character strings.
360  * \param listName  List of common names.
361  * \param listAddr  List of addresses.
362  * \param listFirst List of first names.
363  * \param listLast  List of last names.
364  */
365 static void ldapqry_free_lists(
366                 GSList *listName, GSList *listAddr, GSList *listFirst,
367                 GSList *listLast, GSList *listDisplay, GSList *other_attrs )
368 {
369         GSList *cur = other_attrs;
370         mgu_free_list( listName );
371         mgu_free_list( listAddr );
372         mgu_free_list( listFirst );
373         mgu_free_list( listLast );
374         mgu_free_list( listDisplay );
375         for(;cur; cur = cur->next)
376                 addritem_free_attribute((UserAttribute *)cur->data);
377         g_slist_free(other_attrs);
378 }
379
380 /**
381  * Add all LDAP attribute values to a list.
382  * \param ld LDAP handle.
383  * \param entry LDAP entry to process.
384  * \param attr  LDAP attribute.
385  * \return List of values.
386  */
387 static GSList *ldapqry_add_list_values(
388                 LDAP *ld, LDAPMessage *entry, char *attr )
389 {
390         GSList *list = NULL;
391         gint i;
392         struct berval **vals;
393
394         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
395                 for( i = 0; vals[i] != NULL; i++ ) {
396                         /*debug_print("lv\t%s: %s\n", attr?attr:"null",
397                                         vals[i]->bv_val?vals[i]->bv_val:"null");*/
398                         list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
399                 }
400         }
401         ldap_value_free_len( vals );
402         return list;
403 }
404
405 /**
406  * Add a single attribute value to a list.
407  * \param  ld    LDAP handle.
408  * \param  entry LDAP entry to process.
409  * \param  attr  LDAP attribute name to process.
410  * \return List of values; only one value will be present.
411  */
412 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
413         GSList *list = NULL;
414         struct berval **vals;
415
416         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
417                 if( vals[0] != NULL ) {
418                         if (strcmp(attr, "jpegPhoto")) {
419                                 debug_print("sv\t%s: %s\n", attr?attr:"null",
420                                                 vals[0]->bv_val?vals[0]->bv_val:"null");
421                                 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
422                         } else {
423                                 char *file = get_tmp_file();
424                                 FILE *fp = g_fopen(file, "wb");
425                                 if (fp) {
426                                         fwrite(vals[0]->bv_val, 1, vals[0]->bv_len, fp);
427                                         fclose(fp);
428                                 }
429                                 list = g_slist_append( list, file);
430                         }
431                 }
432         }
433         ldap_value_free_len( vals );
434         return list;
435 }
436
437 /**
438  * Build an address list entry and append to list of address items. Name is formatted
439  * as "<first-name> <last-name>".
440  *
441  * \param  cache     Address cache to load.
442  * \param  qry Query object to process.
443  * \param  dn        DN for entry found on server.
444  * \param  listName  List of common names for entry; see notes below.
445  * \param  listAddr  List of EMail addresses for entry.
446  * \param  listFirst List of first names for entry.
447  * \param  listLast  List of last names for entry.
448  *
449  * \return List of ItemEMail objects.
450  *
451  * Notes:
452  * 1) Each LDAP server entry may have multiple LDAP attributes with the same
453  *    name. For example, a single entry for a person may have more than one
454  *    common name, email address, etc.
455 *
456  * 2) The DN for the entry is unique for the server.
457  */
458 static GList *ldapqry_build_items_fl(
459                 AddressCache *cache, LdapQuery *qry, gchar *dn,
460                 GSList *listName, GSList *listAddr, GSList *listFirst,
461                 GSList *listLast, GSList *listDisplay, GSList *attributes )
462 {
463         GSList *nodeAddress, *cur;
464         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
465         gboolean allocated = FALSE;
466         ItemPerson *person;
467         ItemEMail *email;
468         ItemFolder *folder;
469         gchar *picfile = NULL;
470         GList *listReturn = NULL;
471
472         folder = ADDRQUERY_FOLDER(qry);
473         if( folder == NULL ) return listReturn;
474         if( listAddr == NULL ) return listReturn;
475
476         if ( listDisplay ) {
477                 allocated = FALSE;
478                 fullName = listDisplay->data;
479         }
480
481         /* Find longest first name in list */
482         firstName = mgu_slist_longest_entry( listFirst );
483
484         /* Format last name */
485         if( listLast ) {
486                 lastName = listLast->data;
487         }
488
489         if ( fullName == NULL ) {
490                 /* Find longest common name */
491                 allocated = FALSE;
492                 fullName = mgu_slist_longest_entry( listName );
493                 if( fullName == NULL ) {
494                         /* Format a full name from first and last names */
495                         if( firstName ) {
496                                 if( lastName ) {
497                                         fullName = g_strdup_printf( "%s %s", firstName, lastName );
498                                 }
499                                 else {
500                                         fullName = g_strdup_printf( "%s", firstName );
501                                 }
502                         }
503                         else {
504                                 if( lastName ) {
505                                         fullName = g_strdup_printf( "%s", lastName );
506                                 }
507                         }
508                         if( fullName ) {
509                                 g_strchug( fullName ); g_strchomp( fullName );
510                                 allocated = TRUE;
511                         }
512                 }
513         }
514
515         /* Add person into folder */            
516         person = addritem_create_item_person();
517         addritem_person_set_common_name( person, fullName );
518         addritem_person_set_first_name( person, firstName );
519         addritem_person_set_last_name( person, lastName );
520         addritem_person_set_nick_name( person, fullName );
521         addrcache_id_person( cache, person );
522         addritem_person_set_external_id( person, dn );
523         
524         for (cur = attributes; cur; cur = cur->next) {
525                 UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
526                 if (attrib->name && strcmp(attrib->name, "jpegPhoto")) {
527                         addritem_person_add_attribute( person, attrib );
528                 } else {
529                         if (qry && qry->server && qry->server->control) {
530                                 gchar *dir = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, 
531                                                         ADDRBOOK_DIR, G_DIR_SEPARATOR_S, NULL );
532                                 gchar *filename = g_strdup_printf("%s-%s-%s",
533                                         qry->server->control->hostName?qry->server->control->hostName:"nohost", 
534                                         qry->server->control->baseDN?qry->server->control->baseDN:"nobase", 
535                                         dn);
536                                 picfile = g_strdup_printf("%s%s.png", dir, filename);
537                                 addritem_person_set_picture( person, filename );
538                                 rename_force(attrib->value, picfile);
539                                 g_free(filename);
540                                 g_free(picfile);
541                                 g_free(dir);
542                         }
543                 }
544         }
545         
546         addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
547
548         qry->entriesRead++;
549
550         /* Add each address item */
551         nodeAddress = listAddr;
552         while( nodeAddress ) {
553                 email = addritem_create_item_email();
554                 addritem_email_set_address( email, nodeAddress->data );
555                 addrcache_id_email( cache, email );
556                 addrcache_person_add_email( cache, person, email );
557                 addritem_person_add_email( person, email );
558                 /*if (debug_get_mode()) {
559                         addritem_print_item_email(email, stdout);
560                 }*/
561                 listReturn = g_list_append( listReturn, email );
562                 nodeAddress = g_slist_next( nodeAddress );
563         }
564
565         /* Free any allocated memory */
566         if( allocated ) {
567                 g_free( fullName );
568         }
569         fullName = firstName = lastName = NULL;
570
571         return listReturn;
572 }
573
574 /**
575  * Process a single search entry.
576  * \param  cache Address cache to load.
577  * \param  qry   Query object to process.
578  * \param  ld    LDAP handle.
579  * \param  e     LDAP message.
580  * \return List of EMail objects found.
581  */
582 static GList *ldapqry_process_single_entry(
583                 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
584 {
585         char *dnEntry;
586         char *attribute;
587         LdapControl *ctl;
588         BerElement *ber;
589         GSList *listName = NULL, *listAddress = NULL;
590         GSList *listFirst = NULL, *listLast = NULL;
591         GSList *listDisplay = NULL;
592         GSList *other_attrs = NULL;
593         GList *listReturn;
594
595         listReturn = NULL;
596         ctl = qry->control;
597         dnEntry = ldap_get_dn( ld, e );
598         debug_print( "DN: %s\n", dnEntry?dnEntry:"null" );
599
600         /* Process all attributes */
601         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
602                 attribute = ldap_next_attribute( ld, e, ber ) ) {
603                 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
604                         listAddress = ldapqry_add_list_values( ld, e, attribute );
605                 }
606                 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
607                         listName = ldapqry_add_list_values( ld, e, attribute );
608                 }
609                 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
610                         listFirst = ldapqry_add_list_values( ld, e, attribute );
611                 }
612                 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
613                         listLast = ldapqry_add_single_value( ld, e, attribute );
614                 } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
615                         listDisplay = ldapqry_add_single_value( ld, e, attribute );
616                 } else {
617                         GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
618                         UserAttribute *attrib = addritem_create_attribute();
619                         const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
620                         if (attvalue) {
621                                 addritem_attrib_set_name( attrib, attribute );
622                                 addritem_attrib_set_value( attrib, attvalue );
623                                 other_attrs = g_slist_prepend(other_attrs, attrib);
624                         }
625                         mgu_free_list(attlist);
626                 }
627                 /* Free memory used to store attribute */
628                 ldap_memfree( attribute );
629         }
630
631         /* Format and add items to cache */
632         listReturn = ldapqry_build_items_fl(
633                 cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
634
635         /* Free up */
636         ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
637         listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
638
639         if( ber != NULL ) {
640                 ber_free( ber, 0 );
641         }
642         g_free( dnEntry );
643
644         return listReturn;
645 }
646
647 /**
648  * Check parameters that are required for a search. This should
649  * be called before performing a search.
650  * \param  qry Query object to process.
651  * \return <i>TRUE</i> if search criteria appear OK.
652  */
653 gboolean ldapqry_check_search( LdapQuery *qry ) {
654         LdapControl *ctl;
655         ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
656
657         /* Test for control data */
658         ctl = qry->control;
659         if( ctl == NULL ) {
660                 return FALSE;
661         }
662
663         /* Test for search value */
664         if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
665                 return FALSE;
666         }
667         if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
668                 return FALSE;
669         }
670         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
671         return TRUE;
672 }
673
674 /**
675  * Touch the query. This nudges the touch time with the current time.
676  * \param qry Query object to process.
677  */
678 void ldapqry_touch( LdapQuery *qry ) {
679         qry->touchTime = time( NULL );
680         qry->agedFlag = FALSE;
681 }
682
683 /**
684  * Connect to LDAP server.
685  * \param  qry Query object to process.
686  * \return Error/status code.
687  */
688 static gint ldapqry_connect( LdapQuery *qry ) {
689         LdapControl *ctl;
690         LDAP *ld = NULL;
691         gint rc;
692         gint version;
693         gchar *uri = 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         ldapsrv_set_options (ctl->timeOut, NULL);
711
712         uri = g_strdup_printf("ldap%s://%s:%d",
713                                 ctl->enableSSL?"s":"",
714                                 ctl->hostName, ctl->port);
715         ldap_initialize(&ld, uri);
716         g_free(uri);
717
718         if (ld == NULL)
719                 return ADDRQUERY_RETVAL(qry);
720
721         qry->ldap = ld;
722         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
723         if( ldapqry_get_stop_flag( qry ) ) {
724                 return ADDRQUERY_RETVAL(qry);
725         }
726         ldapqry_touch( qry );
727
728         debug_print("connected to LDAP host %s on port %d\n",
729                         ctl->hostName?ctl->hostName:"null", ctl->port);
730
731 #ifdef USE_LDAP_TLS
732         /* Handle TLS */
733         version = LDAP_VERSION3;
734         rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
735         if( rc == LDAP_OPT_SUCCESS ) {
736                 ctl->version = LDAP_VERSION3;
737         }
738
739         if( ctl->version == LDAP_VERSION3 ) {
740                 if( ctl->enableTLS && !ctl->enableSSL ) {
741                         ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
742                         rc = ldap_start_tls_s( ld, NULL, NULL );
743                         
744                         debug_print("rc=%d\n", rc);
745                         debug_print("LDAP Status: set_option: %s\n", ldap_err2string(rc));
746
747                         if( rc != LDAP_SUCCESS ) {
748                                 return ADDRQUERY_RETVAL(qry);
749                         }
750                 }
751         }
752 #endif
753
754         /* Bind to the server, if required */
755         ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
756         if( ctl->bindDN ) {
757                 if( * ctl->bindDN != '\0' ) {
758                         debug_print("binding...\n");
759                         rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
760                         debug_print("rc=%d\n", rc);
761                         if( rc != LDAP_SUCCESS ) {
762                                 debug_print("LDAP Error: ldap_simple_bind_s: %s\n",     ldap_err2string(rc));
763                                 return ADDRQUERY_RETVAL(qry);
764                         }
765                 }
766         }
767         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
768         if( ldapqry_get_stop_flag( qry ) ) {
769                 return ADDRQUERY_RETVAL(qry);
770         }
771         ldapqry_touch( qry );
772
773         ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
774
775         return ADDRQUERY_RETVAL(qry);
776 }
777
778 /**
779  * Connect to LDAP server.
780  * \param  qry Query object to process.
781  * \return Error/status code.
782  */
783 static gint ldapqry_disconnect( LdapQuery *qry ) {
784         /* Disconnect */
785         if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
786         qry->ldap = NULL;
787
788         ldapqry_touch( qry );
789         qry->elapsedTime = qry->touchTime - qry->startTime;
790
791         return ADDRQUERY_RETVAL(qry);
792 }
793
794 /**
795  * Perform the LDAP search, reading LDAP entries into cache.
796  * Note that one LDAP entry can have multiple values for many of its
797  * attributes. If these attributes are E-Mail addresses; these are
798  * broken out into separate address items. For any other attribute,
799  * only the first occurrence is read.
800  * 
801  * \param  qry Query object to process.
802  * \return Error/status code.
803  */
804 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
805         LdapControl *ctl;
806         LDAP *ld;
807         LDAPMessage *result, *e = NULL;
808         char **attribs;
809         gchar *criteria;
810         gboolean searchFlag;
811         gboolean entriesFound;
812         gboolean first;
813         struct timeval timeout;
814         gint rc;
815         AddressCache *cache;
816         GList *listEMail;
817
818         /* Initialize some variables */
819         ld = qry->ldap;
820         ctl = qry->control;
821         cache = qry->server->addressCache;
822         timeout.tv_sec = ctl->timeOut;
823         timeout.tv_usec = 0L;
824         entriesFound = FALSE;
825         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
826
827         /* Define all attributes we are interested in. */
828         attribs = ldapctl_full_attribute_array( ctl );
829
830         /* Create LDAP search string */
831         criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
832         debug_print("Search criteria ::%s::\n", criteria?criteria:"null");
833
834         /*
835          * Execute the search - this step may take some time to complete
836          * depending on network traffic and server response time.
837          */
838         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
839         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
840                 attribs, 0, NULL, NULL, &timeout, 0, &result );
841         debug_print("LDAP Error: ldap_search_st: %d\n", rc);
842         debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
843         ldapctl_free_attribute_array( attribs );
844         g_free( criteria );
845         criteria = NULL;
846         if( rc == LDAP_TIMEOUT ) {
847                 return ADDRQUERY_RETVAL(qry);
848         }
849         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
850
851         /* Test valid returns */
852         searchFlag = FALSE;
853         if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
854                 searchFlag = TRUE;
855         }
856         else if( rc == LDAP_SUCCESS ) {
857                 searchFlag = TRUE;
858         }
859         else if( rc == LDAP_PARTIAL_RESULTS || ldap_count_entries(ld, result) > 0 ) {
860                 searchFlag = TRUE;
861         }
862         else {
863                 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
864                 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
865                 return ADDRQUERY_RETVAL(qry);
866         }
867         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
868
869         debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
870
871         /* Process results */
872         first = TRUE;
873         while( searchFlag ) {
874                 ldapqry_touch( qry );
875                 if( qry->entriesRead >= ctl->maxEntries ) break;                
876
877                 /* Test for stop */             
878                 if( ldapqry_get_stop_flag( qry ) ) {
879                         break;
880                 }
881
882                 /* Retrieve entry */            
883                 if( first ) {
884                         first = FALSE;
885                         e = ldap_first_entry( ld, result );
886                 }
887                 else {
888                         e = ldap_next_entry( ld, e );
889                 }
890                 if( e == NULL ) break;
891                 entriesFound = TRUE;
892
893                 /* Setup a critical section here */
894                 pthread_mutex_lock( qry->mutexEntry );
895
896                 /* Process entry */
897                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
898
899                 /* Process callback */
900                 if( qry->callBackEntry ) {
901                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
902                 }
903                 else {
904                         /*if (debug_get_mode()) {
905                                 GList *node = listEMail;
906                                 while (node) {
907                                         addritem_print_item_email(node->data, stdout);
908                                         node = g_list_next(node);
909                                 }
910                         }*/
911                         g_list_free( listEMail );
912                 }
913                 pthread_mutex_unlock( qry->mutexEntry );
914         }
915
916         /* Free up and disconnect */
917         ldap_msgfree( result );
918
919         if( searchFlag ) {
920                 if( entriesFound ) {
921                         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
922                 }
923                 else {
924                         ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
925                 }
926         }
927
928         return ADDRQUERY_RETVAL(qry);
929 }
930
931 /**
932  * Connection, perform search and disconnect.
933  * \param  qry Query object to process.
934  * \return Error/status code.
935  */
936 static gint ldapqry_perform_search( LdapQuery *qry ) {
937         /* Check search criteria */     
938         if( ! ldapqry_check_search( qry ) ) {
939                 return ADDRQUERY_RETVAL(qry);
940         }
941
942         /* Connect */
943         qry->ldap = NULL;
944         ldapqry_connect( qry );
945         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
946                 /* Perform search */
947                 ldapqry_search_retrieve( qry );
948         }
949         /* Disconnect */
950         ldapqry_disconnect( qry );
951         qry->ldap = NULL;
952
953         return ADDRQUERY_RETVAL(qry);
954 }
955
956 static gint ldapqry_perform_locate( LdapQuery *qry );
957
958 /**
959  * Wrapper around search.
960  * \param  qry Query object to process.
961  * \return Error/status code.
962  */
963 static gint ldapqry_search( LdapQuery *qry ) {
964         gint retVal;
965
966         cm_return_val_if_fail( qry != NULL, -1 );
967         cm_return_val_if_fail( qry->control != NULL, -1 );
968
969         ldapqry_touch( qry );
970         qry->completed = FALSE;
971
972         /* Setup pointer to thread specific area */
973         pthread_setspecific( _queryThreadKey_, qry );
974
975         pthread_detach( pthread_self() );
976         
977         /* Now perform the search */
978         qry->entriesRead = 0;
979         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
980         ldapqry_set_busy_flag( qry, TRUE );
981         ldapqry_set_stop_flag( qry, FALSE );
982         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
983                 retVal = ldapqry_perform_locate( qry );
984         }
985         else {
986                 retVal = ldapqry_perform_search( qry );
987         }
988         if( retVal == LDAPRC_SUCCESS ) {
989                 qry->server->addressCache->dataRead = TRUE;
990                 qry->server->addressCache->accessFlag = FALSE;
991                 if( ldapqry_get_stop_flag( qry ) ) {
992                         debug_print("Search was terminated prematurely\n");
993                 }
994                 else {
995                         ldapqry_touch( qry );
996                         qry->completed = TRUE;
997                         debug_print("Search ran to completion\n");
998                 }
999         }
1000         ldapqry_set_stop_flag( qry, TRUE );
1001         ldapqry_set_busy_flag( qry, FALSE );
1002
1003         /* Process callback */  
1004         if( qry->callBackEnd ) {
1005                 g_timeout_add(0, callbackend, qry);
1006         }
1007
1008         return ADDRQUERY_RETVAL(qry);
1009 }
1010
1011 /**
1012  * Read data into list using a background thread. Callback function will be
1013  * notified when search is complete.
1014  * \param  qry Query object to process.
1015  * \return Error/status code.
1016  */
1017 gint ldapqry_read_data_th( LdapQuery *qry ) {
1018         cm_return_val_if_fail( qry != NULL, -1 );
1019         cm_return_val_if_fail( qry->control != NULL, -1 );
1020
1021         ldapqry_set_stop_flag( qry, FALSE );
1022         ldapqry_touch( qry );
1023         if( ldapqry_check_search( qry ) ) {
1024                 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1025                         debug_print("Starting LDAP search thread\n");
1026                         ldapqry_set_busy_flag( qry, TRUE );
1027                         qry->thread = g_malloc0( sizeof( pthread_t ) );
1028
1029                         /* Setup thread */                      
1030                         pthread_create( qry->thread, NULL,
1031                                 (void *) ldapqry_search, (void *) qry );
1032                 }
1033         }
1034         return ADDRQUERY_RETVAL(qry);
1035 }
1036
1037 /**
1038  * Cleanup LDAP thread data. This function will be called when each thread
1039  * exits. Note that the thread object will be freed by the kernel.
1040  * \param ptr Pointer to object being destroyed (a query object in this case).
1041  */
1042 static void ldapqry_destroyer( void * ptr ) {
1043         LdapQuery *qry;
1044
1045         qry = ( LdapQuery * ) ptr;
1046         cm_return_if_fail( qry != NULL );
1047
1048         debug_print("ldapqry_destroyer::%d::%s\n", (int) pthread_self(),
1049                         ADDRQUERY_NAME(qry)?ADDRQUERY_NAME(qry):"null");
1050
1051         /* Perform any destruction here */
1052         if( qry->control != NULL ) {
1053                 ldapctl_free( qry->control );
1054         }
1055         qry->control = NULL;
1056         qry->thread = NULL;
1057         ldapqry_set_busy_flag( qry, FALSE );
1058 }
1059
1060 /**
1061  * Cancel thread associated with query.
1062  * \param qry Query object to process.
1063  */
1064 void ldapqry_cancel( LdapQuery *qry ) {
1065         cm_return_if_fail( qry != NULL );
1066
1067         debug_print("cancelling::%d::%s\n", (int) pthread_self(),
1068                         ADDRQUERY_NAME(qry)?ADDRQUERY_NAME(qry):"null");
1069         if( ldapqry_get_busy_flag( qry ) ) {
1070                 if( qry->thread ) {
1071                         debug_print("calling pthread_cancel\n");
1072                         pthread_cancel( * qry->thread );
1073                 }
1074         }
1075 }
1076
1077 /**
1078  * Initialize LDAP query. This function should be called once before executing
1079  * any LDAP queries to initialize thread specific data.
1080  */
1081 void ldapqry_initialize( void ) {
1082         debug_print("ldapqry_initialize...\n");
1083         if( ! _queryThreadInit_ ) {
1084                 debug_print("ldapqry_initialize::creating thread specific area\n");
1085                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1086                 _queryThreadInit_ = TRUE;
1087         }
1088         debug_print("ldapqry_initialize... done!\n");
1089 }
1090
1091 /**
1092  * Age the query based on LDAP control parameters.
1093  * \param qry    Query object to process.
1094  * \param maxAge Maximum age of query (in seconds).
1095  */
1096 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1097         gint age;
1098
1099         cm_return_if_fail( qry != NULL );
1100
1101         /* Limit the time that queries can hang around */       
1102         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1103
1104         /* Check age of query */
1105         age = time( NULL ) - qry->touchTime;
1106         if( age > maxAge ) {
1107                 qry->agedFlag = TRUE;
1108         }
1109 }
1110
1111 /**
1112  * Delete folder associated with query results.
1113  * \param qry Query object to process.
1114  */
1115 void ldapqry_delete_folder( LdapQuery *qry ) {
1116         AddressCache *cache;
1117         ItemFolder *folder;
1118
1119         cm_return_if_fail( qry != NULL );
1120
1121         folder = ADDRQUERY_FOLDER(qry);
1122         if( folder ) {
1123                 cache = qry->server->addressCache;
1124                 folder = addrcache_remove_folder_delete( cache, folder );
1125                 if( folder ) {
1126                         addritem_free_item_folder( folder );
1127                 }
1128                 ADDRQUERY_FOLDER(qry) = NULL;
1129         }
1130 }
1131
1132 /**
1133  * Create a name/value pair object.
1134  * \param n Name.
1135  * \param v Value.
1136  * \return Initialized object.
1137  */
1138 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1139         NameValuePair *nvp = g_new0( NameValuePair, 1 );
1140
1141         nvp->name = g_strdup( n );
1142         nvp->value = g_strdup( v );
1143         return nvp;
1144 }
1145
1146 /**
1147  * Free up name/value pair object.
1148  * \param nvp Name/value object.
1149  */
1150 void ldapqry_free_name_value( NameValuePair *nvp ) {
1151         if( nvp ) {
1152                 g_free( nvp->name );
1153                 g_free( nvp->value );
1154                 nvp->name = nvp->value = NULL;
1155                 g_free( nvp );
1156         }
1157 }
1158
1159 /**
1160  * Free up a list name/value pair objects.
1161  * \param list List of name/value objects.
1162  */
1163 void ldapqry_free_list_name_value( GList *list ) {
1164         GList *node;
1165
1166         node = list;
1167         while( node ) {
1168                 NameValuePair *nvp = ( NameValuePair * ) node->data;
1169                 ldapqry_free_name_value( nvp );
1170                 node->data = NULL;
1171                 node = g_list_next( node );
1172         }
1173         g_list_free( list );
1174 }
1175
1176 /**
1177  * Load a list of name/value pairs from LDAP attributes.
1178  * \param  ld          LDAP handle.
1179  * \param  e          LDAP message.
1180  * \param  attr       Attribute name.
1181  * \param  listValues List to populate.
1182  * \return List of attribute name/value pairs.
1183  */
1184 static GList *ldapqry_load_attrib_values(
1185                 LDAP *ld, LDAPMessage *entry, char *attr,
1186                 GList *listValues )
1187 {
1188         GList *list = NULL;
1189         gint i;
1190         struct berval **vals;
1191         NameValuePair *nvp;
1192
1193         list = listValues;
1194         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1195                 for( i = 0; vals[i] != NULL; i++ ) {
1196                         gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1197                         nvp = ldapqry_create_name_value( attr, tmp );
1198                         g_free(tmp);
1199                         list = g_list_append( list, nvp );
1200                 }
1201         }
1202         ldap_value_free_len( vals );
1203         return list;
1204 }
1205
1206 /**
1207  * Fetch a list of all attributes.
1208  * \param  ld    LDAP handle.
1209  * \param  e     LDAP message.
1210  * \return List of attribute name/value pairs.
1211  */
1212 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1213 {
1214         char *attribute;
1215         BerElement *ber;
1216         GList *listValues = NULL;
1217
1218         /* Process all attributes */
1219         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1220                 attribute = ldap_next_attribute( ld, e, ber ) ) {
1221                 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1222                 ldap_memfree( attribute );
1223         }
1224
1225         /* Free up */
1226         if( ber != NULL ) {
1227                 ber_free( ber, 0 );
1228         }
1229         return listValues;
1230 }
1231
1232 #define CRITERIA_SINGLE "(objectclass=*)"
1233
1234 /**
1235  * Perform the data retrieval for a specific LDAP record.
1236  * 
1237  * \param  qry Query object to process.
1238  * \return Error/status code.
1239  */
1240 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1241         LdapControl *ctl;
1242         LDAP *ld;
1243         LDAPMessage *result, *e = NULL;
1244         gboolean entriesFound;
1245         gboolean first;
1246         struct timeval timeout;
1247         gint rc;
1248         gchar *dn;
1249         GList *listValues;
1250
1251         /* Initialize some variables */
1252         ld = qry->ldap;
1253         ctl = qry->control;
1254         dn = ADDRQUERY_SEARCHVALUE(qry);
1255         timeout.tv_sec = ctl->timeOut;
1256         timeout.tv_usec = 0L;
1257         entriesFound = FALSE;
1258
1259         /*
1260          * Execute the search - this step may take some time to complete
1261          * depending on network traffic and server response time.
1262          */
1263         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1264         rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1265                 NULL, 0, NULL, NULL, &timeout, 0, &result );
1266         if( rc == LDAP_TIMEOUT ) {
1267                 return ADDRQUERY_RETVAL(qry);
1268         }
1269         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1270         if( rc != LDAP_SUCCESS ) {
1271                 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
1272                 return ADDRQUERY_RETVAL(qry);
1273         }
1274
1275         debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1276
1277         /* Process results */
1278         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1279         first = TRUE;
1280         while( TRUE ) {
1281                 ldapqry_touch( qry );
1282                 if( qry->entriesRead >= ctl->maxEntries ) break;                
1283
1284                 /* Test for stop */
1285                 if( ldapqry_get_stop_flag( qry ) ) {
1286                         break;
1287                 }
1288
1289                 /* Retrieve entry */            
1290                 if( first ) {
1291                         first = FALSE;
1292                         e = ldap_first_entry( ld, result );
1293                 }
1294                 else {
1295                         e = ldap_next_entry( ld, e );
1296                 }
1297                 if( e == NULL ) break;
1298
1299                 entriesFound = TRUE;
1300
1301                 /* Setup a critical section here */
1302                 pthread_mutex_lock( qry->mutexEntry );
1303
1304                 /* Process entry */
1305                 listValues = ldapqry_fetch_attribs( ld, e );
1306
1307                 /* Process callback */
1308                 if( qry->callBackEntry ) {
1309                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1310                 }
1311                 ldapqry_free_list_name_value( listValues );
1312                 listValues = NULL;
1313
1314                 pthread_mutex_unlock( qry->mutexEntry );
1315         }
1316
1317         /* Free up and disconnect */
1318         ldap_msgfree( result );
1319
1320         if( entriesFound ) {
1321                 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1322         }
1323         else {
1324                 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1325         }
1326
1327         return ADDRQUERY_RETVAL(qry);
1328 }
1329
1330 /**
1331  * Perform the search to locate a specific LDAP record identified by
1332  * distinguished name (dn).
1333  * 
1334  * \param  qry Query object to process.
1335  * \return Error/status code.
1336  */
1337 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1338         /* Connect */
1339         qry->ldap = NULL;
1340         ldapqry_connect( qry );
1341         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1342                 /* Perform search */
1343                 ldapqry_locate_retrieve( qry );
1344         }
1345         /* Disconnect */
1346         ldapqry_disconnect( qry );
1347         qry->ldap = NULL;
1348
1349         /* Process callback */  
1350         if( qry->callBackEnd ) {
1351                 g_timeout_add(0, callbackend, qry);
1352         }
1353
1354         return ADDRQUERY_RETVAL(qry);
1355 }
1356
1357 /**
1358  * Remove results (folder and data) for specified LDAP query.
1359  * \param  qry Query object to process.
1360  * \return TRUE if folder deleted successfully.
1361  */
1362 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1363         gboolean retVal = FALSE;
1364
1365         ldapqry_set_aged_flag( qry, TRUE );
1366
1367         if( ldapqry_get_busy_flag( qry ) ) {
1368                 ldapqry_set_stop_flag( qry, TRUE );
1369         }
1370         else {
1371                 LdapServer *server = qry->server;
1372                 server->listQuery = g_list_remove(server->listQuery, qry);
1373
1374                 retVal = TRUE;
1375         }
1376         return retVal;
1377 }
1378
1379 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1380         cm_return_if_fail( qry != NULL );
1381
1382         ldapsvr_print_data(qry->server, stream);
1383         ldapctl_print(qry->control, stream);
1384         fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1385         fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1386         fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1387         fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1388         fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1389         fprintf(stream, "completed: %d\n", qry->completed);
1390         fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1391         fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1392         fprintf(stream, "data: %s\n", qry->data?(gchar *)qry->data:"null");
1393 }
1394
1395 #endif  /* USE_LDAP */
1396
1397 /*
1398  * End of Source.
1399  */
1400
1401