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