b4f4e4256826ff71e4bc9f883e1d14d64c16e708
[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
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         g_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         g_return_if_fail( server != NULL );
92         addrcache_set_name( server->addressCache, value );
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         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         g_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         g_return_val_if_fail( server != NULL, NULL );
120         /*
121         printf( "ldapsvr_get_root_folder/start\n" );
122         ldapsvr_print_data( server, stdout );
123         printf( "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         g_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         g_return_if_fail( server != NULL );
145         server->addressCache->accessFlag = 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         g_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         g_return_if_fail( server != NULL );
165         server->addressCache->modified = value;
166 }
167
168 /**
169  * Test whether data was read from server.
170  * \param server Server object.
171  * \return <i>TRUE</i> if data was read.
172  */
173 gboolean ldapsvr_get_read_flag( LdapServer *server ) {
174         g_return_val_if_fail( server != NULL, FALSE );
175         return server->addressCache->dataRead;
176 }
177
178 /**
179  * Test whether server is to be used for dynamic searches.
180  * \param server Server object.
181  * \return <i>TRUE</i> if server is used for dynamic searches.
182  */
183 gboolean ldapsvr_get_search_flag( LdapServer *server ) {
184         g_return_val_if_fail( server != NULL, FALSE );
185         return server->searchFlag;
186 }
187
188 /**
189  * Specify that server is to be used for dynamic searches.
190  * \param server Server object.
191  * \param value      Name for server.
192  */
193 void ldapsvr_set_search_flag( LdapServer *server, const gboolean value ) {
194         g_return_if_fail( server != NULL );
195         server->searchFlag = value;
196 }
197
198 /**
199  * Specify the reference to control data that will be used for the query. The calling
200  * module should be responsible for creating and destroying this control object.
201  * \param server Server object.
202  * \param ctl    Control data.
203  */
204 void ldapsvr_set_control( LdapServer *server, LdapControl *ctl ) {
205         g_return_if_fail( server != NULL );
206         addrcache_refresh( server->addressCache );
207         server->control = ctl;
208 }
209
210 /**
211  * Free all queries.
212  * \param server Server object.
213  */
214 void ldapsvr_free_all_query( LdapServer *server ) {
215         GList *node;    
216         g_return_if_fail( server != NULL );
217
218         node = server->listQuery;
219         while( node ) {
220                 LdapQuery *qry = node->data;
221                 ldapqry_free( qry );
222                 node->data = NULL;
223                 node = g_list_next( node );
224         }
225         g_list_free( server->listQuery );
226         server->listQuery = NULL;
227 }
228
229 /**
230  * Add query to server.
231  * \param server Server object.
232  * \param qry    Query object.
233  */
234 void ldapsvr_add_query( LdapServer *server, LdapQuery *qry ) {
235         g_return_if_fail( server != NULL );
236         g_return_if_fail( qry != NULL );
237
238         server->listQuery = g_list_append( server->listQuery, qry );
239         qry->server = server;
240 }
241
242 /**
243  * Free up LDAP server interface object by releasing internal memory.
244  * \param server Server object.
245  */
246 void ldapsvr_free( LdapServer *server ) {
247         g_return_if_fail( server != NULL );
248
249         /* Stop and cancel any queries that may be active */
250         ldapsvr_stop_all_query( server );
251         ldapsvr_cancel_all_query( server );
252
253         /* Clear cache */
254         addrcache_clear( server->addressCache );
255         addrcache_free( server->addressCache );
256
257         /* Free LDAP control block */
258         ldapctl_free( server->control );
259         server->control = NULL;
260
261         /* Free all queries */
262         ldapsvr_free_all_query( server );
263
264         /* Clear pointers */
265         server->type = ADBOOKTYPE_NONE;
266         server->addressCache = NULL;
267         server->retVal = MGU_SUCCESS;
268         server->listQuery = NULL;
269         server->searchFlag = FALSE;
270
271         /* Now release LDAP object */
272         g_free( server );
273 }
274
275 /**
276  * Display object to specified stream.
277  * \param server Server object.
278  * \param stream     Output stream.
279  */
280 void ldapsvr_print_data( LdapServer *server, FILE *stream ) {
281         GList *node;
282         gint  i;
283
284         g_return_if_fail( server != NULL );
285
286         fprintf( stream, "LdapServer:\n" );
287         fprintf( stream, "  ret val: %d\n", server->retVal );
288         fprintf( stream, "srch flag: %s\n",
289                         server->searchFlag ? "yes" : "no" );
290         if( server->control ) {
291                 ldapctl_print( server->control, stream );
292         }
293         else {
294                 fprintf( stream, "  control: NULL\n" );
295         }
296         addrcache_print( server->addressCache, stream );
297         addritem_print_item_folder( server->addressCache->rootFolder, stream );
298
299         /* Dump queries */
300         i = 1;
301         node = server->listQuery;
302         while( node ) {
303                 LdapQuery *qry = node->data;
304                 fprintf( stream, "    query: %2d : %s\n", i, ADDRQUERY_NAME(qry) );
305                 i++;
306                 node = g_list_next( node );
307         }
308 }
309
310 /**
311  * Return link list of persons.
312  * \param server Server object.
313  * \return List of persons.
314  */
315 GList *ldapsvr_get_list_person( LdapServer *server ) {
316         g_return_val_if_fail( server != NULL, NULL );
317         return addrcache_get_list_person( server->addressCache );
318 }
319
320 /**
321  * Return link list of folders. There are no "real" folders that are returned
322  * from the server.
323  * \param  server Server object.
324  * \return List of folders.
325  */
326 GList *ldapsvr_get_list_folder( LdapServer *server ) {
327         g_return_val_if_fail( server != NULL, NULL );
328         /* return addrcache_get_list_folder( server->addressCache ); */
329         return NULL;
330 }
331
332 /**
333  * Execute specified query.
334  * \param server LDAP server.
335  * \param qry    LDAP query.
336  */
337 void ldapsvr_execute_query( LdapServer *server, LdapQuery *qry ) {
338         LdapControl *ctlCopy;
339
340         g_return_if_fail( server != NULL );
341         g_return_if_fail( qry != NULL );
342
343         /* Copy server's control data to the query */
344         ctlCopy = ldapctl_create();
345         ldapctl_copy( server->control, ctlCopy );
346         ldapqry_set_control( qry, ctlCopy );
347         ldapqry_initialize();
348
349         /* Perform query */     
350         /* printf( "ldapsvr_execute_query::checking query...\n" ); */
351         if( ldapqry_check_search( qry ) ) {
352                 /* printf( "ldapsvr_execute_query::reading with thread...\n" ); */
353                 ldapqry_read_data_th( qry );
354                 /*
355                 if( qry->retVal == LDAPRC_SUCCESS ) {
356                         printf( "ldapsvr_execute_query::SUCCESS with thread...\n" );
357                 }
358                 */
359         }
360         /* printf( "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         g_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         g_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         g_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         g_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         /* printf( "ldapsvr_retire_query\n" ); */
471         g_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                         /*
493                         printf( "deleting folder... ::%s::\n", ADDRQUERY_NAME(qry) );
494                         */
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         g_return_val_if_fail( server != NULL, FALSE );
572         g_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         g_return_val_if_fail( server != NULL, NULL );
606         g_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         g_return_val_if_fail( server != NULL, NULL );
664         g_return_val_if_fail( req != NULL, NULL );
665         g_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         g_return_val_if_fail( server != NULL, -1 );
702
703         //printf( "...addrbook_read_data :%s:\n", addrcache_get_name( server->addressCache ) );
704         
705         addrcache_clear(server->addressCache);
706         server->addressCache->modified = FALSE;
707         server->addressCache->accessFlag = FALSE;
708         server->addressCache->dataRead = TRUE;
709         addrcache_set_dirty(server->addressCache, FALSE);
710         return 0;
711 }
712
713 #endif  /* USE_LDAP */
714
715 /*
716  * End of Source.
717  */
718