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