don't forget to set thread member to null
[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         ldapServer->thread = NULL;
645         return TRUE;
646 }
647
648 /* ============================================================================================ */
649 /*
650 * Read data into list. Main entry point
651 * Return: TRUE if file read successfully.
652 */
653 /* ============================================================================================ */
654 gint syldap_read_data( SyldapServer *ldapServer ) {
655         g_return_val_if_fail( ldapServer != NULL, -1 );
656
657         ldapServer->accessFlag = FALSE;
658         pthread_detach( pthread_self() );
659         if( ldapServer->newSearch ) {
660                 /* Read data into the list */
661                 syldap_search( ldapServer );
662
663                 /* Mark cache */
664                 ldapServer->addressCache->modified = FALSE;
665                 ldapServer->addressCache->dataRead = TRUE;
666                 ldapServer->accessFlag = FALSE;
667         }
668
669         /* Callback */
670         ldapServer->busyFlag = FALSE;
671         if( ldapServer->callBack ) {
672                 /* make the ui thread update the search results */
673                 /* TODO: really necessary to call gdk_threads_XXX()??? gtk_idle_add()
674                  * should do this - could someone check the GTK sources please? */
675                 gdk_threads_enter();
676                 ldapServer->idleId = gtk_idle_add(syldap_display_search_results, ldapServer);
677                 gdk_threads_leave();
678         
679         }
680
681         return ldapServer->retVal;
682 }
683
684 /* ============================================================================================ */
685 /*
686 * Cancel read with thread.
687 */
688 /* ============================================================================================ */
689 void syldap_cancel_read( SyldapServer *ldapServer ) {
690         g_return_if_fail( ldapServer != NULL );
691
692         /* DELETEME: this is called from inside UI thread so it's OK, Christoph! */
693         if( ldapServer->thread ) {
694                 /* printf( "thread cancelled\n" ); */
695                 pthread_cancel( *ldapServer->thread );
696         }
697         g_free(ldapServer->thread);
698         ldapServer->thread = NULL;
699         ldapServer->busyFlag = FALSE;
700 }
701
702 /* ============================================================================================ */
703 /*
704 * Read data into list using a background thread.
705 * Return: TRUE if file read successfully. Callback function will be
706 * notified when search is complete.
707 */
708 /* ============================================================================================ */
709 gint syldap_read_data_th( SyldapServer *ldapServer ) {
710         g_return_val_if_fail( ldapServer != NULL, -1 );
711
712         ldapServer->busyFlag = FALSE;
713         syldap_check_search( ldapServer );
714         if( ldapServer->retVal == MGU_SUCCESS ) {
715 //              debug_print("Staring LDAP read thread\n");
716
717                 ldapServer->busyFlag = TRUE;
718                 ldapServer->thread = g_new0(pthread_t, 1);
719                 pthread_create( ldapServer->thread, NULL, (void *) syldap_read_data, (void *) ldapServer );
720         }
721         return ldapServer->retVal;
722 }
723
724 /*
725 * Return link list of persons.
726 */
727 GList *syldap_get_list_person( SyldapServer *ldapServer ) {
728         g_return_val_if_fail( ldapServer != NULL, NULL );
729         return addrcache_get_list_person( ldapServer->addressCache );
730 }
731
732 /*
733 * Return link list of folders. This is always NULL since there are
734 * no folders in GnomeCard.
735 * Return: NULL.
736 */
737 GList *syldap_get_list_folder( SyldapServer *ldapServer ) {
738         g_return_val_if_fail( ldapServer != NULL, NULL );
739         return NULL;
740 }
741
742 #define SYLDAP_TEST_FILTER   "(objectclass=*)"
743 #define SYLDAP_SEARCHBASE_V2 "cn=config"
744 #define SYLDAP_SEARCHBASE_V3 ""
745 #define SYLDAP_V2_TEST_ATTR  "database"
746 #define SYLDAP_V3_TEST_ATTR  "namingcontexts"
747
748 /*
749 * Attempt to discover the base DN for the server.
750 * Enter:
751 *       host    Host name
752 *       port    Port number
753 *       bindDN  Bind DN (optional).
754 *       bindPW  Bind PW (optional).
755 *       tov     Timeout value (seconds), or 0 for none, default 30 secs.
756 * Return: List of Base DN's, or NULL if could not read. Base DN should
757 * be g_free() when done.
758 */
759 GList *syldap_read_basedn_s( const gchar *host, const gint port, const gchar *bindDN, const gchar *bindPW, const gint tov ) {
760         GList *baseDN = NULL;
761         LDAP *ld;
762         gint rc, i;
763         LDAPMessage *result, *e;
764         gchar *attribs[10];
765         BerElement *ber;
766         gchar *attribute;
767         gchar **vals;
768         struct timeval timeout;
769
770         if( host == NULL ) return baseDN;
771         if( port < 1 ) return baseDN;
772
773         /* Set timeout */
774         timeout.tv_usec = 0L;
775         if( tov > 0 ) {
776                 timeout.tv_sec = tov;
777         }
778         else {
779                 timeout.tv_sec = 30L;
780         }
781
782         /* Connect to server. */
783         if( ( ld = ldap_init( host, port ) ) == NULL ) {
784                 return baseDN;
785         }
786
787         /* Bind to the server, if required */
788         if( bindDN ) {
789                 if( *bindDN != '\0' ) {
790                         rc = ldap_simple_bind_s( ld, bindDN, bindPW );
791                         if( rc != LDAP_SUCCESS ) {
792                                 /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */
793                                 ldap_unbind( ld );
794                                 return baseDN;
795                         }
796                 }
797         }
798
799         /* Test for LDAP version 3 */
800         attribs[0] = SYLDAP_V3_TEST_ATTR;
801         attribs[1] = NULL;
802         rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
803                        0, NULL, NULL, &timeout, 0, &result );
804         if( rc == LDAP_SUCCESS ) {
805                 /* Process entries */
806                 for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
807                         /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
808
809                         /* Process attributes */
810                         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
811                                         attribute = ldap_next_attribute( ld, e, ber ) ) {
812                                 if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
813                                         if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
814                                                 for( i = 0; vals[i] != NULL; i++ ) {
815                                                         /* printf( "\t%s: %s\n", attribute, vals[i] ); */
816                                                         baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
817                                                 }
818                                         }
819                                         ldap_value_free( vals );
820                                 }
821                         }
822                         ldap_memfree( attribute );
823                         if( ber != NULL ) {
824                                 ber_free( ber, 0 );
825                         }
826                 }
827                 ldap_msgfree( result );
828         }
829         else {
830         }
831
832         if( baseDN == NULL ) {
833                 /* Test for LDAP version 2 */
834                 attribs[0] = NULL;
835                 rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
836                                0, NULL, NULL, &timeout, 0, &result );
837                 if( rc == LDAP_SUCCESS ) {
838                         /* Process entries */
839                         for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
840                                 /* if( baseDN ) break;                   */
841                                 /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
842
843                                 /* Process attributes */
844                                 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
845                                                attribute = ldap_next_attribute( ld, e, ber ) ) {
846                                         /* if( baseDN ) break;                   */
847                                         if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
848                                                 if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
849                                                         for( i = 0; vals[i] != NULL; i++ ) {
850                                                                 char *ch;
851                                                                 /* Strip the 'ldb:' from the front of the value */
852                                                                 ch = ( char * ) strchr( vals[i], ':' );
853                                                                 if( ch ) {
854                                                                         gchar *bn = g_strdup( ++ch );
855                                                                         g_strchomp( bn );
856                                                                         g_strchug( bn );
857                                                                         baseDN = g_list_append( baseDN, g_strdup( bn ) );
858                                                                 }
859                                                         }
860                                                 }
861                                                 ldap_value_free( vals );
862                                         }
863                                 }
864                                 ldap_memfree( attribute );
865                                 if( ber != NULL ) {
866                                         ber_free( ber, 0 );
867                                 }
868                         }
869                         ldap_msgfree( result );
870                 }
871         }
872         ldap_unbind( ld );
873         return baseDN;
874 }
875
876 /*
877 * Attempt to discover the base DN for the server.
878 * Enter:  ldapServer Server to test.
879 * Return: List of Base DN's, or NULL if could not read. Base DN should
880 * be g_free() when done. Return code set in ldapServer.
881 */
882 GList *syldap_read_basedn( SyldapServer *ldapServer ) {
883         GList *baseDN = NULL;
884         LDAP *ld;
885         gint rc, i;
886         LDAPMessage *result, *e;
887         gchar *attribs[10];
888         BerElement *ber;
889         gchar *attribute;
890         gchar **vals;
891         struct timeval timeout;
892
893         ldapServer->retVal = MGU_BAD_ARGS;
894         if( ldapServer == NULL ) return baseDN;
895         if( ldapServer->hostName == NULL ) return baseDN;
896         if( ldapServer->port < 1 ) return baseDN;
897
898         /* Set timeout */
899         timeout.tv_usec = 0L;
900         if( ldapServer->timeOut > 0 ) {
901                 timeout.tv_sec = ldapServer->timeOut;
902         }
903         else {
904                 timeout.tv_sec = 30L;
905         }
906
907         /* Connect to server. */
908         if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) {
909                 ldapServer->retVal = MGU_LDAP_INIT;
910                 return baseDN;
911         }
912
913         /* Bind to the server, if required */
914         if( ldapServer->bindDN ) {
915                 if( *ldapServer->bindDN != '\0' ) {
916                         rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass );
917                         if( rc != LDAP_SUCCESS ) {
918                                 /* printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) ); */
919                                 ldap_unbind( ld );
920                                 ldapServer->retVal = MGU_LDAP_BIND;
921                                 return baseDN;
922                         }
923                 }
924         }
925
926         ldapServer->retVal = MGU_LDAP_SEARCH;
927
928         /* Test for LDAP version 3 */
929         attribs[0] = SYLDAP_V3_TEST_ATTR;
930         attribs[1] = NULL;
931         rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
932                        0, NULL, NULL, &timeout, 0, &result );
933         if( rc == LDAP_SUCCESS ) {
934                 /* Process entries */
935                 for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
936                         /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
937
938                         /* Process attributes */
939                         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
940                                         attribute = ldap_next_attribute( ld, e, ber ) ) {
941                                 if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
942                                         if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
943                                                 for( i = 0; vals[i] != NULL; i++ ) {
944                                                         /* printf( "\t%s: %s\n", attribute, vals[i] ); */
945                                                         baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
946                                                 }
947                                         }
948                                         ldap_value_free( vals );
949                                 }
950                         }
951                         ldap_memfree( attribute );
952                         if( ber != NULL ) {
953                                 ber_free( ber, 0 );
954                         }
955                 }
956                 ldap_msgfree( result );
957                 ldapServer->retVal = MGU_SUCCESS;
958         }
959         else if( rc == LDAP_TIMEOUT ) {
960                 ldapServer->retVal = MGU_LDAP_TIMEOUT;
961         }
962
963         if( baseDN == NULL ) {
964                 /* Test for LDAP version 2 */
965                 attribs[0] = NULL;
966                 rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
967                                0, NULL, NULL, &timeout, 0, &result );
968                 if( rc == LDAP_SUCCESS ) {
969                         /* Process entries */
970                         for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
971                                 /* if( baseDN ) break;                   */
972                                 /* printf( "DN: %s\n", ldap_get_dn( ld, e ) ); */
973
974                                 /* Process attributes */
975                                 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
976                                                attribute = ldap_next_attribute( ld, e, ber ) ) {
977                                         /* if( baseDN ) break;                   */
978                                         if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
979                                                 if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
980                                                         for( i = 0; vals[i] != NULL; i++ ) {
981                                                                 char *ch;
982                                                                 /* Strip the 'ldb:' from the front of the value */
983                                                                 ch = ( char * ) strchr( vals[i], ':' );
984                                                                 if( ch ) {
985                                                                         gchar *bn = g_strdup( ++ch );
986                                                                         g_strchomp( bn );
987                                                                         g_strchug( bn );
988                                                                         baseDN = g_list_append( baseDN, g_strdup( bn ) );
989                                                                 }
990                                                         }
991                                                 }
992                                                 ldap_value_free( vals );
993                                         }
994                                 }
995                                 ldap_memfree( attribute );
996                                 if( ber != NULL ) {
997                                         ber_free( ber, 0 );
998                                 }
999                         }
1000                         ldap_msgfree( result );
1001                         ldapServer->retVal = MGU_SUCCESS;
1002                 }
1003                 else if( rc == LDAP_TIMEOUT ) {
1004                         ldapServer->retVal = MGU_LDAP_TIMEOUT;
1005                 }
1006         }
1007         ldap_unbind( ld );
1008
1009         return baseDN;
1010 }
1011
1012 /*
1013 * Attempt to connect to the server.
1014 * Enter:
1015 *       host    Host name
1016 *       port    Port number
1017 * Return: TRUE if connected successfully.
1018 */
1019 gboolean syldap_test_connect_s( const gchar *host, const gint port ) {
1020         gboolean retVal = FALSE;
1021         LDAP *ld;
1022
1023         if( host == NULL ) return retVal;
1024         if( port < 1 ) return retVal;
1025         if( ( ld = ldap_open( host, port ) ) != NULL ) {
1026                 retVal = TRUE;
1027         }
1028         if( ld != NULL ) {
1029                 ldap_unbind( ld );
1030         }
1031         return retVal;
1032 }
1033
1034 /*
1035 * Attempt to connect to the server.
1036 * Enter:  ldapServer Server to test.
1037 * Return: TRUE if connected successfully. Return code set in ldapServer.
1038 */
1039 gboolean syldap_test_connect( SyldapServer *ldapServer ) {
1040         gboolean retVal = FALSE;
1041         LDAP *ld;
1042
1043         ldapServer->retVal = MGU_BAD_ARGS;
1044         if( ldapServer == NULL ) return retVal;
1045         if( ldapServer->hostName == NULL ) return retVal;
1046         if( ldapServer->port < 1 ) return retVal;
1047         ldapServer->retVal = MGU_LDAP_INIT;
1048         if( ( ld = ldap_open( ldapServer->hostName, ldapServer->port ) ) != NULL ) {
1049                 ldapServer->retVal = MGU_SUCCESS;
1050                 retVal = TRUE;
1051         }
1052         if( ld != NULL ) {
1053                 ldap_unbind( ld );
1054         }
1055         return retVal;
1056 }
1057
1058 #define LDAP_LINK_LIB_NAME_1 "libldap.so"
1059 #define LDAP_LINK_LIB_NAME_2 "liblber.so"
1060 #define LDAP_LINK_LIB_NAME_3 "libresolv.so"
1061 #define LDAP_LINK_LIB_NAME_4 "libpthread.so"
1062
1063 /*
1064 * Test whether LDAP libraries installed.
1065 * Return: TRUE if library available.
1066 */
1067 gboolean syldap_test_ldap_lib() {
1068         void *handle, *fun;
1069         
1070         /* Get library */
1071         handle = dlopen( LDAP_LINK_LIB_NAME_1, RTLD_LAZY );
1072         if( ! handle ) {
1073                 return FALSE;
1074         }
1075
1076         /* Test for symbols we need */
1077         fun = dlsym( handle, "ldap_init" );
1078         if( ! fun ) {
1079                 dlclose( handle );
1080                 return FALSE;
1081         }
1082         dlclose( handle ); handle = NULL; fun = NULL;
1083
1084         handle = dlopen( LDAP_LINK_LIB_NAME_2, RTLD_LAZY );
1085         if( ! handle ) {
1086                 return FALSE;
1087         }
1088         fun = dlsym( handle, "ber_init" );
1089         if( ! fun ) {
1090                 dlclose( handle );
1091                 return FALSE;
1092         }
1093         dlclose( handle ); handle = NULL; fun = NULL;
1094
1095         handle = dlopen( LDAP_LINK_LIB_NAME_3, RTLD_LAZY );
1096         if( ! handle ) {
1097                 return FALSE;
1098         }
1099         fun = dlsym( handle, "res_query" );
1100         if( ! fun ) {
1101                 dlclose( handle );
1102                 return FALSE;
1103         }
1104         dlclose( handle ); handle = NULL; fun = NULL;
1105
1106         handle = dlopen( LDAP_LINK_LIB_NAME_4, RTLD_LAZY );
1107         if( ! handle ) {
1108                 return FALSE;
1109         }
1110         fun = dlsym( handle, "pthread_create" );
1111         if( ! fun ) {
1112                 dlclose( handle );
1113                 return FALSE;
1114         }
1115         dlclose( handle ); handle = NULL; fun = NULL;
1116
1117         return TRUE;
1118 }
1119
1120 #endif  /* USE_LDAP */
1121
1122 /*
1123 * End of Source.
1124 */