2010-10-17 [colin] 3.7.6cvs56
[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         gchar *pwd;
695
696         /* Initialize connection */
697         if (debug_get_mode()) {
698                 debug_print("===ldapqry_connect===\n");
699                 /*ldapqry_print(qry, stdout);*/
700         }
701         ctl = qry->control;
702         /*if (debug_get_mode()) {
703                 ldapctl_print(ctl, stdout);
704                 debug_print("======\n");
705         }*/
706         ldapqry_touch( qry );
707         qry->startTime = qry->touchTime;
708         qry->elapsedTime = -1;
709         ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
710
711         ldapsrv_set_options (ctl->timeOut, NULL);
712
713         uri = g_strdup_printf("ldap%s://%s:%d",
714                                 ctl->enableSSL?"s":"",
715                                 ctl->hostName, ctl->port);
716         ldap_initialize(&ld, uri);
717         g_free(uri);
718
719         if (ld == NULL)
720                 return ADDRQUERY_RETVAL(qry);
721
722         qry->ldap = ld;
723         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
724         if( ldapqry_get_stop_flag( qry ) ) {
725                 return ADDRQUERY_RETVAL(qry);
726         }
727         ldapqry_touch( qry );
728
729         debug_print("connected to LDAP host %s on port %d\n",
730                         ctl->hostName?ctl->hostName:"null", ctl->port);
731
732 #ifdef USE_LDAP_TLS
733         /* Handle TLS */
734         version = LDAP_VERSION3;
735         rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
736         if( rc == LDAP_OPT_SUCCESS ) {
737                 ctl->version = LDAP_VERSION3;
738         }
739
740         if( ctl->version == LDAP_VERSION3 ) {
741                 if( ctl->enableTLS && !ctl->enableSSL ) {
742                         ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
743                         rc = ldap_start_tls_s( ld, NULL, NULL );
744                         
745                         debug_print("rc=%d\n", rc);
746                         debug_print("LDAP Status: set_option: %s\n", ldap_err2string(rc));
747
748                         if( rc != LDAP_SUCCESS ) {
749                                 return ADDRQUERY_RETVAL(qry);
750                         }
751                 }
752         }
753 #endif
754
755         /* Bind to the server, if required */
756         ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
757         if( ctl->bindDN ) {
758                 if( * ctl->bindDN != '\0' ) {
759                         debug_print("binding...\n");
760                         pwd = ldapctl_get_bind_password(ctl);
761                         rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, pwd);
762                         g_free(pwd);
763                         debug_print("rc=%d\n", rc);
764                         if( rc != LDAP_SUCCESS ) {
765                                 debug_print("LDAP Error: ldap_simple_bind_s: %s\n",     ldap_err2string(rc));
766                                 return ADDRQUERY_RETVAL(qry);
767                         }
768                 }
769         }
770         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
771         if( ldapqry_get_stop_flag( qry ) ) {
772                 return ADDRQUERY_RETVAL(qry);
773         }
774         ldapqry_touch( qry );
775
776         ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
777
778         return ADDRQUERY_RETVAL(qry);
779 }
780
781 /**
782  * Connect to LDAP server.
783  * \param  qry Query object to process.
784  * \return Error/status code.
785  */
786 static gint ldapqry_disconnect( LdapQuery *qry ) {
787         /* Disconnect */
788         if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
789         qry->ldap = NULL;
790
791         ldapqry_touch( qry );
792         qry->elapsedTime = qry->touchTime - qry->startTime;
793
794         return ADDRQUERY_RETVAL(qry);
795 }
796
797 /**
798  * Perform the LDAP search, reading LDAP entries into cache.
799  * Note that one LDAP entry can have multiple values for many of its
800  * attributes. If these attributes are E-Mail addresses; these are
801  * broken out into separate address items. For any other attribute,
802  * only the first occurrence is read.
803  * 
804  * \param  qry Query object to process.
805  * \return Error/status code.
806  */
807 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
808         LdapControl *ctl;
809         LDAP *ld;
810         LDAPMessage *result = NULL, *e = NULL;
811         char **attribs;
812         gchar *criteria;
813         gboolean searchFlag;
814         gboolean entriesFound;
815         gboolean first;
816         struct timeval timeout;
817         gint rc;
818         AddressCache *cache;
819         GList *listEMail;
820
821         /* Initialize some variables */
822         ld = qry->ldap;
823         ctl = qry->control;
824         cache = qry->server->addressCache;
825         timeout.tv_sec = ctl->timeOut;
826         timeout.tv_usec = 0L;
827         entriesFound = FALSE;
828         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
829
830         /* Define all attributes we are interested in. */
831         attribs = ldapctl_full_attribute_array( ctl );
832
833         /* Create LDAP search string */
834         criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
835         debug_print("Search criteria ::%s::\n", criteria?criteria:"null");
836
837         /*
838          * Execute the search - this step may take some time to complete
839          * depending on network traffic and server response time.
840          */
841         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
842         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
843                 attribs, 0, NULL, NULL, &timeout, 0, &result );
844         debug_print("LDAP Error: ldap_search_st: %d\n", rc);
845         debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
846         ldapctl_free_attribute_array( attribs );
847         g_free( criteria );
848         criteria = NULL;
849         if( rc == LDAP_TIMEOUT ) {
850                 return ADDRQUERY_RETVAL(qry);
851         }
852         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
853
854         /* Test valid returns */
855         searchFlag = FALSE;
856         if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
857                 searchFlag = TRUE;
858         }
859         else if( rc == LDAP_SUCCESS ) {
860                 searchFlag = TRUE;
861         }
862         else if( rc == LDAP_PARTIAL_RESULTS || (result && ldap_count_entries(ld, result) > 0) ) {
863                 searchFlag = TRUE;
864         }
865         else {
866                 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
867                 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
868                 return ADDRQUERY_RETVAL(qry);
869         }
870         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
871
872         debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
873
874         /* Process results */
875         first = TRUE;
876         while( searchFlag ) {
877                 ldapqry_touch( qry );
878                 if( qry->entriesRead >= ctl->maxEntries ) break;                
879
880                 /* Test for stop */             
881                 if( ldapqry_get_stop_flag( qry ) ) {
882                         break;
883                 }
884
885                 /* Retrieve entry */            
886                 if( first ) {
887                         first = FALSE;
888                         e = ldap_first_entry( ld, result );
889                 }
890                 else {
891                         e = ldap_next_entry( ld, e );
892                 }
893                 if( e == NULL ) break;
894                 entriesFound = TRUE;
895
896                 /* Setup a critical section here */
897                 pthread_mutex_lock( qry->mutexEntry );
898
899                 /* Process entry */
900                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
901
902                 /* Process callback */
903                 if( qry->callBackEntry ) {
904                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
905                 }
906                 else {
907                         /*if (debug_get_mode()) {
908                                 GList *node = listEMail;
909                                 while (node) {
910                                         addritem_print_item_email(node->data, stdout);
911                                         node = g_list_next(node);
912                                 }
913                         }*/
914                         g_list_free( listEMail );
915                 }
916                 pthread_mutex_unlock( qry->mutexEntry );
917         }
918
919         /* Free up and disconnect */
920         ldap_msgfree( result );
921
922         if( searchFlag ) {
923                 if( entriesFound ) {
924                         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
925                 }
926                 else {
927                         ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
928                 }
929         }
930
931         return ADDRQUERY_RETVAL(qry);
932 }
933
934 /**
935  * Connection, perform search and disconnect.
936  * \param  qry Query object to process.
937  * \return Error/status code.
938  */
939 static gint ldapqry_perform_search( LdapQuery *qry ) {
940         /* Check search criteria */     
941         if( ! ldapqry_check_search( qry ) ) {
942                 return ADDRQUERY_RETVAL(qry);
943         }
944
945         /* Connect */
946         qry->ldap = NULL;
947         ldapqry_connect( qry );
948         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
949                 /* Perform search */
950                 ldapqry_search_retrieve( qry );
951         }
952         /* Disconnect */
953         ldapqry_disconnect( qry );
954         qry->ldap = NULL;
955
956         return ADDRQUERY_RETVAL(qry);
957 }
958
959 static gint ldapqry_perform_locate( LdapQuery *qry );
960
961 /**
962  * Wrapper around search.
963  * \param  qry Query object to process.
964  * \return Error/status code.
965  */
966 static gint ldapqry_search( LdapQuery *qry ) {
967         gint retVal;
968
969         cm_return_val_if_fail( qry != NULL, -1 );
970         cm_return_val_if_fail( qry->control != NULL, -1 );
971
972         ldapqry_touch( qry );
973         qry->completed = FALSE;
974
975         /* Setup pointer to thread specific area */
976         pthread_setspecific( _queryThreadKey_, qry );
977
978         pthread_detach( pthread_self() );
979         
980         /* Now perform the search */
981         qry->entriesRead = 0;
982         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
983         ldapqry_set_busy_flag( qry, TRUE );
984         ldapqry_set_stop_flag( qry, FALSE );
985         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
986                 retVal = ldapqry_perform_locate( qry );
987         }
988         else {
989                 retVal = ldapqry_perform_search( qry );
990         }
991         if( retVal == LDAPRC_SUCCESS ) {
992                 qry->server->addressCache->dataRead = TRUE;
993                 qry->server->addressCache->accessFlag = FALSE;
994                 if( ldapqry_get_stop_flag( qry ) ) {
995                         debug_print("Search was terminated prematurely\n");
996                 }
997                 else {
998                         ldapqry_touch( qry );
999                         qry->completed = TRUE;
1000                         debug_print("Search ran to completion\n");
1001                 }
1002         }
1003         ldapqry_set_stop_flag( qry, TRUE );
1004         ldapqry_set_busy_flag( qry, FALSE );
1005
1006         /* Process callback */  
1007         if( qry->callBackEnd ) {
1008                 g_timeout_add(0, callbackend, qry);
1009         }
1010
1011         return ADDRQUERY_RETVAL(qry);
1012 }
1013
1014 /**
1015  * Read data into list using a background thread. Callback function will be
1016  * notified when search is complete.
1017  * \param  qry Query object to process.
1018  * \return Error/status code.
1019  */
1020 gint ldapqry_read_data_th( LdapQuery *qry ) {
1021         cm_return_val_if_fail( qry != NULL, -1 );
1022         cm_return_val_if_fail( qry->control != NULL, -1 );
1023
1024         ldapqry_set_stop_flag( qry, FALSE );
1025         ldapqry_touch( qry );
1026         if( ldapqry_check_search( qry ) ) {
1027                 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1028                         debug_print("Starting LDAP search thread\n");
1029                         ldapqry_set_busy_flag( qry, TRUE );
1030                         qry->thread = g_malloc0( sizeof( pthread_t ) );
1031
1032                         /* Setup thread */                      
1033                         pthread_create( qry->thread, NULL,
1034                                 (void *) ldapqry_search, (void *) qry );
1035                 }
1036         }
1037         return ADDRQUERY_RETVAL(qry);
1038 }
1039
1040 /**
1041  * Cleanup LDAP thread data. This function will be called when each thread
1042  * exits. Note that the thread object will be freed by the kernel.
1043  * \param ptr Pointer to object being destroyed (a query object in this case).
1044  */
1045 static void ldapqry_destroyer( void * ptr ) {
1046         LdapQuery *qry;
1047
1048         qry = ( LdapQuery * ) ptr;
1049         cm_return_if_fail( qry != NULL );
1050
1051         debug_print("ldapqry_destroyer::%d::%s\n", (int) pthread_self(),
1052                         ADDRQUERY_NAME(qry)?ADDRQUERY_NAME(qry):"null");
1053
1054         /* Perform any destruction here */
1055         if( qry->control != NULL ) {
1056                 ldapctl_free( qry->control );
1057         }
1058         qry->control = NULL;
1059         qry->thread = NULL;
1060         ldapqry_set_busy_flag( qry, FALSE );
1061 }
1062
1063 /**
1064  * Cancel thread associated with query.
1065  * \param qry Query object to process.
1066  */
1067 void ldapqry_cancel( LdapQuery *qry ) {
1068         cm_return_if_fail( qry != NULL );
1069
1070         debug_print("cancelling::%d::%s\n", (int) pthread_self(),
1071                         ADDRQUERY_NAME(qry)?ADDRQUERY_NAME(qry):"null");
1072         if( ldapqry_get_busy_flag( qry ) ) {
1073                 if( qry->thread ) {
1074                         debug_print("calling pthread_cancel\n");
1075                         pthread_cancel( * qry->thread );
1076                 }
1077         }
1078 }
1079
1080 /**
1081  * Initialize LDAP query. This function should be called once before executing
1082  * any LDAP queries to initialize thread specific data.
1083  */
1084 void ldapqry_initialize( void ) {
1085         debug_print("ldapqry_initialize...\n");
1086         if( ! _queryThreadInit_ ) {
1087                 debug_print("ldapqry_initialize::creating thread specific area\n");
1088                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1089                 _queryThreadInit_ = TRUE;
1090         }
1091         debug_print("ldapqry_initialize... done!\n");
1092 }
1093
1094 /**
1095  * Age the query based on LDAP control parameters.
1096  * \param qry    Query object to process.
1097  * \param maxAge Maximum age of query (in seconds).
1098  */
1099 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1100         gint age;
1101
1102         cm_return_if_fail( qry != NULL );
1103
1104         /* Limit the time that queries can hang around */       
1105         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1106
1107         /* Check age of query */
1108         age = time( NULL ) - qry->touchTime;
1109         if( age > maxAge ) {
1110                 qry->agedFlag = TRUE;
1111         }
1112 }
1113
1114 /**
1115  * Delete folder associated with query results.
1116  * \param qry Query object to process.
1117  */
1118 void ldapqry_delete_folder( LdapQuery *qry ) {
1119         AddressCache *cache;
1120         ItemFolder *folder;
1121
1122         cm_return_if_fail( qry != NULL );
1123
1124         folder = ADDRQUERY_FOLDER(qry);
1125         if( folder ) {
1126                 cache = qry->server->addressCache;
1127                 folder = addrcache_remove_folder_delete( cache, folder );
1128                 if( folder ) {
1129                         addritem_free_item_folder( folder );
1130                 }
1131                 ADDRQUERY_FOLDER(qry) = NULL;
1132         }
1133 }
1134
1135 /**
1136  * Create a name/value pair object.
1137  * \param n Name.
1138  * \param v Value.
1139  * \return Initialized object.
1140  */
1141 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1142         NameValuePair *nvp = g_new0( NameValuePair, 1 );
1143
1144         nvp->name = g_strdup( n );
1145         nvp->value = g_strdup( v );
1146         return nvp;
1147 }
1148
1149 /**
1150  * Free up name/value pair object.
1151  * \param nvp Name/value object.
1152  */
1153 void ldapqry_free_name_value( NameValuePair *nvp ) {
1154         if( nvp ) {
1155                 g_free( nvp->name );
1156                 g_free( nvp->value );
1157                 nvp->name = nvp->value = NULL;
1158                 g_free( nvp );
1159         }
1160 }
1161
1162 /**
1163  * Free up a list name/value pair objects.
1164  * \param list List of name/value objects.
1165  */
1166 void ldapqry_free_list_name_value( GList *list ) {
1167         GList *node;
1168
1169         node = list;
1170         while( node ) {
1171                 NameValuePair *nvp = ( NameValuePair * ) node->data;
1172                 ldapqry_free_name_value( nvp );
1173                 node->data = NULL;
1174                 node = g_list_next( node );
1175         }
1176         g_list_free( list );
1177 }
1178
1179 /**
1180  * Load a list of name/value pairs from LDAP attributes.
1181  * \param  ld          LDAP handle.
1182  * \param  e          LDAP message.
1183  * \param  attr       Attribute name.
1184  * \param  listValues List to populate.
1185  * \return List of attribute name/value pairs.
1186  */
1187 static GList *ldapqry_load_attrib_values(
1188                 LDAP *ld, LDAPMessage *entry, char *attr,
1189                 GList *listValues )
1190 {
1191         GList *list = NULL;
1192         gint i;
1193         struct berval **vals;
1194         NameValuePair *nvp;
1195
1196         list = listValues;
1197         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1198                 for( i = 0; vals[i] != NULL; i++ ) {
1199                         gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1200                         nvp = ldapqry_create_name_value( attr, tmp );
1201                         g_free(tmp);
1202                         list = g_list_append( list, nvp );
1203                 }
1204         }
1205         ldap_value_free_len( vals );
1206         return list;
1207 }
1208
1209 /**
1210  * Fetch a list of all attributes.
1211  * \param  ld    LDAP handle.
1212  * \param  e     LDAP message.
1213  * \return List of attribute name/value pairs.
1214  */
1215 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1216 {
1217         char *attribute;
1218         BerElement *ber;
1219         GList *listValues = NULL;
1220
1221         /* Process all attributes */
1222         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1223                 attribute = ldap_next_attribute( ld, e, ber ) ) {
1224                 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1225                 ldap_memfree( attribute );
1226         }
1227
1228         /* Free up */
1229         if( ber != NULL ) {
1230                 ber_free( ber, 0 );
1231         }
1232         return listValues;
1233 }
1234
1235 #define CRITERIA_SINGLE "(objectclass=*)"
1236
1237 /**
1238  * Perform the data retrieval for a specific LDAP record.
1239  * 
1240  * \param  qry Query object to process.
1241  * \return Error/status code.
1242  */
1243 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1244         LdapControl *ctl;
1245         LDAP *ld;
1246         LDAPMessage *result, *e = NULL;
1247         gboolean entriesFound;
1248         gboolean first;
1249         struct timeval timeout;
1250         gint rc;
1251         gchar *dn;
1252         GList *listValues;
1253
1254         /* Initialize some variables */
1255         ld = qry->ldap;
1256         ctl = qry->control;
1257         dn = ADDRQUERY_SEARCHVALUE(qry);
1258         timeout.tv_sec = ctl->timeOut;
1259         timeout.tv_usec = 0L;
1260         entriesFound = FALSE;
1261
1262         /*
1263          * Execute the search - this step may take some time to complete
1264          * depending on network traffic and server response time.
1265          */
1266         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1267         rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1268                 NULL, 0, NULL, NULL, &timeout, 0, &result );
1269         if( rc == LDAP_TIMEOUT ) {
1270                 return ADDRQUERY_RETVAL(qry);
1271         }
1272         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1273         if( rc != LDAP_SUCCESS ) {
1274                 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
1275                 return ADDRQUERY_RETVAL(qry);
1276         }
1277
1278         debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1279
1280         /* Process results */
1281         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1282         first = TRUE;
1283         while( TRUE ) {
1284                 ldapqry_touch( qry );
1285                 if( qry->entriesRead >= ctl->maxEntries ) break;                
1286
1287                 /* Test for stop */
1288                 if( ldapqry_get_stop_flag( qry ) ) {
1289                         break;
1290                 }
1291
1292                 /* Retrieve entry */            
1293                 if( first ) {
1294                         first = FALSE;
1295                         e = ldap_first_entry( ld, result );
1296                 }
1297                 else {
1298                         e = ldap_next_entry( ld, e );
1299                 }
1300                 if( e == NULL ) break;
1301
1302                 entriesFound = TRUE;
1303
1304                 /* Setup a critical section here */
1305                 pthread_mutex_lock( qry->mutexEntry );
1306
1307                 /* Process entry */
1308                 listValues = ldapqry_fetch_attribs( ld, e );
1309
1310                 /* Process callback */
1311                 if( qry->callBackEntry ) {
1312                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1313                 }
1314                 ldapqry_free_list_name_value( listValues );
1315                 listValues = NULL;
1316
1317                 pthread_mutex_unlock( qry->mutexEntry );
1318         }
1319
1320         /* Free up and disconnect */
1321         ldap_msgfree( result );
1322
1323         if( entriesFound ) {
1324                 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1325         }
1326         else {
1327                 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1328         }
1329
1330         return ADDRQUERY_RETVAL(qry);
1331 }
1332
1333 /**
1334  * Perform the search to locate a specific LDAP record identified by
1335  * distinguished name (dn).
1336  * 
1337  * \param  qry Query object to process.
1338  * \return Error/status code.
1339  */
1340 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1341         /* Connect */
1342         qry->ldap = NULL;
1343         ldapqry_connect( qry );
1344         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1345                 /* Perform search */
1346                 ldapqry_locate_retrieve( qry );
1347         }
1348         /* Disconnect */
1349         ldapqry_disconnect( qry );
1350         qry->ldap = NULL;
1351
1352         /* Process callback */  
1353         if( qry->callBackEnd ) {
1354                 g_timeout_add(0, callbackend, qry);
1355         }
1356
1357         return ADDRQUERY_RETVAL(qry);
1358 }
1359
1360 /**
1361  * Remove results (folder and data) for specified LDAP query.
1362  * \param  qry Query object to process.
1363  * \return TRUE if folder deleted successfully.
1364  */
1365 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1366         gboolean retVal = FALSE;
1367
1368         ldapqry_set_aged_flag( qry, TRUE );
1369
1370         if( ldapqry_get_busy_flag( qry ) ) {
1371                 ldapqry_set_stop_flag( qry, TRUE );
1372         }
1373         else {
1374                 LdapServer *server = qry->server;
1375                 server->listQuery = g_list_remove(server->listQuery, qry);
1376
1377                 retVal = TRUE;
1378         }
1379         return retVal;
1380 }
1381
1382 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1383         cm_return_if_fail( qry != NULL );
1384
1385         ldapsvr_print_data(qry->server, stream);
1386         ldapctl_print(qry->control, stream);
1387         fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1388         fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1389         fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1390         fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1391         fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1392         fprintf(stream, "completed: %d\n", qry->completed);
1393         fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1394         fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1395         fprintf(stream, "data: %s\n", qry->data?(gchar *)qry->data:"null");
1396 }
1397
1398 #endif  /* USE_LDAP */
1399
1400 /*
1401  * End of Source.
1402  */
1403
1404