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