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