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