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