my stab at fixing the non MT safety code in the LDAP lookup
[claws.git] / src / syldap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001 Match Grun
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Functions necessary to access LDAP servers.
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 <ldap.h>
34 #include <lber.h>
35 #include <pthread.h>
36 #include <dlfcn.h>
37
38 #include "mgutils.h"
39 #include "addritem.h"
40 #include "addrcache.h"
41 #include "syldap.h"
42 #include "utils.h"
43
44 /*
45 * Create new LDAP server interface object.
46 */
47 SyldapServer *syldap_create() {
48         SyldapServer *ldapServer;
49
50         debug_print("Creating LDAP server interface object\n");
51
52         ldapServer = g_new0( SyldapServer, 1 );
53         ldapServer->name = NULL;
54         ldapServer->hostName = NULL;
55         ldapServer->port = SYLDAP_DFL_PORT;
56         ldapServer->baseDN = NULL;
57         ldapServer->bindDN = NULL;
58         ldapServer->bindPass = NULL;
59         ldapServer->searchCriteria = NULL;
60         ldapServer->searchValue = NULL;
61         ldapServer->entriesRead = 0;
62         ldapServer->maxEntries = SYLDAP_MAX_ENTRIES;
63         ldapServer->timeOut = SYLDAP_DFL_TIMEOUT;
64         ldapServer->newSearch = TRUE;
65         ldapServer->addressCache = addrcache_create();
66         ldapServer->thread = NULL;
67         ldapServer->busyFlag = FALSE;
68         ldapServer->retVal = MGU_SUCCESS;
69         ldapServer->callBack = NULL;
70         ldapServer->accessFlag = FALSE;
71         ldapServer->idleId = 0;
72         return ldapServer;
73 }
74
75 /*
76 * Specify name to be used.
77 */
78 void syldap_set_name( SyldapServer* ldapServer, const gchar *value ) {
79         ldapServer->name = mgu_replace_string( ldapServer->name, value );
80         g_strstrip( ldapServer->name );
81 }
82
83 /*
84 * Specify hostname to be used.
85 */
86 void syldap_set_host( SyldapServer* ldapServer, const gchar *value ) {
87         addrcache_refresh( ldapServer->addressCache );
88         ldapServer->hostName = mgu_replace_string( ldapServer->hostName, value );
89         g_strstrip( ldapServer->hostName );
90 }
91
92 /*
93 * Specify port to be used.
94 */
95 void syldap_set_port( SyldapServer* ldapServer, const gint value ) {
96         addrcache_refresh( ldapServer->addressCache );
97         if( value > 0 ) {
98                 ldapServer->port = value;
99         }
100         else {
101                 ldapServer->port = SYLDAP_DFL_PORT;
102         }
103 }
104
105 /*
106 * Specify base DN to be used.
107 */
108 void syldap_set_base_dn( SyldapServer* ldapServer, const gchar *value ) {
109         addrcache_refresh( ldapServer->addressCache );
110         ldapServer->baseDN = mgu_replace_string( ldapServer->baseDN, value );
111         g_strstrip( ldapServer->baseDN );
112 }
113
114 /*
115 * Specify bind DN to be used.
116 */
117 void syldap_set_bind_dn( SyldapServer* ldapServer, const gchar *value ) {
118         addrcache_refresh( ldapServer->addressCache );
119         ldapServer->bindDN = mgu_replace_string( ldapServer->bindDN, value );
120         g_strstrip( ldapServer->bindDN );
121 }
122
123 /*
124 * Specify bind password to be used.
125 */
126 void syldap_set_bind_password( SyldapServer* ldapServer, const gchar *value ) {
127         addrcache_refresh( ldapServer->addressCache );
128         ldapServer->bindPass = mgu_replace_string( ldapServer->bindPass, value );
129         g_strstrip( ldapServer->bindPass );
130 }
131
132 /*
133 * Specify search criteria to be used.
134 */
135 void syldap_set_search_criteria( SyldapServer* ldapServer, const gchar *value ) {
136         addrcache_refresh( ldapServer->addressCache );
137         ldapServer->searchCriteria = mgu_replace_string( ldapServer->searchCriteria, value );
138         g_strstrip( ldapServer->searchCriteria );
139         ldapServer->newSearch = TRUE;
140 }
141
142 /*
143 * Specify search value to be searched for.
144 */
145 void syldap_set_search_value( SyldapServer* ldapServer, const gchar *value ) {
146         addrcache_refresh( ldapServer->addressCache );
147         ldapServer->searchValue = mgu_replace_string( ldapServer->searchValue, value );
148         g_strstrip( ldapServer->searchValue );
149         ldapServer->newSearch = TRUE;
150 }
151
152 /*
153 * Specify maximum number of entries to retrieve.
154 */
155 void syldap_set_max_entries( SyldapServer* ldapServer, const gint value ) {
156         addrcache_refresh( ldapServer->addressCache );
157         if( value > 0 ) {
158                 ldapServer->maxEntries = value;
159         }
160         else {
161                 ldapServer->maxEntries = SYLDAP_MAX_ENTRIES;
162         }
163 }
164
165 /*
166 * Specify timeout value for LDAP operation (in seconds).
167 */
168 void syldap_set_timeout( SyldapServer* ldapServer, const gint value ) {
169         addrcache_refresh( ldapServer->addressCache );
170         if( value > 0 ) {
171                 ldapServer->timeOut = value;
172         }
173         else {
174                 ldapServer->timeOut = SYLDAP_DFL_TIMEOUT;
175         }
176 }
177
178 /*
179 * Register a callback function. When called, the function will be passed
180 * this object as an argument.
181 */
182 void syldap_set_callback( SyldapServer *ldapServer, void *func ) {
183         ldapServer->callBack = func;
184 }
185
186 void syldap_set_accessed( SyldapServer *ldapServer, const gboolean value ) {
187         g_return_if_fail( ldapServer != NULL );
188         ldapServer->accessFlag = value;
189 }
190
191 /*
192 * Refresh internal variables to force a file read.
193 */
194 void syldap_force_refresh( SyldapServer *ldapServer ) {
195         addrcache_refresh( ldapServer->addressCache );
196         ldapServer->newSearch = TRUE;
197 }
198
199 gint syldap_get_status( SyldapServer *ldapServer ) {
200         g_return_val_if_fail( ldapServer != NULL, -1 );
201         return ldapServer->retVal;
202 }
203
204 ItemFolder *syldap_get_root_folder( SyldapServer *ldapServer ) {
205         g_return_val_if_fail( ldapServer != NULL, NULL );
206         return addrcache_get_root_folder( ldapServer->addressCache );
207 }
208
209 gchar *syldap_get_name( SyldapServer *ldapServer ) {
210         g_return_val_if_fail( ldapServer != NULL, NULL );
211         return ldapServer->name;
212 }
213
214 gboolean syldap_get_accessed( SyldapServer *ldapServer ) {
215         g_return_val_if_fail( ldapServer != NULL, FALSE );
216         return ldapServer->accessFlag;
217 }
218
219 /*
220 * Free up LDAP server interface object by releasing internal memory.
221 */
222 void syldap_free( SyldapServer *ldapServer ) {
223         g_return_if_fail( ldapServer != NULL );
224
225         debug_print("Freeing LDAP server interface object\n");
226
227         ldapServer->callBack = NULL;
228
229         /* Free internal stuff */
230         g_free( ldapServer->name );
231         g_free( ldapServer->hostName );
232         g_free( ldapServer->baseDN );
233         g_free( ldapServer->bindDN );
234         g_free( ldapServer->bindPass );
235         g_free( ldapServer->searchCriteria );
236         g_free( ldapServer->searchValue );
237
238         ldapServer->port = 0;
239         ldapServer->entriesRead = 0;
240         ldapServer->maxEntries = 0;
241         ldapServer->newSearch = FALSE;
242
243         /* Clear cache */
244         addrcache_clear( ldapServer->addressCache );
245         addrcache_free( ldapServer->addressCache );
246
247         /* Clear pointers */
248         ldapServer->name = NULL;
249         ldapServer->hostName = NULL;
250         ldapServer->baseDN = NULL;
251         ldapServer->bindDN = NULL;
252         ldapServer->bindPass = NULL;
253         ldapServer->searchCriteria = NULL;
254         ldapServer->searchValue = NULL;
255         ldapServer->addressCache = NULL;
256         g_free(ldapServer->thread);
257         ldapServer->thread = NULL;
258         ldapServer->busyFlag = FALSE;
259         ldapServer->retVal = MGU_SUCCESS;
260         ldapServer->accessFlag = FALSE;
261
262         /* Now release LDAP object */
263         g_free( ldapServer );
264
265 }
266
267 /*
268 * Display object to specified stream.
269 */
270 void syldap_print_data( SyldapServer *ldapServer, FILE *stream ) {
271         g_return_if_fail( ldapServer != NULL );
272
273         fprintf( stream, "SyldapServer:\n" );
274         fprintf( stream, "     name: '%s'\n", ldapServer->name );
275         fprintf( stream, "host name: '%s'\n", ldapServer->hostName );
276         fprintf( stream, "     port: %d\n",   ldapServer->port );
277         fprintf( stream, "  base dn: '%s'\n", ldapServer->baseDN );
278         fprintf( stream, "  bind dn: '%s'\n", ldapServer->bindDN );
279         fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass );
280         fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria );
281         fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue );
282         fprintf( stream, "max entry: %d\n",   ldapServer->maxEntries );
283         fprintf( stream, " num read: %d\n",   ldapServer->entriesRead );
284         fprintf( stream, "  ret val: %d\n",   ldapServer->retVal );
285         addrcache_print( ldapServer->addressCache, stream );
286         addritem_print_item_folder( ldapServer->addressCache->rootFolder, stream );
287 }
288
289 /*
290 * Display object to specified stream.
291 */
292 void syldap_print_short( SyldapServer *ldapServer, FILE *stream ) {
293         g_return_if_fail( ldapServer != NULL );
294
295         fprintf( stream, "SyldapServer:\n" );
296         fprintf( stream, "     name: '%s'\n", ldapServer->name );
297         fprintf( stream, "host name: '%s'\n", ldapServer->hostName );
298         fprintf( stream, "     port: %d\n",   ldapServer->port );
299         fprintf( stream, "  base dn: '%s'\n", ldapServer->baseDN );
300         fprintf( stream, "  bind dn: '%s'\n", ldapServer->bindDN );
301         fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass );
302         fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria );
303         fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue );
304         fprintf( stream, "max entry: %d\n",   ldapServer->maxEntries );
305         fprintf( stream, " num read: %d\n",   ldapServer->entriesRead );
306         fprintf( stream, "  ret val: %d\n",   ldapServer->retVal );
307 }
308
309 /*
310 * Build an address list entry and append to list of address items. Name is formatted
311 * as it appears in the common name (cn) attribute.
312 */
313 static void syldap_build_items_cn( SyldapServer *ldapServer, GSList *listName, GSList *listAddr ) {
314         ItemPerson *person;
315         ItemEMail *email;
316         GSList *nodeName = listName;
317
318         while( nodeName ) {
319                 GSList *nodeAddress = listAddr;
320                 person = addritem_create_item_person();
321                 addritem_person_set_common_name( person, nodeName->data );
322                 addrcache_id_person( ldapServer->addressCache, person );
323                 addrcache_add_person( ldapServer->addressCache, person );
324
325                 while( nodeAddress ) {
326                         email = addritem_create_item_email();
327                         addritem_email_set_address( email, nodeAddress->data );
328                         addrcache_id_email( ldapServer->addressCache, email );
329                         addrcache_person_add_email( ldapServer->addressCache, person, email );
330                         nodeAddress = g_slist_next( nodeAddress );
331                         ldapServer->entriesRead++;
332                 }
333                 nodeName = g_slist_next( nodeName );
334         }
335 }
336
337 /*
338 * Build an address list entry and append to list of address items. Name is formatted
339 * as "<first-name> <last-name>".
340 */
341 static void syldap_build_items_fl( SyldapServer *ldapServer, GSList *listAddr, GSList *listFirst, GSList *listLast  ) {
342         GSList *nodeFirst = listFirst;
343         GSList *nodeAddress = listAddr;
344         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
345         gint iLen = 0, iLenT = 0;
346         ItemPerson *person;
347         ItemEMail *email;
348
349         /* Find longest first name in list */
350         while( nodeFirst ) {
351                 if( firstName == NULL ) {
352                         firstName = nodeFirst->data;
353                         iLen = strlen( firstName );
354                 }
355                 else {
356                         if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
357                                 firstName = nodeFirst->data;
358                                 iLen = iLenT;
359                         }
360                 }
361                 nodeFirst = g_slist_next( nodeFirst );
362         }
363
364         /* Format name */
365         if( listLast ) {
366                 lastName = listLast->data;
367         }
368
369         if( firstName ) {
370                 if( lastName ) {
371                         fullName = g_strdup_printf( "%s %s", firstName, lastName );
372                 }
373                 else {
374                         fullName = g_strdup_printf( "%s", firstName );
375                 }
376         }
377         else {
378                 if( lastName ) {
379                         fullName = g_strdup_printf( "%s", lastName );
380                 }
381         }
382         if( fullName ) {
383                 g_strchug( fullName ); g_strchomp( fullName );
384         }
385
386         if( nodeAddress ) {
387                 person = addritem_create_item_person();
388                 addritem_person_set_common_name( person, fullName );
389                 addritem_person_set_first_name( person, firstName );
390                 addritem_person_set_last_name( person, lastName );
391                 addrcache_id_person( ldapServer->addressCache, person );
392                 addrcache_add_person( ldapServer->addressCache, person );
393         }
394
395         /* Add address item */
396         while( nodeAddress ) {
397                 email = addritem_create_item_email();
398                 addritem_email_set_address( email, nodeAddress->data );
399                 addrcache_id_email( ldapServer->addressCache, email );
400                 addrcache_person_add_email( ldapServer->addressCache, person, email );
401                 nodeAddress = g_slist_next( nodeAddress );
402                 ldapServer->entriesRead++;
403         }
404         g_free( fullName );
405         fullName = firstName = lastName = NULL;
406
407 }
408
409 /*
410 * Add all attribute values to a list.
411 */
412 static GSList *syldap_add_list_values( LDAP *ld, LDAPMessage *entry, char *attr ) {
413         GSList *list = NULL;
414         gint i;
415         gchar **vals;
416
417         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
418                 for( i = 0; vals[i] != NULL; i++ ) {
419                         /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
420                         list = g_slist_append( list, g_strdup( vals[i] ) );
421                 }
422         }
423         ldap_value_free( vals );
424         return list;
425 }
426
427 /*
428 * Add a single attribute value to a list.
429 */
430 static GSList *syldap_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
431         GSList *list = NULL;
432         gchar **vals;
433
434         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
435                 if( vals[0] != NULL ) {
436                         /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
437                         list = g_slist_append( list, g_strdup( vals[0] ) );
438                 }
439         }
440         ldap_value_free( vals );
441         return list;
442 }
443
444 /*
445 * Free linked lists of character strings.
446 */
447 static void syldap_free_lists( GSList *listName, GSList *listAddr, GSList *listID, GSList *listDN, GSList *listFirst, GSList *listLast ) {
448         mgu_free_list( listName );
449         mgu_free_list( listAddr );
450         mgu_free_list( listID );
451         mgu_free_list( listDN );
452         mgu_free_list( listFirst );
453         mgu_free_list( listLast );
454 }
455
456 /*
457 * Check parameters that are required for a search. This should
458 * be called before performing a search.
459 * Return: TRUE if search criteria appear OK.
460 */
461 gboolean syldap_check_search( SyldapServer *ldapServer ) {
462         g_return_val_if_fail( ldapServer != NULL, FALSE );
463
464         ldapServer->retVal = MGU_LDAP_CRITERIA;
465
466         /* Test search criteria */
467         if( ldapServer->searchCriteria == NULL ) {
468                 return FALSE;
469         }
470         if( strlen( ldapServer->searchCriteria ) < 1 ) {
471                 return FALSE;
472         }
473
474         if( ldapServer->searchValue == NULL ) {
475                 return FALSE;
476         }
477         if( strlen( ldapServer->searchValue ) < 1 ) {
478                 return FALSE;
479         }
480
481         ldapServer->retVal = MGU_SUCCESS;
482         return TRUE;
483 }
484
485 /*
486 * Perform the LDAP search, reading LDAP entries into cache.
487 * Note that one LDAP entry can have multiple values for many of its
488 * attributes. If these attributes are E-Mail addresses; these are
489 * broken out into separate address items. For any other attribute,
490 * only the first occurrence is read.
491 */
492 gint syldap_search( SyldapServer *ldapServer ) {
493         LDAP *ld;
494         LDAPMessage *result, *e;
495         char *attribs[10];
496         char *attribute;
497         gchar *criteria;
498         BerElement *ber;
499         gint rc;
500         GSList *listName = NULL, *listAddress = NULL, *listID = NULL;
501         GSList *listFirst = NULL, *listLast = NULL, *listDN = NULL;
502         struct timeval timeout;
503         gboolean entriesFound = FALSE;
504
505         g_return_val_if_fail( ldapServer != NULL, -1 );
506
507         ldapServer->retVal = MGU_SUCCESS;
508         if( ! syldap_check_search( ldapServer ) ) {
509                 return ldapServer->retVal;
510         }
511
512         /* Set timeout */
513         timeout.tv_sec = ldapServer->timeOut;
514         timeout.tv_usec = 0L;
515
516         ldapServer->entriesRead = 0;
517         if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) {
518                 ldapServer->retVal = MGU_LDAP_INIT;
519                 return ldapServer->retVal;
520         }
521
522         /* printf( "connected to LDAP host %s on port %d\n", ldapServer->hostName, ldapServer->port ); */
523
524         /* Bind to the server, if required */
525         if( ldapServer->bindDN ) {
526                 if( * ldapServer->bindDN != '\0' ) {
527                         /* printf( "binding...\n" ); */
528                         rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass );
529                         /* printf( "rc=%d\n", rc ); */
530                         if( rc != LDAP_SUCCESS ) {
531                                 /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */
532                                 ldap_unbind( ld );
533                                 ldapServer->retVal = MGU_LDAP_BIND;
534                                 return ldapServer->retVal;
535                         }
536                 }
537         }
538
539         /* Define all attributes we are interested in. */
540         attribs[0] = SYLDAP_ATTR_DN;
541         attribs[1] = SYLDAP_ATTR_COMMONNAME;
542         attribs[2] = SYLDAP_ATTR_GIVENNAME;
543         attribs[3] = SYLDAP_ATTR_SURNAME;
544         attribs[4] = SYLDAP_ATTR_EMAIL;
545         attribs[5] = SYLDAP_ATTR_UID;
546         attribs[6] = NULL;
547
548         /* Create LDAP search string and apply search criteria */
549         criteria = g_strdup_printf( ldapServer->searchCriteria, ldapServer->searchValue );
550         rc = ldap_search_ext_s( ld, ldapServer->baseDN, LDAP_SCOPE_SUBTREE, criteria, attribs, 0, NULL, NULL,
551                        &timeout, 0, &result );
552         g_free( criteria );
553         criteria = NULL;
554         if( rc == LDAP_TIMEOUT ) {
555                 ldap_unbind( ld );
556                 ldapServer->retVal = MGU_LDAP_TIMEOUT;
557                 return ldapServer->retVal;
558         }
559         if( rc != LDAP_SUCCESS ) {
560                 /* printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) ); */
561                 ldap_unbind( ld );
562                 ldapServer->retVal = MGU_LDAP_SEARCH;
563                 return ldapServer->retVal;
564         }
565
566         /* printf( "Total results are: %d\n", ldap_count_entries( ld, result ) ); */
567
568         /* Clear the cache if we have new entries, otherwise leave untouched. */
569         if( ldap_count_entries( ld, result ) > 0 ) {
570                 addrcache_clear( ldapServer->addressCache );
571         }
572
573         /* Process results */
574         ldapServer->entriesRead = 0;
575         for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
576                 entriesFound = TRUE;
577                 if( ldapServer->entriesRead >= ldapServer->maxEntries ) break;          
578                 /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
579
580                 /* Process all attributes */
581                 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
582                                attribute = ldap_next_attribute( ld, e, ber ) ) {
583                         if( strcasecmp( attribute, SYLDAP_ATTR_COMMONNAME ) == 0 ) {
584                                 listName = syldap_add_list_values( ld, e, attribute );
585                         }
586                         if( strcasecmp( attribute, SYLDAP_ATTR_EMAIL ) == 0 ) {
587                                 listAddress = syldap_add_list_values( ld, e, attribute );
588                         }
589                         if( strcasecmp( attribute, SYLDAP_ATTR_UID ) == 0 ) {
590                                 listID = syldap_add_single_value( ld, e, attribute );
591                         }
592                         if( strcasecmp( attribute, SYLDAP_ATTR_GIVENNAME ) == 0 ) {
593                                 listFirst = syldap_add_list_values( ld, e, attribute );
594                         }
595                         if( strcasecmp( attribute, SYLDAP_ATTR_SURNAME ) == 0 ) {
596                                 listLast = syldap_add_single_value( ld, e, attribute );
597                         }
598                         if( strcasecmp( attribute, SYLDAP_ATTR_DN ) == 0 ) {
599                                 listDN = syldap_add_single_value( ld, e, attribute );
600                         }
601                 }
602
603                 /* Free memory used to store attribute */
604                 ldap_memfree( attribute );
605
606                 /* Format and add items to cache */
607                 syldap_build_items_fl( ldapServer, listAddress, listFirst, listLast );
608
609                 /* Free up */
610                 syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast );
611                 listName = listAddress = listID = listFirst = listLast = listDN = NULL;
612
613                 if( ber != NULL ) {
614                         ber_free( ber, 0 );
615                 }
616         }
617
618         syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast );
619         listName = listAddress = listID = listFirst = listLast = listDN = NULL;
620         
621         /* Free up and disconnect */
622         ldap_msgfree( result );
623         ldap_unbind( ld );
624         ldapServer->newSearch = FALSE;
625         if( entriesFound ) {
626                 ldapServer->retVal = MGU_SUCCESS;
627         }
628         else {
629                 ldapServer->retVal = MGU_LDAP_NOENTRIES;
630         }
631         return ldapServer->retVal;
632 }
633
634 /* syldap_display_search_results() - updates the ui. this function is called from the
635  * main thread (the thread running the GTK event loop). */
636 static gint syldap_display_search_results(SyldapServer *ldapServer)
637 {
638         /* NOTE: when this function is called the accompanying thread should 
639          * already be terminated. */
640         gtk_idle_remove(ldapServer->idleId);
641         ldapServer->callBack(ldapServer);
642         /* FIXME:  match should know whether to free this SyldapServer stuff.  */
643         g_free(ldapServer->thread); 
644         return TRUE;
645 }
646
647 /* ============================================================================================ */
648 /*
649 * Read data into list. Main entry point
650 * Return: TRUE if file read successfully.
651 */
652 /* ============================================================================================ */
653 gint syldap_read_data( SyldapServer *ldapServer ) {
654         g_return_val_if_fail( ldapServer != NULL, -1 );
655
656         ldapServer->accessFlag = FALSE;
657         pthread_detach( pthread_self() );
658         if( ldapServer->newSearch ) {
659                 /* Read data into the list */
660                 syldap_search( ldapServer );
661
662                 /* Mark cache */
663                 ldapServer->addressCache->modified = FALSE;
664                 ldapServer->addressCache->dataRead = TRUE;
665                 ldapServer->accessFlag = FALSE;
666         }
667
668         /* Callback */
669         ldapServer->busyFlag = FALSE;
670         if( ldapServer->callBack ) {
671                 /* make the ui thread update the search results */
672                 /* TODO: really necessary to call gdk_threads_XXX()??? gtk_idle_add()
673                  * should do this - could someone check the GTK sources please? */
674                 gdk_threads_enter();
675                 ldapServer->idleId = gtk_idle_add(syldap_display_search_results, ldapServer);
676                 gdk_threads_leave();
677         
678         }
679
680         return ldapServer->retVal;
681 }
682
683 /* ============================================================================================ */
684 /*
685 * Cancel read with thread.
686 */
687 /* ============================================================================================ */
688 void syldap_cancel_read( SyldapServer *ldapServer ) {
689         g_return_if_fail( ldapServer != NULL );
690
691         /* DELETEME: this is called from inside UI thread so it's OK, Christoph! */
692         if( ldapServer->thread ) {
693                 /* printf( "thread cancelled\n" ); */
694                 pthread_cancel( *ldapServer->thread );
695         }
696         g_free(ldapServer->thread);
697         ldapServer->thread = NULL;
698         ldapServer->busyFlag = FALSE;
699 }
700
701 /* ============================================================================================ */
702 /*
703 * Read data into list using a background thread.
704 * Return: TRUE if file read successfully. Callback function will be
705 * notified when search is complete.
706 */
707 /* ============================================================================================ */
708 gint syldap_read_data_th( SyldapServer *ldapServer ) {
709         g_return_val_if_fail( ldapServer != NULL, -1 );
710
711         ldapServer->busyFlag = FALSE;
712         syldap_check_search( ldapServer );
713         if( ldapServer->retVal == MGU_SUCCESS ) {
714                 debug_print("Staring LDAP read thread\n");
715
716                 ldapServer->busyFlag = TRUE;
717                 ldapServer->thread = g_new0(pthread_t, 1);
718                 pthread_create( ldapServer->thread, NULL, (void *) syldap_read_data, (void *) ldapServer );
719         }
720         return ldapServer->retVal;
721 }
722
723 /*
724 * Return link list of persons.
725 */
726 GList *syldap_get_list_person( SyldapServer *ldapServer ) {
727         g_return_val_if_fail( ldapServer != NULL, NULL );
728         return addrcache_get_list_person( ldapServer->addressCache );
729 }
730
731 /*
732 * Return link list of folders. This is always NULL since there are
733 * no folders in GnomeCard.
734 * Return: NULL.
735 */
736 GList *syldap_get_list_folder( SyldapServer *ldapServer ) {
737         g_return_val_if_fail( ldapServer != NULL, NULL );
738         return NULL;
739 }
740
741 #define SYLDAP_TEST_FILTER   "(objectclass=*)"
742 #define SYLDAP_SEARCHBASE_V2 "cn=config"
743 #define SYLDAP_SEARCHBASE_V3 ""
744 #define SYLDAP_V2_TEST_ATTR  "database"
745 #define SYLDAP_V3_TEST_ATTR  "namingcontexts"
746
747 /*
748 * Attempt to discover the base DN for the server.
749 * Enter:
750 *       host    Host name
751 *       port    Port number
752 *       bindDN  Bind DN (optional).
753 *       bindPW  Bind PW (optional).
754 *       tov     Timeout value (seconds), or 0 for none, default 30 secs.
755 * Return: List of Base DN's, or NULL if could not read. Base DN should
756 * be g_free() when done.
757 */
758 GList *syldap_read_basedn_s( const gchar *host, const gint port, const gchar *bindDN, const gchar *bindPW, const gint tov ) {
759         GList *baseDN = NULL;
760         LDAP *ld;
761         gint rc, i;
762         LDAPMessage *result, *e;
763         gchar *attribs[10];
764         BerElement *ber;
765         gchar *attribute;
766         gchar **vals;
767         struct timeval timeout;
768
769         if( host == NULL ) return baseDN;
770         if( port < 1 ) return baseDN;
771
772         /* Set timeout */
773         timeout.tv_usec = 0L;
774         if( tov > 0 ) {
775                 timeout.tv_sec = tov;
776         }
777         else {
778                 timeout.tv_sec = 30L;
779         }
780
781         /* Connect to server. */
782         if( ( ld = ldap_init( host, port ) ) == NULL ) {
783                 return baseDN;
784         }
785
786         /* Bind to the server, if required */
787         if( bindDN ) {
788                 if( *bindDN != '\0' ) {
789                         rc = ldap_simple_bind_s( ld, bindDN, bindPW );
790                         if( rc != LDAP_SUCCESS ) {
791                                 /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */
792                                 ldap_unbind( ld );
793                                 return baseDN;
794                         }
795                 }
796         }
797
798         /* Test for LDAP version 3 */
799         attribs[0] = SYLDAP_V3_TEST_ATTR;
800         attribs[1] = NULL;
801         rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
802                        0, NULL, NULL, &timeout, 0, &result );
803         if( rc == LDAP_SUCCESS ) {
804                 /* Process entries */
805                 for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
806                         /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
807
808                         /* Process attributes */
809                         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
810                                         attribute = ldap_next_attribute( ld, e, ber ) ) {
811                                 if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
812                                         if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
813                                                 for( i = 0; vals[i] != NULL; i++ ) {
814                                                         /* printf( "\t%s: %s\n", attribute, vals[i] ); */
815                                                         baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
816                                                 }
817                                         }
818                                         ldap_value_free( vals );
819                                 }
820                         }
821                         ldap_memfree( attribute );
822                         if( ber != NULL ) {
823                                 ber_free( ber, 0 );
824                         }
825                 }
826                 ldap_msgfree( result );
827         }
828         else {
829         }
830
831         if( baseDN == NULL ) {
832                 /* Test for LDAP version 2 */
833                 attribs[0] = NULL;
834                 rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
835                                0, NULL, NULL, &timeout, 0, &result );
836                 if( rc == LDAP_SUCCESS ) {
837                         /* Process entries */
838                         for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
839                                 /* if( baseDN ) break;                   */
840                                 /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
841
842                                 /* Process attributes */
843                                 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
844                                                attribute = ldap_next_attribute( ld, e, ber ) ) {
845                                         /* if( baseDN ) break;                   */
846                                         if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
847                                                 if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
848                                                         for( i = 0; vals[i] != NULL; i++ ) {
849                                                                 char *ch;
850                                                                 /* Strip the 'ldb:' from the front of the value */
851                                                                 ch = ( char * ) strchr( vals[i], ':' );
852                                                                 if( ch ) {
853                                                                         gchar *bn = g_strdup( ++ch );
854                                                                         g_strchomp( bn );
855                                                                         g_strchug( bn );
856                                                                         baseDN = g_list_append( baseDN, g_strdup( bn ) );
857                                                                 }
858                                                         }
859                                                 }
860                                                 ldap_value_free( vals );
861                                         }
862                                 }
863                                 ldap_memfree( attribute );
864                                 if( ber != NULL ) {
865                                         ber_free( ber, 0 );
866                                 }
867                         }
868                         ldap_msgfree( result );
869                 }
870         }
871         ldap_unbind( ld );
872         return baseDN;
873 }
874
875 /*
876 * Attempt to discover the base DN for the server.
877 * Enter:  ldapServer Server to test.
878 * Return: List of Base DN's, or NULL if could not read. Base DN should
879 * be g_free() when done. Return code set in ldapServer.
880 */
881 GList *syldap_read_basedn( SyldapServer *ldapServer ) {
882         GList *baseDN = NULL;
883         LDAP *ld;
884         gint rc, i;
885         LDAPMessage *result, *e;
886         gchar *attribs[10];
887         BerElement *ber;
888         gchar *attribute;
889         gchar **vals;
890         struct timeval timeout;
891
892         ldapServer->retVal = MGU_BAD_ARGS;
893         if( ldapServer == NULL ) return baseDN;
894         if( ldapServer->hostName == NULL ) return baseDN;
895         if( ldapServer->port < 1 ) return baseDN;
896
897         /* Set timeout */
898         timeout.tv_usec = 0L;
899         if( ldapServer->timeOut > 0 ) {
900                 timeout.tv_sec = ldapServer->timeOut;
901         }
902         else {
903                 timeout.tv_sec = 30L;
904         }
905
906         /* Connect to server. */
907         if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) {
908                 ldapServer->retVal = MGU_LDAP_INIT;
909                 return baseDN;
910         }
911
912         /* Bind to the server, if required */
913         if( ldapServer->bindDN ) {
914                 if( *ldapServer->bindDN != '\0' ) {
915                         rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass );
916                         if( rc != LDAP_SUCCESS ) {
917                                 /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */
918                                 ldap_unbind( ld );
919                                 ldapServer->retVal = MGU_LDAP_BIND;
920                                 return baseDN;
921                         }
922                 }
923         }
924
925         ldapServer->retVal = MGU_LDAP_SEARCH;
926
927         /* Test for LDAP version 3 */
928         attribs[0] = SYLDAP_V3_TEST_ATTR;
929         attribs[1] = NULL;
930         rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
931                        0, NULL, NULL, &timeout, 0, &result );
932         if( rc == LDAP_SUCCESS ) {
933                 /* Process entries */
934                 for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
935                         /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
936
937                         /* Process attributes */
938                         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
939                                         attribute = ldap_next_attribute( ld, e, ber ) ) {
940                                 if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
941                                         if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
942                                                 for( i = 0; vals[i] != NULL; i++ ) {
943                                                         /* printf( "\t%s: %s\n", attribute, vals[i] ); */
944                                                         baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
945                                                 }
946                                         }
947                                         ldap_value_free( vals );
948                                 }
949                         }
950                         ldap_memfree( attribute );
951                         if( ber != NULL ) {
952                                 ber_free( ber, 0 );
953                         }
954                 }
955                 ldap_msgfree( result );
956                 ldapServer->retVal = MGU_SUCCESS;
957         }
958         else if( rc == LDAP_TIMEOUT ) {
959                 ldapServer->retVal = MGU_LDAP_TIMEOUT;
960         }
961
962         if( baseDN == NULL ) {
963                 /* Test for LDAP version 2 */
964                 attribs[0] = NULL;
965                 rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
966                                0, NULL, NULL, &timeout, 0, &result );
967                 if( rc == LDAP_SUCCESS ) {
968                         /* Process entries */
969                         for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
970                                 /* if( baseDN ) break;                   */
971                                 /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
972
973                                 /* Process attributes */
974                                 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
975                                                attribute = ldap_next_attribute( ld, e, ber ) ) {
976                                         /* if( baseDN ) break;                   */
977                                         if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
978                                                 if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
979                                                         for( i = 0; vals[i] != NULL; i++ ) {
980                                                                 char *ch;
981                                                                 /* Strip the 'ldb:' from the front of the value */
982                                                                 ch = ( char * ) strchr( vals[i], ':' );
983                                                                 if( ch ) {
984                                                                         gchar *bn = g_strdup( ++ch );
985                                                                         g_strchomp( bn );
986                                                                         g_strchug( bn );
987                                                                         baseDN = g_list_append( baseDN, g_strdup( bn ) );
988                                                                 }
989                                                         }
990                                                 }
991                                                 ldap_value_free( vals );
992                                         }
993                                 }
994                                 ldap_memfree( attribute );
995                                 if( ber != NULL ) {
996                                         ber_free( ber, 0 );
997                                 }
998                         }
999                         ldap_msgfree( result );
1000                         ldapServer->retVal = MGU_SUCCESS;
1001                 }
1002                 else if( rc == LDAP_TIMEOUT ) {
1003                         ldapServer->retVal = MGU_LDAP_TIMEOUT;
1004                 }
1005         }
1006         ldap_unbind( ld );
1007
1008         return baseDN;
1009 }
1010
1011 /*
1012 * Attempt to connect to the server.
1013 * Enter:
1014 *       host    Host name
1015 *       port    Port number
1016 * Return: TRUE if connected successfully.
1017 */
1018 gboolean syldap_test_connect_s( const gchar *host, const gint port ) {
1019         gboolean retVal = FALSE;
1020         LDAP *ld;
1021
1022         if( host == NULL ) return retVal;
1023         if( port < 1 ) return retVal;
1024         if( ( ld = ldap_open( host, port ) ) != NULL ) {
1025                 retVal = TRUE;
1026         }
1027         if( ld != NULL ) {
1028                 ldap_unbind( ld );
1029         }
1030         return retVal;
1031 }
1032
1033 /*
1034 * Attempt to connect to the server.
1035 * Enter:  ldapServer Server to test.
1036 * Return: TRUE if connected successfully. Return code set in ldapServer.
1037 */
1038 gboolean syldap_test_connect( SyldapServer *ldapServer ) {
1039         gboolean retVal = FALSE;
1040         LDAP *ld;
1041
1042         ldapServer->retVal = MGU_BAD_ARGS;
1043         if( ldapServer == NULL ) return retVal;
1044         if( ldapServer->hostName == NULL ) return retVal;
1045         if( ldapServer->port < 1 ) return retVal;
1046         ldapServer->retVal = MGU_LDAP_INIT;
1047         if( ( ld = ldap_open( ldapServer->hostName, ldapServer->port ) ) != NULL ) {
1048                 ldapServer->retVal = MGU_SUCCESS;
1049                 retVal = TRUE;
1050         }
1051         if( ld != NULL ) {
1052                 ldap_unbind( ld );
1053         }
1054         return retVal;
1055 }
1056
1057 #define LDAP_LINK_LIB_NAME_1 "libldap.so"
1058 #define LDAP_LINK_LIB_NAME_2 "liblber.so"
1059 #define LDAP_LINK_LIB_NAME_3 "libresolv.so"
1060 #define LDAP_LINK_LIB_NAME_4 "libpthread.so"
1061
1062 /*
1063 * Test whether LDAP libraries installed.
1064 * Return: TRUE if library available.
1065 */
1066 gboolean syldap_test_ldap_lib() {
1067         void *handle, *fun;
1068         
1069         /* Get library */
1070         handle = dlopen( LDAP_LINK_LIB_NAME_1, RTLD_LAZY );
1071         if( ! handle ) {
1072                 return FALSE;
1073         }
1074
1075         /* Test for symbols we need */
1076         fun = dlsym( handle, "ldap_init" );
1077         if( ! fun ) {
1078                 dlclose( handle );
1079                 return FALSE;
1080         }
1081         dlclose( handle ); handle = NULL; fun = NULL;
1082
1083         handle = dlopen( LDAP_LINK_LIB_NAME_2, RTLD_LAZY );
1084         if( ! handle ) {
1085                 return FALSE;
1086         }
1087         fun = dlsym( handle, "ber_init" );
1088         if( ! fun ) {
1089                 dlclose( handle );
1090                 return FALSE;
1091         }
1092         dlclose( handle ); handle = NULL; fun = NULL;
1093
1094         handle = dlopen( LDAP_LINK_LIB_NAME_3, RTLD_LAZY );
1095         if( ! handle ) {
1096                 return FALSE;
1097         }
1098         fun = dlsym( handle, "res_query" );
1099         if( ! fun ) {
1100                 dlclose( handle );
1101                 return FALSE;
1102         }
1103         dlclose( handle ); handle = NULL; fun = NULL;
1104
1105         handle = dlopen( LDAP_LINK_LIB_NAME_4, RTLD_LAZY );
1106         if( ! handle ) {
1107                 return FALSE;
1108         }
1109         fun = dlsym( handle, "pthread_create" );
1110         if( ! fun ) {
1111                 dlclose( handle );
1112                 return FALSE;
1113         }
1114         dlclose( handle ); handle = NULL; fun = NULL;
1115
1116         return TRUE;
1117 }
1118
1119 #endif  /* USE_LDAP */
1120
1121 /*
1122 * End of Source.
1123 */