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