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