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