d11454d90bf10b183c8eb62a594e10e8b57fb2a3
[claws.git] / src / ldapctrl.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003 Match Grun
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Functions for LDAP control data.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #ifdef USE_LDAP
29
30 #include <glib.h>
31 #include <sys/time.h>
32 #include <string.h>
33
34 #include "ldapctrl.h"
35 #include "mgutils.h"
36
37 /**
38  * Create new LDAP control block object.
39  * \return Initialized control object.
40  */
41 LdapControl *ldapctl_create( void ) {
42         LdapControl *ctl;
43
44         ctl = g_new0( LdapControl, 1 );
45         ctl->hostName = NULL;
46         ctl->port = LDAPCTL_DFL_PORT;
47         ctl->baseDN = NULL;
48         ctl->bindDN = NULL;
49         ctl->bindPass = NULL;
50         ctl->listCriteria = NULL;
51         ctl->attribEMail = g_strdup( LDAPCTL_ATTR_EMAIL );
52         ctl->attribCName = g_strdup( LDAPCTL_ATTR_COMMONNAME );
53         ctl->attribFName = g_strdup( LDAPCTL_ATTR_GIVENNAME );
54         ctl->attribLName = g_strdup( LDAPCTL_ATTR_SURNAME );
55         ctl->maxEntries = LDAPCTL_MAX_ENTRIES;
56         ctl->timeOut = LDAPCTL_DFL_TIMEOUT;
57         ctl->maxQueryAge = LDAPCTL_DFL_QUERY_AGE;
58
59         /* Mutex to protect control block */
60         ctl->mutexCtl = g_malloc0( sizeof( pthread_mutex_t ) );
61         pthread_mutex_init( ctl->mutexCtl, NULL );
62
63         return ctl;
64 }
65
66 /**
67  * Specify hostname to be used.
68  * \param ctl   Control object to process.
69  * \param value Host name.
70  */
71 void ldapctl_set_host( LdapControl* ctl, const gchar *value ) {
72         ctl->hostName = mgu_replace_string( ctl->hostName, value );
73         g_strstrip( ctl->hostName );
74 }
75
76 /**
77  * Specify port to be used.
78  * \param ctl  Control object to process.
79  * \param value Port.
80  */
81 void ldapctl_set_port( LdapControl* ctl, const gint value ) {
82         if( value > 0 ) {
83                 ctl->port = value;
84         }
85         else {
86                 ctl->port = LDAPCTL_DFL_PORT;
87         }
88 }
89
90 /**
91  * Specify base DN to be used.
92  * \param ctl  Control object to process.
93  * \param value Base DN.
94  */
95 void ldapctl_set_base_dn( LdapControl* ctl, const gchar *value ) {
96         ctl->baseDN = mgu_replace_string( ctl->baseDN, value );
97         g_strstrip( ctl->baseDN );
98 }
99
100 /**
101  * Specify bind DN to be used.
102  * \param ctl  Control object to process.
103  * \param value Bind DN.
104  */
105 void ldapctl_set_bind_dn( LdapControl* ctl, const gchar *value ) {
106         ctl->bindDN = mgu_replace_string( ctl->bindDN, value );
107         g_strstrip( ctl->bindDN );
108 }
109
110 /**
111  * Specify bind password to be used.
112  * \param ctl  Control object to process.
113  * \param value Password.
114  */
115 void ldapctl_set_bind_password( LdapControl* ctl, const gchar *value ) {
116         ctl->bindPass = mgu_replace_string( ctl->bindPass, value );
117         g_strstrip( ctl->bindPass );
118 }
119
120 /**
121  * Specify maximum number of entries to retrieve.
122  * \param ctl  Control object to process.
123  * \param value Maximum entries.
124  */
125 void ldapctl_set_max_entries( LdapControl* ctl, const gint value ) {
126         if( value > 0 ) {
127                 ctl->maxEntries = value;
128         }
129         else {
130                 ctl->maxEntries = LDAPCTL_MAX_ENTRIES;
131         }
132 }
133
134 /**
135  * Specify timeout value for LDAP operation (in seconds).
136  * \param ctl  Control object to process.
137  * \param value Timeout.
138  */
139 void ldapctl_set_timeout( LdapControl* ctl, const gint value ) {
140         if( value > 0 ) {
141                 ctl->timeOut = value;
142         }
143         else {
144                 ctl->timeOut = LDAPCTL_DFL_TIMEOUT;
145         }
146 }
147
148 /**
149  * Specify maximum age of query (in seconds) before query is retired.
150  * \param ctl  Control object to process.
151  * \param value Maximum age.
152  */
153 void ldapctl_set_max_query_age( LdapControl* ctl, const gint value ) {
154         if( value > LDAPCTL_MAX_QUERY_AGE ) {
155                 ctl->maxQueryAge = LDAPCTL_MAX_QUERY_AGE;
156         }
157         else if( value < 1 ) {
158                 ctl->maxQueryAge = LDAPCTL_DFL_QUERY_AGE;
159         }
160         else {
161                 ctl->maxQueryAge = value;
162         }
163 }
164
165 /**
166  * Specify search criteria list to be used.
167  * \param ctl   Control data object.
168  * \param value Linked list of LDAP attribute names to use for search.
169  */
170 void ldapctl_set_criteria_list( LdapControl* ctl, GList *value ) {
171         g_return_if_fail( ctl != NULL );
172         mgu_free_dlist( ctl->listCriteria );
173         ctl->listCriteria = value;
174 }
175
176 /**
177  * Return search criteria list.
178  * \param  ctl  Control data object.
179  * \return Linked list of character strings containing LDAP attribute names to
180  *         use for a search. This should not be modified directly. Use the
181  *         <code>ldapctl_set_criteria_list()</code>,
182  *         <code>ldapctl_criteria_list_clear()</code> and
183  *         <code>ldapctl_criteria_list_add()</code> functions for this purpose.
184  */
185 GList *ldapctl_get_criteria_list( const LdapControl* ctl ) {
186         g_return_val_if_fail( ctl != NULL, NULL );
187         return ctl->listCriteria;
188 }
189
190 /**
191  * Clear list of LDAP search attributes.
192  * \param  ctl  Control data object.
193  */
194 void ldapctl_criteria_list_clear( LdapControl *ctl ) {
195         g_return_if_fail( ctl != NULL );
196         mgu_free_dlist( ctl->listCriteria );
197         ctl->listCriteria = NULL;
198 }
199
200 /**
201  * Add LDAP attribute to criteria list.
202  * \param ctl  Control object to process.
203  * \param attr Attribute name to append. If not NULL and unique, a copy will
204  *             be appended to the list.
205  */
206 void ldapctl_criteria_list_add( LdapControl *ctl, gchar *attr ) {
207         g_return_if_fail( ctl != NULL );
208         if( attr != NULL ) {
209                 if( mgu_list_test_unq_nc( ctl->listCriteria, attr ) ) {
210                         ctl->listCriteria = g_list_append(
211                                 ctl->listCriteria, g_strdup( attr ) );
212                 }
213         }
214 }
215
216 /**
217  * Build criteria list using default attributes.
218  * \param ctl  Control object to process.
219  */
220 void ldapctl_default_attributes( LdapControl *ctl ) {
221         g_return_if_fail( ctl != NULL );
222
223         ldapctl_criteria_list_clear( ctl );
224         ldapctl_criteria_list_add( ctl, LDAPCTL_ATTR_COMMONNAME );
225         ldapctl_criteria_list_add( ctl, LDAPCTL_ATTR_GIVENNAME );
226         ldapctl_criteria_list_add( ctl, LDAPCTL_ATTR_SURNAME );
227         ldapctl_criteria_list_add( ctl, LDAPCTL_ATTR_EMAIL );
228 }
229
230 /**
231  * Clear LDAP server member variables.
232  * \param ctl Control object to clear.
233  */
234 void ldapctl_clear( LdapControl *ctl ) {
235         g_return_if_fail( ctl != NULL );
236
237         /* Free internal stuff */
238         g_free( ctl->hostName );
239         g_free( ctl->baseDN );
240         g_free( ctl->bindDN );
241         g_free( ctl->bindPass );
242         g_free( ctl->attribEMail );
243         g_free( ctl->attribCName );
244         g_free( ctl->attribFName );
245         g_free( ctl->attribLName );
246
247         ldapctl_criteria_list_clear( ctl );
248
249         /* Clear pointers */
250         ctl->hostName = NULL;
251         ctl->port = 0;
252         ctl->baseDN = NULL;
253         ctl->bindDN = NULL;
254         ctl->bindPass = NULL;
255         ctl->attribEMail = NULL;
256         ctl->attribCName = NULL;
257         ctl->attribFName = NULL;
258         ctl->attribLName = NULL;
259         ctl->maxEntries = 0;
260         ctl->timeOut = 0;
261         ctl->maxQueryAge = 0;
262 }
263
264 /**
265  * Free up LDAP server interface object by releasing internal memory.
266  * \param ctl Control object to free.
267  */
268 void ldapctl_free( LdapControl *ctl ) {
269         g_return_if_fail( ctl != NULL );
270
271         /* Free internal stuff */
272         ldapctl_clear( ctl );
273
274         /* Free the mutex */
275         pthread_mutex_destroy( ctl->mutexCtl );
276         g_free( ctl->mutexCtl );
277         ctl->mutexCtl = NULL;
278
279         /* Now release LDAP control object */
280         g_free( ctl );
281 }
282
283 /**
284  * Setup default (empty) values for specified object.
285  * \param ctl  Control object to process.
286  */
287 void ldapctl_default_values( LdapControl *ctl ) {
288         g_return_if_fail( ctl != NULL );
289
290         /* Clear our destination */
291         ldapctl_clear( ctl );
292
293         /* Copy strings */
294         ctl->hostName = g_strdup( "" );
295         ctl->baseDN = g_strdup( "" );
296         ctl->bindDN = g_strdup( "" );
297         ctl->bindPass = g_strdup( "" );
298         ctl->port = LDAPCTL_DFL_PORT;
299         ctl->maxEntries = LDAPCTL_MAX_ENTRIES;
300         ctl->timeOut = LDAPCTL_DFL_TIMEOUT;
301         ctl->maxQueryAge = LDAPCTL_DFL_QUERY_AGE;
302
303         ldapctl_default_attributes( ctl );
304 }
305
306 /**
307  * Display object to specified stream.
308  * \param ctl    Control object to process.
309  * \param stream Output stream.
310  */
311 void ldapctl_print( const LdapControl *ctl, FILE *stream ) {
312         g_return_if_fail( ctl != NULL );
313
314         pthread_mutex_lock( ctl->mutexCtl );
315         fprintf( stream, "LdapControl:\n" );
316         fprintf( stream, "host name: '%s'\n", ctl->hostName );
317         fprintf( stream, "     port: %d\n",   ctl->port );
318         fprintf( stream, "  base dn: '%s'\n", ctl->baseDN );
319         fprintf( stream, "  bind dn: '%s'\n", ctl->bindDN );
320         fprintf( stream, "bind pass: '%s'\n", ctl->bindPass );
321         fprintf( stream, "attr mail: '%s'\n", ctl->attribEMail );
322         fprintf( stream, "attr comn: '%s'\n", ctl->attribCName );
323         fprintf( stream, "attr frst: '%s'\n", ctl->attribFName );
324         fprintf( stream, "attr last: '%s'\n", ctl->attribLName );
325         fprintf( stream, "max entry: %d\n",   ctl->maxEntries );
326         fprintf( stream, "  timeout: %d\n",   ctl->timeOut );
327         fprintf( stream, "  max age: %d\n",   ctl->maxQueryAge );
328         fprintf( stream, "crit list:\n" );
329         if( ctl->listCriteria ) {
330                 mgu_print_dlist( ctl->listCriteria, stream );
331         }
332         else {
333                 fprintf( stream, "\t!!!none!!!\n" );
334         }
335         pthread_mutex_unlock( ctl->mutexCtl );
336 }
337
338 /**
339  * Copy member variables to specified object. Mutex lock object is
340  * not copied.
341  * \param ctlFrom Object to copy from.
342  * \param ctlTo   Destination object.
343  */
344 void ldapctl_copy( const LdapControl *ctlFrom, LdapControl *ctlTo ) {
345         GList *node;
346
347         g_return_if_fail( ctlFrom != NULL );
348         g_return_if_fail( ctlTo != NULL );
349
350         /* Lock both objects */
351         pthread_mutex_lock( ctlFrom->mutexCtl );
352         pthread_mutex_lock( ctlTo->mutexCtl );
353
354         /* Clear our destination */
355         ldapctl_clear( ctlTo );
356
357         /* Copy strings */
358         ctlTo->hostName = g_strdup( ctlFrom->hostName );
359         ctlTo->baseDN = g_strdup( ctlFrom->baseDN );
360         ctlTo->bindDN = g_strdup( ctlFrom->bindDN );
361         ctlTo->bindPass = g_strdup( ctlFrom->bindPass );
362         ctlTo->attribEMail = g_strdup( ctlFrom->attribEMail );
363         ctlTo->attribCName = g_strdup( ctlFrom->attribCName );
364         ctlTo->attribFName = g_strdup( ctlFrom->attribFName );
365         ctlTo->attribLName = g_strdup( ctlFrom->attribLName );
366
367         /* Copy search criteria */
368         node = ctlFrom->listCriteria;
369         while( node ) {
370                 ctlTo->listCriteria = g_list_append(
371                         ctlTo->listCriteria, g_strdup( node->data ) );
372                 node = g_list_next( node );
373         }
374
375         /* Copy other members */
376         ctlTo->port = ctlFrom->port;
377         ctlTo->maxEntries = ctlFrom->maxEntries;
378         ctlTo->timeOut = ctlFrom->timeOut;
379         ctlTo->maxQueryAge = ctlFrom->maxQueryAge;
380
381         /* Unlock */
382         pthread_mutex_unlock( ctlTo->mutexCtl );
383         pthread_mutex_unlock( ctlFrom->mutexCtl );
384 }
385
386 /**
387  * Create an LDAP search criteria by parsing specified search term. The search
388  * term may contain two names separated by the first embedded space found in
389  * the search term. It is assumed that the two tokens are first name and last
390  * name, or vice versa. An appropriate search criteria will be constructed.
391  *
392  * \param  searchTerm Reference to search term to process.
393  * \return Formatted search criteria, or <code>NULL</code> if there is no
394  *         embedded spaces. The search term should be g_free() when no
395  *         longer required.
396  */
397 static gchar *ldapctl_build_ldap_criteria( gchar *searchTerm ) {
398         gchar *p;
399         gchar *t1;
400         gchar *t2 = NULL;
401         gchar *term;
402         gchar *crit = NULL;
403
404         term = g_strdup( searchTerm );
405         g_strstrip( term );
406
407         /* Find first space character */        
408         t1 = p = term;
409         while( *p ) {
410                 if( *p == ' ' ) {
411                         *p = '\0';
412                         t2 = g_strdup( 1 + p );
413                         break;
414                 }
415                 p++;
416         }
417
418         if( t2 ) {
419                 /* Format search criteria */
420                 gchar *p1, *p2;
421
422                 g_strstrip( t2 );
423                 p1 = g_strdup_printf( "(&(givenName=%s*)(sn=%s*))", t1, t2 );
424                 p2 = g_strdup_printf( "(&(givenName=%s*)(sn=%s*))", t2, t1 );
425                 crit = g_strdup_printf( "(&(|%s%s)(mail=*))", p1, p2 );
426
427                 g_free( t2 );
428                 g_free( p1 );
429                 g_free( p2 );
430         }
431         g_free( term );
432         return crit;
433 }
434
435 /**
436  * Build a formatted LDAP search criteria string from criteria list.
437  * \param ctl  Control object to process.
438  * \param searchVal Value to search for.
439  * \return Formatted string. Should be g_free() when done.
440  */
441 gchar *ldapctl_format_criteria( LdapControl *ctl, const gchar *searchVal ) {
442         GList *node;
443         gchar *p1, *p2, *retVal;
444
445         g_return_val_if_fail( ctl != NULL, NULL );
446         g_return_val_if_fail( searchVal != NULL, NULL );
447
448         /* Test whether there are more that one search terms */
449         retVal = ldapctl_build_ldap_criteria( searchVal );
450         if( retVal ) return retVal;
451
452         /* No - just a simple search */
453         /* p1 contains previous formatted criteria */
454         /* p2 contains next formatted criteria */
455         retVal = p1 = p2 = NULL;
456         node = ctl->listCriteria;
457         while( node ) {
458                 gchar *attr, *tmp;
459
460                 attr = node->data;
461                 node = g_list_next( node );
462
463                 /* Switch pointers */
464                 tmp = p1; p1 = p2; p2 = tmp;
465
466                 if( p1 ) {
467                         /* Subsequent time through */
468                         gchar *crit;
469
470                         /* Format query criteria */
471                         crit = g_strdup_printf( "(%s=%s*)", attr, searchVal );
472
473                         /* Append to existing criteria */                       
474                         g_free( p2 );
475                         p2 = g_strdup_printf( "(|%s%s)", p1, crit );
476
477                         g_free( crit );
478                 }
479                 else {
480                         /* First time through - Format query criteria */
481                         p2 = g_strdup_printf( "(%s=%s*)", attr, searchVal );
482                 }
483         }
484
485         if( p2 == NULL ) {
486                 /* Nothing processed - format a default attribute */
487                 retVal = g_strdup_printf( "(%s=*)", LDAPCTL_ATTR_EMAIL );
488         }
489         else {
490                 /* We have something - free up previous result */
491                 retVal = p2;
492                 g_free( p1 );
493         }
494         return retVal;
495 }
496
497 /**
498  * Return array of pointers to attributes for LDAP query.
499  * \param  ctl  Control object to process.
500  * \return NULL terminated list.
501  */
502 char **ldapctl_attribute_array( LdapControl *ctl ) {
503         char **ptrArray;
504         GList *node;
505         gint cnt, i;
506         g_return_val_if_fail( ctl != NULL, NULL );
507
508         cnt = g_list_length( ctl->listCriteria );
509         ptrArray = g_new0( char *, 1 + cnt );
510         i = 0;
511         node = ctl->listCriteria;
512         while( node ) {
513                 ptrArray[ i++ ] = node->data;
514                 node = g_list_next( node );
515         }
516         ptrArray[ i ] = NULL;
517         return ptrArray;
518 }
519
520 /**
521  * Free array of pointers allocated by ldapctl_criteria_array().
522  * param ptrArray Array to clear.
523  */
524 void ldapctl_free_attribute_array( char **ptrArray ) {
525         gint i;
526
527         /* Clear array to NULL's */
528         for( i = 0; ptrArray[i] != NULL; i++ ) {
529                 ptrArray[i] = NULL;
530         }
531         g_free( ptrArray );
532 }       
533
534 /**
535  * Parse LDAP search string, building list of LDAP criteria attributes. This
536  * may be used to convert an old style Sylpheed LDAP search criteria to the
537  * new format. The old style uses a standard LDAP search string, for example:
538  * <pre>
539  *    (&(mail=*)(cn=%s*))
540  * </pre>
541  * This function extracts the two LDAP attributes <code>mail</code> and
542  * <code>cn</code>, adding each to a list.
543  *
544  * \param ctl Control object to process.
545  * \param criteria LDAP search criteria string.
546  */
547 void ldapctl_parse_ldap_search( LdapControl *ctl, gchar *criteria ) {
548         gchar *ptr;
549         gchar *pFrom;
550         gchar *attrib;
551         gint iLen;
552
553         g_return_if_fail( ctl != NULL );
554
555         ldapctl_criteria_list_clear( ctl );
556         if( criteria == NULL ) return;
557
558         pFrom = NULL;
559         ptr = criteria;
560         while( *ptr ) {
561                 if( *ptr == '(' ) {
562                         pFrom = 1 + ptr;
563                 }
564                 if( *ptr == '=' ) {
565                         if( pFrom ) {
566                                 iLen = ptr - pFrom;
567                                 attrib = g_strndup( pFrom, iLen );
568                                 g_strstrip( attrib );
569                                 ldapctl_criteria_list_add( ctl, attrib );
570                                 g_free( attrib );
571                         }
572                         pFrom = NULL;
573                 }
574                 ptr++;
575         }
576 }
577
578 #endif  /* USE_LDAP */
579
580 /*
581  * End of Source.
582  */
583