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