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