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