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