670cbab4e2f7ac1d9d5b5aa2960a6a9aaed2074a
[claws.git] / src / ldapserver.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2009 Match Grun and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
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 <sys/time.h>
32 #include <string.h>
33
34 #include "mgutils.h"
35 #include "addritem.h"
36 #include "addrcache.h"
37 #include "ldapctrl.h"
38 #include "ldapquery.h"
39 #include "ldapserver.h"
40 #include "ldaputil.h"
41 #include "utils.h"
42 #include "adbookbase.h"
43
44 /**
45  * Create new LDAP server interface object with no control object.
46  * \return Initialized LDAP server object.
47  */
48 LdapServer *ldapsvr_create_noctl( void ) {
49         LdapServer *server;
50
51         server = g_new0( LdapServer, 1 );
52         server->type = ADBOOKTYPE_LDAP;
53         server->addressCache = addrcache_create();
54         server->retVal = MGU_SUCCESS;
55         server->control = NULL;
56         server->listQuery = NULL;
57         server->searchFlag = FALSE;
58         return server;
59 }
60
61 /**
62  * Create new LDAP server interface object.
63  * \return Initialized LDAP server object.
64  */
65 LdapServer *ldapsvr_create( void ) {
66         LdapServer *server;
67         server = ldapsvr_create_noctl();
68         server->control = ldapctl_create();
69         return server;
70 }
71
72 /**
73  * Return name of server.
74  * \param  server Server object.
75  * \return Name for server.
76  */
77 gchar *ldapsvr_get_name( LdapServer *server ) {
78         cm_return_val_if_fail( server != NULL, NULL );
79         return addrcache_get_name( server->addressCache );
80 }
81
82 /**
83  * Specify name to be used.
84  * \param server Server object.
85  * \param value      Name for server.
86  */
87 void ldapsvr_set_name( LdapServer* server, const gchar *value ) {
88         cm_return_if_fail( server != NULL );
89         addrcache_set_name( server->addressCache, value );
90         debug_print("setting name: %s\n", value?value:"null");
91 }
92
93 /**
94  * Refresh internal variables to force a file read.
95  * \param server Server object.
96  */
97 void ldapsvr_force_refresh( LdapServer *server ) {
98         cm_return_if_fail( server != NULL );
99         addrcache_refresh( server->addressCache );
100 }
101
102 /**
103  * Return status/error code.
104  * \param  server Server object.
105  * \return Status/error code.
106  */
107 gint ldapsvr_get_status( LdapServer *server ) {
108         cm_return_val_if_fail( server != NULL, -1 );
109         return server->retVal;
110 }
111
112 /**
113  * Return reference to root level folder.
114  * \param  server Server object.
115  * \return Root level folder.
116  */
117 ItemFolder *ldapsvr_get_root_folder( LdapServer *server ) {
118         cm_return_val_if_fail( server != NULL, NULL );
119         /*
120         g_print( "ldapsvr_get_root_folder/start\n" );
121         ldapsvr_print_data( server, stdout );
122         g_print( "ldapsvr_get_root_folder/done\n" );
123         */
124         return addrcache_get_root_folder( server->addressCache );
125 }
126
127 /**
128  * Test whether server data has been accessed.
129  * \param  server Server object.
130  * \return <i>TRUE</i> if data was accessed.
131  */
132 gboolean ldapsvr_get_accessed( LdapServer *server ) {
133         cm_return_val_if_fail( server != NULL, FALSE );
134         return server->addressCache->accessFlag;
135 }
136
137 /**
138  * Specify that server's data whas beed accessed.
139  * \param server Server object.
140  * \param value      Value for flag.
141  */
142 void ldapsvr_set_accessed( LdapServer *server, const gboolean value ) {
143         cm_return_if_fail( server != NULL );
144         server->addressCache->accessFlag = value;
145         debug_print("setting accessFlag: %d\n", value);
146 }
147
148 /**
149  * Test whether server data has been modified.
150  * \param  server Server object.
151  * \return <i>TRUE</i> if data was modified.
152  */
153 gboolean ldapsvr_get_modified( LdapServer *server ) {
154         cm_return_val_if_fail( server != NULL, FALSE );
155         return server->addressCache->modified;
156 }
157
158 /**
159  * Specify modify flag.
160  * \param server Server object.
161  * \param value      Value for flag.
162  */
163 void ldapsvr_set_modified( LdapServer *server, const gboolean value ) {
164         cm_return_if_fail( server != NULL );
165         server->addressCache->modified = value;
166         debug_print("setting modified: %d\n", value);
167 }
168
169 /**
170  * Test whether data was read from server.
171  * \param server Server object.
172  * \return <i>TRUE</i> if data was read.
173  */
174 gboolean ldapsvr_get_read_flag( LdapServer *server ) {
175         cm_return_val_if_fail( server != NULL, FALSE );
176         return server->addressCache->dataRead;
177 }
178
179 /**
180  * Test whether server is to be used for dynamic searches.
181  * \param server Server object.
182  * \return <i>TRUE</i> if server is used for dynamic searches.
183  */
184 gboolean ldapsvr_get_search_flag( LdapServer *server ) {
185         cm_return_val_if_fail( server != NULL, FALSE );
186         return server->searchFlag;
187 }
188
189 /**
190  * Specify that server is to be used for dynamic searches.
191  * \param server Server object.
192  * \param value      Name for server.
193  */
194 void ldapsvr_set_search_flag( LdapServer *server, const gboolean value ) {
195         cm_return_if_fail( server != NULL );
196         server->searchFlag = value;
197         debug_print("setting searchFlag: %d\n", value);
198 }
199
200 /**
201  * Specify the reference to control data that will be used for the query. The calling
202  * module should be responsible for creating and destroying this control object.
203  * \param server Server object.
204  * \param ctl    Control data.
205  */
206 void ldapsvr_set_control( LdapServer *server, LdapControl *ctl ) {
207         cm_return_if_fail( server != NULL );
208         addrcache_refresh( server->addressCache );
209         server->control = ctl;
210 }
211
212 /**
213  * Free all queries.
214  * \param server Server object.
215  */
216 void ldapsvr_free_all_query( LdapServer *server ) {
217         GList *node;    
218         cm_return_if_fail( server != NULL );
219
220         node = server->listQuery;
221         while( node ) {
222                 LdapQuery *qry = node->data;
223                 ldapqry_free( qry );
224                 node->data = NULL;
225                 node = g_list_next( node );
226         }
227         g_list_free( server->listQuery );
228         server->listQuery = NULL;
229 }
230
231 /**
232  * Add query to server.
233  * \param server Server object.
234  * \param qry    Query object.
235  */
236 void ldapsvr_add_query( LdapServer *server, LdapQuery *qry ) {
237         cm_return_if_fail( server != NULL );
238         cm_return_if_fail( qry != NULL );
239
240         server->listQuery = g_list_append( server->listQuery, qry );
241         qry->server = server;
242 }
243
244 /**
245  * Free up LDAP server interface object by releasing internal memory.
246  * \param server Server object.
247  */
248 void ldapsvr_free( LdapServer *server ) {
249         cm_return_if_fail( server != NULL );
250
251         /* Stop and cancel any queries that may be active */
252         ldapsvr_stop_all_query( server );
253         ldapsvr_cancel_all_query( server );
254
255         /* Clear cache */
256         addrcache_clear( server->addressCache );
257         addrcache_free( server->addressCache );
258
259         /* Free LDAP control block */
260         ldapctl_free( server->control );
261         server->control = NULL;
262
263         /* Free all queries */
264         ldapsvr_free_all_query( server );
265
266         /* Clear pointers */
267         server->type = ADBOOKTYPE_NONE;
268         server->addressCache = NULL;
269         server->retVal = MGU_SUCCESS;
270         server->listQuery = NULL;
271         server->searchFlag = FALSE;
272
273         /* Now release LDAP object */
274         g_free( server );
275 }
276
277 /**
278  * Display object to specified stream.
279  * \param server Server object.
280  * \param stream     Output stream.
281  */
282 void ldapsvr_print_data( LdapServer *server, FILE *stream ) {
283         GList *node;
284         gint  i;
285
286         cm_return_if_fail( server != NULL );
287
288         fprintf( stream, "LdapServer:\n" );
289         fprintf( stream, "  ret val: %d\n", server->retVal );
290         fprintf( stream, "srch flag: %s\n",
291                         server->searchFlag ? "yes" : "no" );
292         if( server->control ) {
293                 ldapctl_print( server->control, stream );
294         }
295         else {
296                 fprintf( stream, "  control: NULL\n" );
297         }
298         addrcache_print( server->addressCache, stream );
299         addritem_print_item_folder( server->addressCache->rootFolder, stream );
300
301         /* Dump queries */
302         i = 1;
303         node = server->listQuery;
304         while( node ) {
305                 LdapQuery *qry = node->data;
306                 fprintf( stream, "    query: %2d : %s\n", i, ADDRQUERY_NAME(qry) );
307                 i++;
308                 node = g_list_next( node );
309         }
310 }
311
312 /**
313  * Return link list of persons.
314  * \param server Server object.
315  * \return List of persons.
316  */
317 GList *ldapsvr_get_list_person( LdapServer *server ) {
318         cm_return_val_if_fail( server != NULL, NULL );
319         return addrcache_get_list_person( server->addressCache );
320 }
321
322 /**
323  * Return link list of folders. There are no "real" folders that are returned
324  * from the server.
325  * \param  server Server object.
326  * \return List of folders.
327  */
328 GList *ldapsvr_get_list_folder( LdapServer *server ) {
329         cm_return_val_if_fail( server != NULL, NULL );
330         /* return addrcache_get_list_folder( server->addressCache ); */
331         return NULL;
332 }
333
334 /**
335  * Execute specified query.
336  * \param server LDAP server.
337  * \param qry    LDAP query.
338  */
339 void ldapsvr_execute_query( LdapServer *server, LdapQuery *qry ) {
340         LdapControl *ctlCopy;
341
342         cm_return_if_fail( server != NULL );
343         cm_return_if_fail( qry != NULL );
344
345         /* Copy server's control data to the query */
346         ctlCopy = ldapctl_create();
347         ldapctl_copy( server->control, ctlCopy );
348         ldapqry_set_control( qry, ctlCopy );
349         ldapqry_initialize();
350
351         /* Perform query */     
352         debug_print("ldapsvr_execute_query::checking query...\n");
353         if( ldapqry_check_search( qry ) ) {
354                 debug_print("ldapsvr_execute_query::reading with thread...\n");
355                 ldapqry_read_data_th( qry );
356                 if(qry->server->retVal == LDAPRC_SUCCESS) {
357                         debug_print("ldapsvr_execute_query::SUCCESS with thread...\n");
358                 }
359         }
360         debug_print("ldapsvr_execute_query... terminated\n");
361 }
362
363 /**
364  * Stop all queries for specified ID.
365  * \param server Server object.
366  * \param queryID    Query ID to stop.
367  */
368 void ldapsvr_stop_query_id( LdapServer *server, const gint queryID ) {
369         GList *node;    
370         cm_return_if_fail( server != NULL );
371
372         node = server->listQuery;
373         while( node ) {
374                 LdapQuery *qry = node->data;
375                 if( ADDRQUERY_ID(qry) == queryID ) {
376                         /* Notify thread to stop */
377                         ldapqry_set_stop_flag( qry, TRUE );
378                 }
379                 node = g_list_next( node );
380         }
381 }
382
383 /**
384  * Stop all queries by notifying each thread to stop.
385  * \param server Server object.
386  */
387 void ldapsvr_stop_all_query( LdapServer *server ) {
388         GList *node;    
389         cm_return_if_fail( server != NULL );
390
391         node = server->listQuery;
392         while( node ) {
393                 LdapQuery *qry = node->data;
394                 ldapqry_set_stop_flag( qry, TRUE );
395                 node = g_list_next( node );
396         }
397 }
398
399 /**
400  * Cancel all query threads for server.
401  * \param server Server object.
402  */
403 void ldapsvr_cancel_all_query( LdapServer *server ) {
404         GList *node;    
405         cm_return_if_fail( server != NULL );
406
407         node = server->listQuery;
408         while( node ) {
409                 LdapQuery *qry = node->data;
410                 /* Notify thread to stop */
411                 ldapqry_set_stop_flag( qry, TRUE );
412                 /* Now cancel thread */
413                 ldapqry_cancel( qry );
414                 node = g_list_next( node );
415         }
416 }
417
418 /**
419  * Search most recent query for specified search term. The most recent
420  * completed query is returned. If no completed query is found, the most recent
421  * incomplete is returned.
422  * \param server LdapServer.
423  * \param searchTerm Search term to locate.
424  * \return Query object, or <i>NULL</i> if none found.
425  */
426 static LdapQuery *ldapsvr_locate_query(
427         const LdapServer *server, const gchar *searchTerm )
428 {
429         LdapQuery *incomplete = NULL;
430         GList *node;    
431         cm_return_val_if_fail( server != NULL, NULL );
432
433         node = server->listQuery;
434         node = g_list_last( node );
435         /* Search backwards for query */
436         while( node ) {
437                 LdapQuery *qry = node->data;
438                 if( g_utf8_collate( ADDRQUERY_SEARCHVALUE(qry), searchTerm ) == 0 ) {
439                         if( qry->agedFlag ) continue;
440                         if( qry->completed ) {
441                                 /* Found */
442                                 return qry;
443                         }
444                         if( ! incomplete ) {
445                                 incomplete = qry;
446                         }
447                 }
448                 node = g_list_previous( node );
449         }
450         return incomplete;
451 }
452
453 /**
454  * Retire aged queries. Only the following queries are retired:
455  *
456  * a) Dynamic queries.
457  * b) Explicit searches that have a hidden folders.
458  * c) Locate searches that have a hidden folder.
459  *
460  * \param server LdapServer.
461  */
462 void ldapsvr_retire_query( LdapServer *server ) {
463         GList *node;
464         GList *listDelete;
465         GList *listQuery;
466         gint maxAge;
467         LdapControl *ctl;
468         ItemFolder *folder;
469
470         debug_print("ldapsvr_retire_query\n");
471         cm_return_if_fail( server != NULL );
472         ctl = server->control;
473         maxAge = ctl->maxQueryAge;
474
475         /* Identify queries to age and move to deletion list */
476         listDelete = NULL;
477         node = server->listQuery;
478         while( node ) {
479                 LdapQuery *qry = node->data;
480
481                 node = g_list_next( node );
482                 folder = ADDRQUERY_FOLDER(qry);
483                 if( folder == NULL ) continue;
484                 if( ! folder->isHidden ) {
485                         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_EXPLICIT ) continue;
486                         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) continue;
487                 }
488
489                 ldapqry_age( qry, maxAge );
490                 if( qry->agedFlag ) {
491                         /* Delete folder associated with query */
492                         debug_print("deleting folder... ::%s::\n",
493                                         ADDRQUERY_NAME(qry)?ADDRQUERY_NAME(qry):"null");
494                         ldapqry_delete_folder( qry );
495                         listDelete = g_list_append( listDelete, qry );
496                 }
497         }
498
499         /* Delete queries */
500         listQuery = server->listQuery;
501         node = listDelete;
502         while( node ) {
503                 LdapQuery *qry = node->data;
504
505                 listQuery = g_list_remove( listQuery, qry );
506                 ldapqry_free( qry );
507                 node->data = NULL;
508                 node = g_list_next( node );
509         }
510         server->listQuery = listQuery;
511
512         /* Free up deletion list */
513         g_list_free( listDelete );
514 }
515
516 /**
517  * Return results of a previous query by executing callback for each address
518  * contained in specified folder.
519  * 
520  * \param folder  Address book folder to process.
521  * \param req Address query request object.
522  */
523 static void ldapsvr_previous_query(
524         const ItemFolder *folder, const QueryRequest *req, AddrQueryObject *aqo )
525 {
526         AddrSearchCallbackEntry *callBack;
527         GList *listEMail;
528         GList *node;
529         GList *nodeEM;
530         gpointer sender;
531
532         sender = aqo;
533         callBack = ( AddrSearchCallbackEntry * ) req->callBackEntry;
534         if( callBack ) {
535                 listEMail = NULL;
536                 node = folder->listPerson;
537                 while( node ) {
538                         AddrItemObject *aio = node->data;
539                         if( aio &&  aio->type == ITEMTYPE_PERSON ) {
540                                 ItemPerson *person = node->data;
541                                 nodeEM = person->listEMail;
542                                 while( nodeEM ) {
543                                         ItemEMail *email = nodeEM->data;
544
545                                         nodeEM = g_list_next( nodeEM );
546                                         listEMail = g_list_append( listEMail, email );
547                                 }
548                         }
549                         node = g_list_next( node );
550                 }
551                 ( callBack ) ( sender, req->queryID, listEMail, NULL );
552                 /* // g_list_free( listEMail ); */
553         }
554 }
555
556 /**
557  * Reuse search results from a previous LDAP query. If there is a query that
558  * has the same search term as specified in the query request, then the query
559  * will be reused.
560  *
561  * \param server  LDAP server object.
562  * \param req Address query object.
563  * \return <i>TRUE</i> if previous query was used.
564  */
565 gboolean ldapsvr_reuse_previous( const LdapServer *server, const QueryRequest *req ) {
566         LdapQuery *qry;
567         gchar *searchTerm;
568         ItemFolder *folder;
569
570         cm_return_val_if_fail( server != NULL, FALSE );
571         cm_return_val_if_fail( req != NULL, FALSE );
572
573         searchTerm = req->searchTerm;
574
575         /* Test whether any queries for the same term exist */
576         qry = ldapsvr_locate_query( server, searchTerm );
577         if( qry ) {
578                 /* Touch query to ensure it hangs around for a bit longer */
579                 ldapqry_touch( qry );
580                 folder = ADDRQUERY_FOLDER(qry);
581                 if( folder ) {
582                         ldapsvr_previous_query( folder, req, ADDRQUERY_OBJECT(qry) );
583                         return TRUE;
584                 }
585         }
586         return FALSE;
587 }
588
589 /**
590  * Construct a new LdapQuery object that will be used to perform an dynamic
591  * search request.
592  *
593  * \param server LdapServer.
594  * \param req    Query request.
595  * \return LdapQuery object, or <i>NULL</i> if none created.
596  */
597 LdapQuery *ldapsvr_new_dynamic_search( LdapServer *server, QueryRequest *req )
598 {
599         LdapQuery *qry;
600         gchar *name;
601         gchar *searchTerm;
602         ItemFolder *folder;
603
604         cm_return_val_if_fail( server != NULL, NULL );
605         cm_return_val_if_fail( req != NULL, NULL );
606
607         /* Retire any aged queries */
608         /* // ldapsvr_retire_query( server ); */
609
610         /* Name of folder and query */
611         searchTerm = req->searchTerm;
612         name = g_strdup_printf( "Search '%s'", searchTerm );
613
614         /* Create a folder for the search results */
615         folder = addrcache_add_new_folder( server->addressCache, NULL );
616         addritem_folder_set_name( folder, name );
617         addritem_folder_set_remarks( folder, "" );
618
619         /* Construct a query */
620         qry = ldapqry_create();
621         ldapqry_set_query_id( qry, req->queryID );
622         ldapqry_set_search_value( qry, searchTerm );
623         ldapqry_set_search_type( qry, ADDRSEARCH_DYNAMIC );
624         ldapqry_set_callback_entry( qry, req->callBackEntry );
625         ldapqry_set_callback_end( qry, req->callBackEnd );
626
627         /* Specify folder type and back reference */
628         ADDRQUERY_FOLDER(qry) = folder;
629         folder->folderType = ADDRFOLDER_QUERY_RESULTS;
630         folder->folderData = ( gpointer ) qry;
631         folder->isHidden = TRUE;
632
633         /* Name the query */
634         ldapqry_set_name( qry, name );
635         g_free( name );
636
637         /* Add query to request */
638         qryreq_add_query( req, ADDRQUERY_OBJECT(qry) );
639
640         /* Now start the search */
641         ldapsvr_add_query( server, qry );
642
643         return qry;     
644 }
645
646 /**
647  * Construct a new LdapQuery object that will be used to perform an explicit
648  * search request.
649  *
650  * \param server LdapServer.
651  * \param req    Query request.
652  * \param folder Folder that will be used to contain search results.
653  * \return LdapQuery object, or <i>NULL</i> if none created.
654  */
655 LdapQuery *ldapsvr_new_explicit_search(
656                 LdapServer *server, QueryRequest *req, ItemFolder *folder )
657 {
658         LdapQuery *qry;
659         gchar *searchTerm;
660         gchar *name;
661
662         cm_return_val_if_fail( server != NULL, NULL );
663         cm_return_val_if_fail( req != NULL, NULL );
664         cm_return_val_if_fail( folder != NULL, NULL );
665
666         /* Retire any aged queries */
667         /* // ldapsvr_retire_query( server ); */
668
669         /* Name the query */
670         searchTerm = req->searchTerm;
671         name = g_strdup_printf( "Explicit search for '%s'", searchTerm );
672
673         /* Construct a query */
674         qry = ldapqry_create();
675         ldapqry_set_query_id( qry, req->queryID );
676         ldapqry_set_name( qry, name );
677         ldapqry_set_search_value( qry, searchTerm );
678         ldapqry_set_search_type( qry, ADDRSEARCH_EXPLICIT );
679         ldapqry_set_callback_end( qry, req->callBackEnd );
680         ldapqry_set_callback_entry( qry, req->callBackEntry );
681
682         /* Specify folder type and back reference */
683         ADDRQUERY_FOLDER(qry) = folder;
684         folder->folderType = ADDRFOLDER_QUERY_RESULTS;
685         folder->folderData = ( gpointer ) qry;
686
687         /* Setup server */
688         ldapsvr_add_query( server, qry );
689
690         /* Set up query request */
691         qryreq_add_query( req, ADDRQUERY_OBJECT(qry) );
692
693         g_free( name );
694
695         return qry;
696 }
697
698 gint ldapsvr_read_data( LdapServer *server )
699 {
700         gchar *name;
701
702         cm_return_val_if_fail( server != NULL, -1 );
703
704         name = addrcache_get_name(server->addressCache);
705         debug_print("...addrbook_read_data :%s:\n", name?name:"null");
706         
707         addrcache_clear(server->addressCache);
708         ldapsvr_free_all_query( server );
709         server->listQuery = NULL;
710         server->addressCache->modified = FALSE;
711         server->addressCache->accessFlag = FALSE;
712         server->addressCache->dataRead = TRUE;
713         addrcache_set_dirty(server->addressCache, FALSE);
714         return 0;
715 }
716
717 void ldapsrv_set_options (gint secs, LDAP *ld)
718 {
719         static struct timeval timeout;
720         int rc;
721         int i;
722         timeout.tv_sec = secs;
723         timeout.tv_usec = 0;
724 #ifdef G_OS_UNIX
725         i = LDAP_OPT_X_TLS_ALLOW;
726         rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
727         debug_print("cert %s\n", ldap_err2string(rc));
728         /* can crash old libldaps... */
729         rc = ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
730         debug_print("tm %s\n", ldap_err2string(rc));
731 #else
732         rc = ldap_set_option(NULL, LDAP_OPT_TIMELIMIT, &secs);
733         debug_print("tm %s\n", ldap_err2string(rc));
734 #endif
735 }
736
737 /**
738  * Connect to LDAP server.
739  * \param  ctl Control object to process.
740  * \return LDAP Resource to LDAP.
741  */
742 LDAP *ldapsvr_connect(LdapControl *ctl) {
743         LDAP *ld = NULL;
744         gint rc;
745         gint version;
746         gchar *uri = NULL;
747         gchar *pwd;
748
749         cm_return_val_if_fail(ctl != NULL, NULL);
750
751         ldapsrv_set_options (ctl->timeOut, NULL);
752         uri = g_strdup_printf("ldap%s://%s:%d",
753                                 ctl->enableSSL?"s":"",
754                                 ctl->hostName, ctl->port);
755 #ifdef G_OS_UNIX
756         ldap_initialize(&ld, uri);
757 #else
758         ld = ldap_sslinit(ctl->hostName, ctl->port, ctl->enableSSL);
759 #endif
760         g_free(uri);
761
762         if (ld == NULL)
763                 return NULL;
764
765
766         debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
767
768 #ifdef USE_LDAP_TLS
769         /* Handle TLS */
770         version = LDAP_VERSION3;
771         rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
772         if (rc == LDAP_OPT_SUCCESS) {
773                 ctl->version = LDAP_VERSION3;
774         }
775
776         if (ctl->version == LDAP_VERSION3) {
777                 if (ctl->enableTLS && !ctl->enableSSL) {
778                         rc = ldap_start_tls_s(ld, NULL, NULL);
779                         
780                         if (rc != LDAP_SUCCESS) {
781                                 g_printerr("LDAP Error(tls): ldap_simple_bind_s: %s\n",
782                                         ldap_err2string(rc));
783                                 return NULL;
784                         }
785                 }
786         }
787 #endif
788
789         /* Bind to the server, if required */
790         if (ctl->bindDN) {
791                 if (* ctl->bindDN != '\0') {
792                         pwd = ldapctl_get_bind_password(ctl);
793                         rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, pwd);
794                         if (rc != LDAP_SUCCESS) {
795                                 g_printerr("bindDN: %s, bindPass xxx\n", ctl->bindDN);
796                                 g_printerr("LDAP Error(bind): ldap_simple_bind_s: %s\n",
797                                         ldap_err2string(rc));
798                                 g_free(pwd);
799                                 return NULL;
800                         }
801                         g_free(pwd);
802                 }
803         }
804         return ld;
805 }
806
807 /**
808  * Disconnect to LDAP server.
809  * \param ld Resource to LDAP.
810  */
811 void ldapsvr_disconnect(LDAP *ld) {
812         /* Disconnect */
813         cm_return_if_fail(ld != NULL);
814         ldap_unbind_ext(ld, NULL, NULL);
815 }
816
817 #endif  /* USE_LDAP */
818
819 /*
820  * End of Source.
821  */
822