Forgot to date rc1
[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 )
357 {
358         mgu_free_list( listName );
359         mgu_free_list( listAddr );
360         mgu_free_list( listFirst );
361         mgu_free_list( listLast );
362 }
363
364 /**
365  * Add all LDAP attribute values to a list.
366  * \param ld LDAP handle.
367  * \param entry LDAP entry to process.
368  * \param attr  LDAP attribute.
369  * \return List of values.
370  */
371 static GSList *ldapqry_add_list_values(
372                 LDAP *ld, LDAPMessage *entry, char *attr )
373 {
374         GSList *list = NULL;
375         gint i;
376         struct berval **vals;
377
378         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
379                 for( i = 0; vals[i] != NULL; i++ ) {
380                         /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
381                         list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
382                 }
383         }
384         ldap_value_free_len( vals );
385         return list;
386 }
387
388 /**
389  * Add a single attribute value to a list.
390  * \param  ld    LDAP handle.
391  * \param  entry LDAP entry to process.
392  * \param  attr  LDAP attribute name to process.
393  * \return List of values; only one value will be present.
394  */
395 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
396         GSList *list = NULL;
397         struct berval **vals;
398
399         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
400                 if( vals[0] != NULL ) {
401                         /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
402                         list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
403                 }
404         }
405         ldap_value_free_len( vals );
406         return list;
407 }
408
409 /**
410  * Build an address list entry and append to list of address items. Name is formatted
411  * as "<first-name> <last-name>".
412  *
413  * \param  cache     Address cache to load.
414  * \param  qry Query object to process.
415  * \param  dn        DN for entry found on server.
416  * \param  listName  List of common names for entry; see notes below.
417  * \param  listAddr  List of EMail addresses for entry.
418  * \param  listFirst List of first names for entry.
419  * \param  listLast  List of last names for entry.
420  *
421  * \return List of ItemEMail objects.
422  *
423  * Notes:
424  * 1) Each LDAP server entry may have multiple LDAP attributes with the same
425  *    name. For example, a single entry for a person may have more than one
426  *    common name, email address, etc.
427 *
428  * 2) The DN for the entry is unique for the server.
429  */
430 static GList *ldapqry_build_items_fl(
431                 AddressCache *cache, LdapQuery *qry, gchar *dn,
432                 GSList *listName, GSList *listAddr, GSList *listFirst,
433                 GSList *listLast )
434 {
435         GSList *nodeAddress;
436         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
437         gboolean allocated;
438         ItemPerson *person;
439         ItemEMail *email;
440         ItemFolder *folder;
441         GList *listReturn = NULL;
442
443         folder = ADDRQUERY_FOLDER(qry);
444         if( folder == NULL ) return listReturn;
445         if( listAddr == NULL ) return listReturn;
446
447         /* Find longest first name in list */
448         firstName = mgu_slist_longest_entry( listFirst );
449
450         /* Format last name */
451         if( listLast ) {
452                 lastName = listLast->data;
453         }
454
455         /* Find longest common name */
456         allocated = FALSE;
457         fullName = mgu_slist_longest_entry( listName );
458         if( fullName == NULL ) {
459                 /* Format a full name from first and last names */
460                 if( firstName ) {
461                         if( lastName ) {
462                                 fullName = g_strdup_printf( "%s %s", firstName, lastName );
463                         }
464                         else {
465                                 fullName = g_strdup_printf( "%s", firstName );
466                         }
467                 }
468                 else {
469                         if( lastName ) {
470                                 fullName = g_strdup_printf( "%s", lastName );
471                         }
472                 }
473                 if( fullName ) {
474                         g_strchug( fullName ); g_strchomp( fullName );
475                         allocated = TRUE;
476                 }
477         }
478
479         /* Add person into folder */            
480         person = addritem_create_item_person();
481         addritem_person_set_common_name( person, fullName );
482         addritem_person_set_first_name( person, firstName );
483         addritem_person_set_last_name( person, lastName );
484         addrcache_id_person( cache, person );
485         addritem_person_set_external_id( person, dn );
486         addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
487
488         qry->entriesRead++;
489
490         /* Add each address item */
491         nodeAddress = listAddr;
492         while( nodeAddress ) {
493                 email = addritem_create_item_email();
494                 addritem_email_set_address( email, nodeAddress->data );
495                 addrcache_id_email( cache, email );
496                 addrcache_person_add_email( cache, person, email );
497                 addritem_person_add_email( person, email );
498                 listReturn = g_list_append( listReturn, email );
499                 nodeAddress = g_slist_next( nodeAddress );
500         }
501
502         /* Free any allocated memory */
503         if( allocated ) {
504                 g_free( fullName );
505         }
506         fullName = firstName = lastName = NULL;
507
508         return listReturn;
509 }
510
511 /**
512  * Process a single search entry.
513  * \param  cache Address cache to load.
514  * \param  qry   Query object to process.
515  * \param  ld    LDAP handle.
516  * \param  e     LDAP message.
517  * \return List of EMail objects found.
518  */
519 static GList *ldapqry_process_single_entry(
520                 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
521 {
522         char *dnEntry;
523         char *attribute;
524         LdapControl *ctl;
525         BerElement *ber;
526         GSList *listName = NULL, *listAddress = NULL;
527         GSList *listFirst = NULL, *listLast = NULL;
528         GList *listReturn;
529
530         listReturn = NULL;
531         ctl = qry->control;
532         dnEntry = ldap_get_dn( ld, e );
533         /* printf( "DN: %s\n", dnEntry ); */
534
535         /* Process all attributes */
536         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
537                 attribute = ldap_next_attribute( ld, e, ber ) ) {
538
539                 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
540                         listAddress = ldapqry_add_list_values( ld, e, attribute );
541                 }
542                 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
543                         listName = ldapqry_add_list_values( ld, e, attribute );
544                 }
545                 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
546                         listFirst = ldapqry_add_list_values( ld, e, attribute );
547                 }
548                 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
549                         listLast = ldapqry_add_single_value( ld, e, attribute );
550                 }
551
552                 /* Free memory used to store attribute */
553                 ldap_memfree( attribute );
554         }
555
556         /* Format and add items to cache */
557         listReturn = ldapqry_build_items_fl(
558                 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
559
560         /* Free up */
561         ldapqry_free_lists( listName, listAddress, listFirst, listLast );
562         listName = listAddress = listFirst = listLast = NULL;
563
564         if( ber != NULL ) {
565                 ber_free( ber, 0 );
566         }
567         g_free( dnEntry );
568
569         return listReturn;
570 }
571
572 /**
573  * Check parameters that are required for a search. This should
574  * be called before performing a search.
575  * \param  qry Query object to process.
576  * \return <i>TRUE</i> if search criteria appear OK.
577  */
578 gboolean ldapqry_check_search( LdapQuery *qry ) {
579         LdapControl *ctl;
580         ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
581
582         /* Test for control data */
583         ctl = qry->control;
584         if( ctl == NULL ) {
585                 return FALSE;
586         }
587
588         /* Test for search value */
589         if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
590                 return FALSE;
591         }
592         if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
593                 return FALSE;
594         }
595         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
596         return TRUE;
597 }
598
599 /**
600  * Touch the query. This nudges the touch time with the current time.
601  * \param qry Query object to process.
602  */
603 void ldapqry_touch( LdapQuery *qry ) {
604         qry->touchTime = time( NULL );
605         qry->agedFlag = FALSE;
606 }
607
608 /**
609  * Connect to LDAP server.
610  * \param  qry Query object to process.
611  * \return Error/status code.
612  */
613 static gint ldapqry_connect( LdapQuery *qry ) {
614         LdapControl *ctl;
615         LDAP *ld = NULL;
616         gint rc;
617         gint version;
618         gchar *uri = NULL;
619
620         /* Initialize connection */
621         /* printf( "===ldapqry_connect===\n" ); */
622         /* ldapqry_print( qry, stdout ); */
623         ctl = qry->control;
624         /* ldapctl_print( ctl, stdout ); */
625         /* printf( "======\n" ); */
626         ldapqry_touch( qry );
627         qry->startTime = qry->touchTime;
628         qry->elapsedTime = -1;
629         ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
630
631         uri = g_strdup_printf("ldap%s://%s:%d",
632                                 ctl->enableSSL?"s":"",
633                                 ctl->hostName, ctl->port);
634         ldap_initialize(&ld, uri);
635         g_free(uri);
636
637         if (ld == NULL)
638                 return ADDRQUERY_RETVAL(qry);
639
640         qry->ldap = ld;
641         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
642         if( ldapqry_get_stop_flag( qry ) ) {
643                 return ADDRQUERY_RETVAL(qry);
644         }
645         ldapqry_touch( qry );
646
647         /*
648         printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
649         */
650
651 #ifdef USE_LDAP_TLS
652         /* Handle TLS */
653         version = LDAP_VERSION3;
654         rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
655         if( rc == LDAP_OPT_SUCCESS ) {
656                 ctl->version = LDAP_VERSION3;
657         }
658
659         if( ctl->version == LDAP_VERSION3 ) {
660                 if( ctl->enableTLS && !ctl->enableSSL ) {
661                         ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
662                         rc = ldap_start_tls_s( ld, NULL, NULL );
663                         
664                         /* printf( "rc=%d\n", rc );
665                         printf( "LDAP Status: set_option: %s\n", ldap_err2string( rc ) ); */
666                         
667                         if( rc != LDAP_SUCCESS ) {
668                                 return ADDRQUERY_RETVAL(qry);
669                         }
670                 }
671         }
672 #endif
673
674         /* Bind to the server, if required */
675         ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
676         if( ctl->bindDN ) {
677                 if( * ctl->bindDN != '\0' ) {
678                         /* printf( "binding...\n" ); */
679                         rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
680                         /* printf( "rc=%d\n", rc ); */
681                         if( rc != LDAP_SUCCESS ) {
682                                 /*
683                                 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
684                                         ldap_err2string( rc ) );
685                                 */
686                                 return ADDRQUERY_RETVAL(qry);
687                         }
688                 }
689         }
690         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
691         if( ldapqry_get_stop_flag( qry ) ) {
692                 return ADDRQUERY_RETVAL(qry);
693         }
694         ldapqry_touch( qry );
695
696         ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
697
698         return ADDRQUERY_RETVAL(qry);
699 }
700
701 /**
702  * Connect to LDAP server.
703  * \param  qry Query object to process.
704  * \return Error/status code.
705  */
706 static gint ldapqry_disconnect( LdapQuery *qry ) {
707         /* Disconnect */
708         if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
709         qry->ldap = NULL;
710
711         ldapqry_touch( qry );
712         qry->elapsedTime = qry->touchTime - qry->startTime;
713
714         return ADDRQUERY_RETVAL(qry);
715 }
716
717 /**
718  * Perform the LDAP search, reading LDAP entries into cache.
719  * Note that one LDAP entry can have multiple values for many of its
720  * attributes. If these attributes are E-Mail addresses; these are
721  * broken out into separate address items. For any other attribute,
722  * only the first occurrence is read.
723  * 
724  * \param  qry Query object to process.
725  * \return Error/status code.
726  */
727 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
728         LdapControl *ctl;
729         LDAP *ld;
730         LDAPMessage *result, *e = NULL;
731         char **attribs;
732         gchar *criteria;
733         gboolean searchFlag;
734         gboolean entriesFound;
735         gboolean first;
736         struct timeval timeout;
737         gint rc;
738         AddressCache *cache;
739         GList *listEMail;
740
741         /* Initialize some variables */
742         ld = qry->ldap;
743         ctl = qry->control;
744         cache = qry->server->addressCache;
745         timeout.tv_sec = ctl->timeOut;
746         timeout.tv_usec = 0L;
747         entriesFound = FALSE;
748         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
749
750         /* Define all attributes we are interested in. */
751         attribs = ldapctl_attribute_array( ctl );
752
753         /* Create LDAP search string */
754         criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
755         /* printf( "Search criteria ::%s::\n", criteria ); */
756
757         /*
758          * Execute the search - this step may take some time to complete
759          * depending on network traffic and server response time.
760          */
761         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
762         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
763                 attribs, 0, NULL, NULL, &timeout, 0, &result );
764         ldapctl_free_attribute_array( attribs );
765         g_free( criteria );
766         criteria = NULL;
767         if( rc == LDAP_TIMEOUT ) {
768                 return ADDRQUERY_RETVAL(qry);
769         }
770         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
771
772         /* Test valid returns */
773         searchFlag = FALSE;
774         if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
775                 searchFlag = TRUE;
776         }
777         else if( rc == LDAP_SUCCESS ) {
778                 searchFlag = TRUE;
779         }
780         else if( rc == LDAP_PARTIAL_RESULTS ) {
781                 searchFlag = TRUE;
782         }
783         else {
784                 /*
785                 printf( "LDAP Error: ldap_search_st: %d\n", rc );
786                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
787                 */
788                 return ADDRQUERY_RETVAL(qry);
789         }
790         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
791
792         /*
793         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
794         */
795
796         /* Process results */
797         first = TRUE;
798         while( searchFlag ) {
799                 ldapqry_touch( qry );
800                 if( qry->entriesRead >= ctl->maxEntries ) break;                
801
802                 /* Test for stop */             
803                 if( ldapqry_get_stop_flag( qry ) ) {
804                         break;
805                 }
806
807                 /* Retrieve entry */            
808                 if( first ) {
809                         first = FALSE;
810                         e = ldap_first_entry( ld, result );
811                 }
812                 else {
813                         e = ldap_next_entry( ld, e );
814                 }
815                 if( e == NULL ) break;
816                 entriesFound = TRUE;
817
818                 /* Setup a critical section here */
819                 pthread_mutex_lock( qry->mutexEntry );
820
821                 /* Process entry */
822                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
823
824                 /* Process callback */
825                 if( qry->callBackEntry ) {
826                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
827                 }
828                 else {
829                         g_list_free( listEMail );
830                 }
831                 pthread_mutex_unlock( qry->mutexEntry );
832         }
833
834         /* Free up and disconnect */
835         ldap_msgfree( result );
836
837         if( searchFlag ) {
838                 if( entriesFound ) {
839                         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
840                 }
841                 else {
842                         ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
843                 }
844         }
845
846         return ADDRQUERY_RETVAL(qry);
847 }
848
849 /**
850  * Connection, perform search and disconnect.
851  * \param  qry Query object to process.
852  * \return Error/status code.
853  */
854 static gint ldapqry_perform_search( LdapQuery *qry ) {
855         /* Check search criteria */     
856         if( ! ldapqry_check_search( qry ) ) {
857                 return ADDRQUERY_RETVAL(qry);
858         }
859
860         /* Connect */
861         qry->ldap = NULL;
862         ldapqry_connect( qry );
863         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
864                 /* Perform search */
865                 ldapqry_search_retrieve( qry );
866         }
867         /* Disconnect */
868         ldapqry_disconnect( qry );
869         qry->ldap = NULL;
870
871         return ADDRQUERY_RETVAL(qry);
872 }
873
874 static gint ldapqry_perform_locate( LdapQuery *qry );
875
876 /**
877  * Wrapper around search.
878  * \param  qry Query object to process.
879  * \return Error/status code.
880  */
881 static gint ldapqry_search( LdapQuery *qry ) {
882         gint retVal;
883
884         g_return_val_if_fail( qry != NULL, -1 );
885         g_return_val_if_fail( qry->control != NULL, -1 );
886
887         ldapqry_touch( qry );
888         qry->completed = FALSE;
889
890         /* Setup pointer to thread specific area */
891         pthread_setspecific( _queryThreadKey_, qry );
892
893         pthread_detach( pthread_self() );
894         
895         /* Now perform the search */
896         qry->entriesRead = 0;
897         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
898         ldapqry_set_busy_flag( qry, TRUE );
899         ldapqry_set_stop_flag( qry, FALSE );
900         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
901                 retVal = ldapqry_perform_locate( qry );
902         }
903         else {
904                 retVal = ldapqry_perform_search( qry );
905         }
906         if( retVal == LDAPRC_SUCCESS ) {
907                 qry->server->addressCache->dataRead = TRUE;
908                 qry->server->addressCache->accessFlag = FALSE;
909                 if( ldapqry_get_stop_flag( qry ) ) {
910                         /*
911                         printf( "Search was terminated prematurely\n" );
912                         */
913                 }
914                 else {
915                         ldapqry_touch( qry );
916                         qry->completed = TRUE;
917                         /*
918                         printf( "Search ran to completion\n" );
919                         */
920                 }
921         }
922         ldapqry_set_stop_flag( qry, TRUE );
923         ldapqry_set_busy_flag( qry, FALSE );
924
925         /* Process callback */  
926         if( qry->callBackEnd ) {
927                 g_timeout_add(0, callbackend, qry);
928         }
929
930         return ADDRQUERY_RETVAL(qry);
931 }
932
933 /**
934  * Read data into list using a background thread. Callback function will be
935  * notified when search is complete.
936  * \param  qry Query object to process.
937  * \return Error/status code.
938  */
939 gint ldapqry_read_data_th( LdapQuery *qry ) {
940         g_return_val_if_fail( qry != NULL, -1 );
941         g_return_val_if_fail( qry->control != NULL, -1 );
942
943         ldapqry_set_stop_flag( qry, FALSE );
944         ldapqry_touch( qry );
945         if( ldapqry_check_search( qry ) ) {
946                 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
947                         /*
948                         printf( "Starting LDAP search thread\n");
949                         */
950                         ldapqry_set_busy_flag( qry, TRUE );
951                         qry->thread = g_malloc0( sizeof( pthread_t ) );
952
953                         /* Setup thread */                      
954                         pthread_create( qry->thread, NULL,
955                                 (void *) ldapqry_search, (void *) qry );
956                 }
957         }
958         return ADDRQUERY_RETVAL(qry);
959 }
960
961 /**
962  * Cleanup LDAP thread data. This function will be called when each thread
963  * exits. Note that the thread object will be freed by the kernel.
964  * \param ptr Pointer to object being destroyed (a query object in this case).
965  */
966 static void ldapqry_destroyer( void * ptr ) {
967         LdapQuery *qry;
968
969         qry = ( LdapQuery * ) ptr;
970         /*
971         printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
972         */
973
974         /* Perform any destruction here */
975         if( qry->control != NULL ) {
976                 ldapctl_free( qry->control );
977         }
978         qry->control = NULL;
979         qry->thread = NULL;
980         ldapqry_set_busy_flag( qry, FALSE );
981 }
982
983 /**
984  * Cancel thread associated with query.
985  * \param qry Query object to process.
986  */
987 void ldapqry_cancel( LdapQuery *qry ) {
988         g_return_if_fail( qry != NULL );
989
990         /*
991         printf( "cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
992         */
993         if( ldapqry_get_busy_flag( qry ) ) {
994                 if( qry->thread ) {
995                         /* printf( "calling pthread_cancel\n" ); */
996                         pthread_cancel( * qry->thread );
997                 }
998         }
999 }
1000
1001 /**
1002  * Initialize LDAP query. This function should be called once before executing
1003  * any LDAP queries to initialize thread specific data.
1004  */
1005 void ldapqry_initialize( void ) {
1006         /* printf( "ldapqry_initialize...\n" ); */
1007         if( ! _queryThreadInit_ ) {
1008                 /*
1009                 printf( "ldapqry_initialize::creating thread specific area\n" );
1010                 */
1011                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1012                 _queryThreadInit_ = TRUE;
1013         }
1014         /* printf( "ldapqry_initialize... done!\n" ); */
1015 }
1016
1017 /**
1018  * Age the query based on LDAP control parameters.
1019  * \param qry    Query object to process.
1020  * \param maxAge Maximum age of query (in seconds).
1021  */
1022 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1023         gint age;
1024
1025         g_return_if_fail( qry != NULL );
1026
1027         /* Limit the time that queries can hang around */       
1028         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1029
1030         /* Check age of query */
1031         age = time( NULL ) - qry->touchTime;
1032         if( age > maxAge ) {
1033                 qry->agedFlag = TRUE;
1034         }
1035 }
1036
1037 /**
1038  * Delete folder associated with query results.
1039  * \param qry Query object to process.
1040  */
1041 void ldapqry_delete_folder( LdapQuery *qry ) {
1042         AddressCache *cache;
1043         ItemFolder *folder;
1044
1045         g_return_if_fail( qry != NULL );
1046
1047         folder = ADDRQUERY_FOLDER(qry);
1048         if( folder ) {
1049                 cache = qry->server->addressCache;
1050                 folder = addrcache_remove_folder_delete( cache, folder );
1051                 if( folder ) {
1052                         addritem_free_item_folder( folder );
1053                 }
1054                 ADDRQUERY_FOLDER(qry) = NULL;
1055         }
1056 }
1057
1058 /**
1059  * Create a name/value pair object.
1060  * \param n Name.
1061  * \param v Value.
1062  * \return Initialized object.
1063  */
1064 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1065         NameValuePair *nvp = g_new0( NameValuePair, 1 );
1066
1067         nvp->name = g_strdup( n );
1068         nvp->value = g_strdup( v );
1069         return nvp;
1070 }
1071
1072 /**
1073  * Free up name/value pair object.
1074  * \param nvp Name/value object.
1075  */
1076 void ldapqry_free_name_value( NameValuePair *nvp ) {
1077         if( nvp ) {
1078                 g_free( nvp->name );
1079                 g_free( nvp->value );
1080                 nvp->name = nvp->value = NULL;
1081                 g_free( nvp );
1082         }
1083 }
1084
1085 /**
1086  * Free up a list name/value pair objects.
1087  * \param list List of name/value objects.
1088  */
1089 void ldapqry_free_list_name_value( GList *list ) {
1090         GList *node;
1091
1092         node = list;
1093         while( node ) {
1094                 NameValuePair *nvp = ( NameValuePair * ) node->data;
1095                 ldapqry_free_name_value( nvp );
1096                 node->data = NULL;
1097                 node = g_list_next( node );
1098         }
1099         g_list_free( list );
1100 }
1101
1102 /**
1103  * Load a list of name/value pairs from LDAP attributes.
1104  * \param  ld          LDAP handle.
1105  * \param  e          LDAP message.
1106  * \param  attr       Attribute name.
1107  * \param  listValues List to populate.
1108  * \return List of attribute name/value pairs.
1109  */
1110 static GList *ldapqry_load_attrib_values(
1111                 LDAP *ld, LDAPMessage *entry, char *attr,
1112                 GList *listValues )
1113 {
1114         GList *list = NULL;
1115         gint i;
1116         struct berval **vals;
1117         NameValuePair *nvp;
1118
1119         list = listValues;
1120         if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1121                 for( i = 0; vals[i] != NULL; i++ ) {
1122                         gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1123                         nvp = ldapqry_create_name_value( attr, tmp );
1124                         g_free(tmp);
1125                         list = g_list_append( list, nvp );
1126                 }
1127         }
1128         ldap_value_free_len( vals );
1129         return list;
1130 }
1131
1132 /**
1133  * Fetch a list of all attributes.
1134  * \param  ld    LDAP handle.
1135  * \param  e     LDAP message.
1136  * \return List of attribute name/value pairs.
1137  */
1138 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1139 {
1140         char *attribute;
1141         BerElement *ber;
1142         GList *listValues = NULL;
1143
1144         /* Process all attributes */
1145         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1146                 attribute = ldap_next_attribute( ld, e, ber ) ) {
1147                 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1148                 ldap_memfree( attribute );
1149         }
1150
1151         /* Free up */
1152         if( ber != NULL ) {
1153                 ber_free( ber, 0 );
1154         }
1155         return listValues;
1156 }
1157
1158 #define CRITERIA_SINGLE "(objectclass=*)"
1159
1160 /**
1161  * Perform the data retrieval for a specific LDAP record.
1162  * 
1163  * \param  qry Query object to process.
1164  * \return Error/status code.
1165  */
1166 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1167         LdapControl *ctl;
1168         LDAP *ld;
1169         LDAPMessage *result, *e = NULL;
1170         gboolean entriesFound;
1171         gboolean first;
1172         struct timeval timeout;
1173         gint rc;
1174         gchar *dn;
1175         GList *listValues;
1176
1177         /* Initialize some variables */
1178         ld = qry->ldap;
1179         ctl = qry->control;
1180         dn = ADDRQUERY_SEARCHVALUE(qry);
1181         timeout.tv_sec = ctl->timeOut;
1182         timeout.tv_usec = 0L;
1183         entriesFound = FALSE;
1184
1185         /*
1186          * Execute the search - this step may take some time to complete
1187          * depending on network traffic and server response time.
1188          */
1189         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1190         rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1191                 NULL, 0, NULL, NULL, &timeout, 0, &result );
1192         if( rc == LDAP_TIMEOUT ) {
1193                 return ADDRQUERY_RETVAL(qry);
1194         }
1195         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1196         if( rc != LDAP_SUCCESS ) {
1197                 /*
1198                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
1199                 */
1200                 return ADDRQUERY_RETVAL(qry);
1201         }
1202
1203         /*
1204         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
1205         */
1206
1207         /* Process results */
1208         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1209         first = TRUE;
1210         while( TRUE ) {
1211                 ldapqry_touch( qry );
1212                 if( qry->entriesRead >= ctl->maxEntries ) break;                
1213
1214                 /* Test for stop */
1215                 if( ldapqry_get_stop_flag( qry ) ) {
1216                         break;
1217                 }
1218
1219                 /* Retrieve entry */            
1220                 if( first ) {
1221                         first = FALSE;
1222                         e = ldap_first_entry( ld, result );
1223                 }
1224                 else {
1225                         e = ldap_next_entry( ld, e );
1226                 }
1227                 if( e == NULL ) break;
1228
1229                 entriesFound = TRUE;
1230
1231                 /* Setup a critical section here */
1232                 pthread_mutex_lock( qry->mutexEntry );
1233
1234                 /* Process entry */
1235                 listValues = ldapqry_fetch_attribs( ld, e );
1236
1237                 /* Process callback */
1238                 if( qry->callBackEntry ) {
1239                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1240                 }
1241                 ldapqry_free_list_name_value( listValues );
1242                 listValues = NULL;
1243
1244                 pthread_mutex_unlock( qry->mutexEntry );
1245         }
1246
1247         /* Free up and disconnect */
1248         ldap_msgfree( result );
1249
1250         if( entriesFound ) {
1251                 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1252         }
1253         else {
1254                 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1255         }
1256
1257         return ADDRQUERY_RETVAL(qry);
1258 }
1259
1260 /**
1261  * Perform the search to locate a specific LDAP record identified by
1262  * distinguished name (dn).
1263  * 
1264  * \param  qry Query object to process.
1265  * \return Error/status code.
1266  */
1267 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1268         /* Connect */
1269         qry->ldap = NULL;
1270         ldapqry_connect( qry );
1271         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1272                 /* Perform search */
1273                 ldapqry_locate_retrieve( qry );
1274         }
1275         /* Disconnect */
1276         ldapqry_disconnect( qry );
1277         qry->ldap = NULL;
1278
1279         /* Process callback */  
1280         if( qry->callBackEnd ) {
1281                 g_timeout_add(0, callbackend, qry);
1282         }
1283
1284         return ADDRQUERY_RETVAL(qry);
1285 }
1286
1287 /**
1288  * Remove results (folder and data) for specified LDAP query.
1289  * \param  qry Query object to process.
1290  * \return TRUE if folder deleted successfully.
1291  */
1292 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1293         gboolean retVal = FALSE;
1294
1295         ldapqry_set_aged_flag( qry, TRUE );
1296
1297         if( ldapqry_get_busy_flag( qry ) ) {
1298                 ldapqry_set_stop_flag( qry, TRUE );
1299         }
1300         else {
1301                 LdapServer *server = qry->server;
1302                 server->listQuery = g_list_remove(server->listQuery, qry);
1303
1304                 retVal = TRUE;
1305         }
1306         return retVal;
1307 }
1308
1309 #endif  /* USE_LDAP */
1310
1311 /*
1312  * End of Source.
1313  */
1314
1315