2010-04-15 [pawel] 3.7.5cvs47
[claws.git] / src / ldapctrl.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2009 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 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 #include "passcrypt.h"
37 #include "editaddress_other_attributes_ldap.h"
38 #include "common/utils.h"
39
40 /**
41  * Create new LDAP control block object.
42  * \return Initialized control object.
43  */
44 LdapControl *ldapctl_create( void ) {
45         LdapControl *ctl;
46
47         ctl = g_new0( LdapControl, 1 );
48         ctl->hostName = NULL;
49         ctl->port = LDAPCTL_DFL_PORT;
50         ctl->baseDN = NULL;
51         ctl->bindDN = NULL;
52         ctl->bindPass = NULL;
53         ctl->listCriteria = NULL;
54         ctl->attribEMail = g_strdup( LDAPCTL_ATTR_EMAIL );
55         ctl->attribCName = g_strdup( LDAPCTL_ATTR_COMMONNAME );
56         ctl->attribFName = g_strdup( LDAPCTL_ATTR_GIVENNAME );
57         ctl->attribLName = g_strdup( LDAPCTL_ATTR_SURNAME );
58         ctl->attribDName = g_strdup( LDAPCTL_ATTR_DISPLAYNAME );
59         ctl->maxEntries = LDAPCTL_MAX_ENTRIES;
60         ctl->timeOut = LDAPCTL_DFL_TIMEOUT;
61         ctl->maxQueryAge = LDAPCTL_DFL_QUERY_AGE;
62         ctl->matchingOption = LDAPCTL_MATCH_BEGINWITH;
63         ctl->version = 0;
64         ctl->enableTLS = FALSE;
65         ctl->enableSSL = FALSE;
66
67         /* Mutex to protect control block */
68         ctl->mutexCtl = g_malloc0( sizeof( pthread_mutex_t ) );
69         pthread_mutex_init( ctl->mutexCtl, NULL );
70
71         return ctl;
72 }
73
74 /**
75  * Specify hostname to be used.
76  * \param ctl   Control object to process.
77  * \param value Host name.
78  */
79 void ldapctl_set_host( LdapControl* ctl, const gchar *value ) {
80         ctl->hostName = mgu_replace_string( ctl->hostName, value );
81
82         if ( ctl->hostName == NULL )
83                 return;
84
85         g_strstrip( ctl->hostName );
86         debug_print("setting hostname: %s\n", ctl->hostName);
87 }
88
89 /**
90  * Specify port to be used.
91  * \param ctl  Control object to process.
92  * \param value Port.
93  */
94 void ldapctl_set_port( LdapControl* ctl, const gint value ) {
95         if( value > 0 ) {
96                 ctl->port = value;
97         }
98         else {
99                 ctl->port = LDAPCTL_DFL_PORT;
100         }
101         debug_print("setting port: %d\n", ctl->port);
102 }
103
104 /**
105  * Specify base DN to be used.
106  * \param ctl  Control object to process.
107  * \param value Base DN.
108  */
109 void ldapctl_set_base_dn( LdapControl* ctl, const gchar *value ) {
110         ctl->baseDN = mgu_replace_string( ctl->baseDN, value );
111
112         if ( ctl->baseDN == NULL )
113                 return;
114
115         g_strstrip( ctl->baseDN );
116         debug_print("setting baseDN: %s\n", ctl->baseDN);
117 }
118
119 /**
120  * Specify bind DN to be used.
121  * \param ctl  Control object to process.
122  * \param value Bind DN.
123  */
124 void ldapctl_set_bind_dn( LdapControl* ctl, const gchar *value ) {
125         ctl->bindDN = mgu_replace_string( ctl->bindDN, value );
126
127         if ( ctl->bindDN == NULL )
128                 return;
129
130         g_strstrip( ctl->bindDN );
131         debug_print("setting bindDN: %s\n", ctl->bindDN);
132 }
133
134 /**
135  * Specify bind password to be used.
136  * \param ctl  Control object to process.
137  * \param value Password.
138  * \param encrypt Encrypt password
139  * \param change Save encrypted
140  */
141 void ldapctl_set_bind_password( 
142         LdapControl* ctl, const gchar *value, gboolean encrypt, gboolean change ) {
143         gchar *buf = NULL, *tmp;
144
145         ctl->bindPass = mgu_replace_string( ctl->bindPass, value );
146
147         if ( ctl->bindPass == NULL )
148                 return;
149
150         g_strstrip( ctl->bindPass );
151
152         if ( encrypt ) {
153                 /* If first char is not ! the password is not encrypted */
154                 if (ctl->bindPass[0] == '!' || change) {
155                         if (ctl->bindPass[0] != '!' && change)
156                                 buf = mgu_replace_string( buf, ctl->bindPass );
157                         else
158                                 buf = mgu_replace_string( buf, ctl->bindPass + 1 );
159                         passcrypt_encrypt( buf, strlen(buf) );
160                         if (ctl->bindPass[0] != '!' && change) {
161                                 tmp = g_strconcat( "!", buf, NULL );
162                                 g_free(buf);
163                                 buf = g_strdup(tmp);
164                                 g_free(tmp);
165                         }
166                         ctl->bindPass = mgu_replace_string( ctl->bindPass, buf );
167                         g_free(buf);
168                 }
169         }
170         debug_print("setting bindPassword\n");
171 }
172
173 /**
174  * Fetch bind password to be used.
175  * \param ctl  Control object to process.
176  * \return Decrypted password.
177  */
178 gchar* ldapctl_get_bind_password( LdapControl* ctl ) {
179         gchar *pwd = NULL, *buf;
180
181         if ( ctl->bindPass != NULL ) {
182                 pwd = mgu_replace_string( pwd, ctl->bindPass );
183                 /* If first char is not ! the password is not encrypted */
184                 if (pwd && pwd[0] == '!') {
185                         buf = g_strdup(pwd + 1);
186                         g_free(pwd);
187                         passcrypt_decrypt( buf, strlen(buf) );
188                         pwd = g_strdup(buf);
189                         g_free(buf);
190                 }
191         }
192
193         debug_print("getting bindPassword\n");
194
195         return pwd;
196 }
197
198 /**
199  * Specify maximum number of entries to retrieve.
200  * \param ctl  Control object to process.
201  * \param value Maximum entries.
202  */
203 void ldapctl_set_max_entries( LdapControl* ctl, const gint value ) {
204         if( value > 0 ) {
205                 ctl->maxEntries = value;
206         }
207         else {
208                 ctl->maxEntries = LDAPCTL_MAX_ENTRIES;
209         }
210         debug_print("setting maxEntries: %d\n", ctl->maxEntries);
211 }
212
213 /**
214  * Specify timeout value for LDAP operation (in seconds).
215  * \param ctl  Control object to process.
216  * \param value Timeout.
217  */
218 void ldapctl_set_timeout( LdapControl* ctl, const gint value ) {
219         if( value > 0 ) {
220                 ctl->timeOut = value;
221         }
222         else {
223                 ctl->timeOut = LDAPCTL_DFL_TIMEOUT;
224         }
225         debug_print("setting timeOut: %d\n", ctl->timeOut);
226 }
227
228 /**
229  * Specify maximum age of query (in seconds) before query is retired.
230  * \param ctl  Control object to process.
231  * \param value Maximum age.
232  */
233 void ldapctl_set_max_query_age( LdapControl* ctl, const gint value ) {
234         if( value > LDAPCTL_MAX_QUERY_AGE ) {
235                 ctl->maxQueryAge = LDAPCTL_MAX_QUERY_AGE;
236         }
237         else if( value < 1 ) {
238                 ctl->maxQueryAge = LDAPCTL_DFL_QUERY_AGE;
239         }
240         else {
241                 ctl->maxQueryAge = value;
242         }
243         debug_print("setting maxAge: %d\n", ctl->maxQueryAge);
244 }
245
246 /**
247  * Specify matching option to be used for searches.
248  * \param ctl   Control object to process.
249  * \param value Matching option, as follows:
250  * <ul>
251  * <li><code>LDAPCTL_MATCH_BEGINWITH</code> for "begins with" search</li>
252  * <li><code>LDAPCTL_MATCH_CONTAINS</code> for "contains" search</li>
253  * </ul>
254  */
255 void ldapctl_set_matching_option( LdapControl* ctl, const gint value ) {
256         if( value < LDAPCTL_MATCH_BEGINWITH ) {
257                 ctl->matchingOption = LDAPCTL_MATCH_BEGINWITH;
258         }
259         else if( value > LDAPCTL_MATCH_CONTAINS ) {
260                 ctl->matchingOption = LDAPCTL_MATCH_BEGINWITH;
261         }
262         else {
263                 ctl->matchingOption = value;
264         }
265         debug_print("setting matchingOption: %d\n", ctl->matchingOption);
266 }
267
268 /**
269  * Specify TLS option.
270  * \param ctl   Control object to process.
271  * \param value <i>TRUE</i> to enable TLS.
272  */
273 void ldapctl_set_tls( LdapControl* ctl, const gboolean value ) {
274         ctl->enableTLS = value;
275         debug_print("setting TLS: %d\n", ctl->enableTLS);
276 }
277
278 void ldapctl_set_ssl( LdapControl* ctl, const gboolean value ) {
279         ctl->enableSSL = value;
280         debug_print("setting SSL: %d\n", ctl->enableSSL);
281 }
282
283 /**
284  * Return search criteria list.
285  * \param  ctl  Control data object.
286  * \return Linked list of character strings containing LDAP attribute names to
287  *         use for a search. This should not be modified directly. Use the
288  *         <code>ldapctl_set_criteria_list()</code>,
289  *         <code>ldapctl_criteria_list_clear()</code> and
290  *         <code>ldapctl_criteria_list_add()</code> functions for this purpose.
291  */
292 GList *ldapctl_get_criteria_list( const LdapControl* ctl ) {
293         cm_return_val_if_fail( ctl != NULL, NULL );
294         return ctl->listCriteria;
295 }
296
297 /**
298  * Clear list of LDAP search attributes.
299  * \param  ctl  Control data object.
300  */
301 void ldapctl_criteria_list_clear( LdapControl *ctl ) {
302         cm_return_if_fail( ctl != NULL );
303         mgu_free_dlist( ctl->listCriteria );
304         ctl->listCriteria = NULL;
305 }
306
307 /**
308  * Add LDAP attribute to criteria list.
309  * \param ctl  Control object to process.
310  * \param attr Attribute name to append. If not NULL and unique, a copy will
311  *             be appended to the list.
312  */
313 void ldapctl_criteria_list_add( LdapControl *ctl, gchar *attr ) {
314         cm_return_if_fail( ctl != NULL );
315         if( attr != NULL ) {
316                 if( mgu_list_test_unq_nc( ctl->listCriteria, attr ) ) {
317                         debug_print("adding to criteria list: %s\n", attr);
318                         ctl->listCriteria = g_list_append(
319                                 ctl->listCriteria, g_strdup( attr ) );
320                 }
321         }
322 }
323
324 /**
325  * Clear LDAP server member variables.
326  * \param ctl Control object to clear.
327  */
328 static void ldapctl_clear( LdapControl *ctl ) {
329         cm_return_if_fail( ctl != NULL );
330
331         debug_print("clearing ldap controller members\n");
332         /* Free internal stuff */
333         g_free( ctl->hostName );
334         g_free( ctl->baseDN );
335         g_free( ctl->bindDN );
336         g_free( ctl->bindPass );
337         g_free( ctl->attribEMail );
338         g_free( ctl->attribCName );
339         g_free( ctl->attribFName );
340         g_free( ctl->attribLName );
341         g_free( ctl->attribDName );
342
343         ldapctl_criteria_list_clear( ctl );
344
345         /* Clear pointers */
346         ctl->hostName = NULL;
347         ctl->port = 0;
348         ctl->baseDN = NULL;
349         ctl->bindDN = NULL;
350         ctl->bindPass = NULL;
351         ctl->attribEMail = NULL;
352         ctl->attribCName = NULL;
353         ctl->attribFName = NULL;
354         ctl->attribLName = NULL;
355         ctl->attribDName = NULL;
356         ctl->maxEntries = 0;
357         ctl->timeOut = 0;
358         ctl->maxQueryAge = 0;
359         ctl->matchingOption = LDAPCTL_MATCH_BEGINWITH;
360         ctl->version = 0;
361         ctl->enableTLS = FALSE;
362         ctl->enableSSL = FALSE;
363 }
364
365 /**
366  * Free up LDAP server interface object by releasing internal memory.
367  * \param ctl Control object to free.
368  */
369 void ldapctl_free( LdapControl *ctl ) {
370         cm_return_if_fail( ctl != NULL );
371
372         debug_print("releasing requested memory for ldap controller\n");
373         /* Free internal stuff */
374         ldapctl_clear( ctl );
375
376         /* Free the mutex */
377         pthread_mutex_destroy( ctl->mutexCtl );
378         g_free( ctl->mutexCtl );
379         ctl->mutexCtl = NULL;
380
381         /* Now release LDAP control object */
382         g_free( ctl );
383 }
384
385 /**
386  * Display object to specified stream.
387  * \param ctl    Control object to process.
388  * \param stream Output stream.
389  */
390 void ldapctl_print( const LdapControl *ctl, FILE *stream ) {
391         cm_return_if_fail( ctl != NULL );
392         gchar *pwd;
393
394         pthread_mutex_lock( ctl->mutexCtl );
395         fprintf( stream, "LdapControl:\n" );
396         fprintf( stream, "host name: '%s'\n", ctl->hostName?ctl->hostName:"null" );
397         fprintf( stream, "     port: %d\n",   ctl->port );
398         fprintf( stream, "  base dn: '%s'\n", ctl->baseDN?ctl->baseDN:"null" );
399         fprintf( stream, "  bind dn: '%s'\n", ctl->bindDN?ctl->bindDN:"null" );
400         pwd = ldapctl_get_bind_password((LdapControl *) ctl);
401         fprintf( stream, "bind pass: '%s'\n", pwd?pwd:"null" );
402         g_free(pwd);
403         fprintf( stream, "attr mail: '%s'\n", ctl->attribEMail?ctl->attribEMail:"null" );
404         fprintf( stream, "attr comn: '%s'\n", ctl->attribCName?ctl->attribCName:"null" );
405         fprintf( stream, "attr frst: '%s'\n", ctl->attribFName?ctl->attribFName:"null" );
406         fprintf( stream, "attr last: '%s'\n", ctl->attribLName?ctl->attribLName:"null" );
407         fprintf( stream, "attr disn: '%s'\n", ctl->attribDName?ctl->attribDName:"null" );
408         fprintf( stream, "max entry: %d\n",   ctl->maxEntries );
409         fprintf( stream, "  timeout: %d\n",   ctl->timeOut );
410         fprintf( stream, "  max age: %d\n",   ctl->maxQueryAge );
411         fprintf( stream, "match opt: %d\n",   ctl->matchingOption );
412         fprintf( stream, "  version: %d\n",   ctl->version );
413         fprintf( stream, "      TLS: %s\n",   ctl->enableTLS ? "yes" : "no" );
414         fprintf( stream, "      SSL: %s\n",   ctl->enableSSL ? "yes" : "no" );
415         fprintf( stream, "crit list:\n" );
416         if( ctl->listCriteria ) {
417                 mgu_print_dlist( ctl->listCriteria, stream );
418         }
419         else {
420                 fprintf( stream, "\t!!!none!!!\n" );
421         }
422         pthread_mutex_unlock( ctl->mutexCtl );
423 }
424
425 /**
426  * Copy member variables to specified object. Mutex lock object is
427  * not copied.
428  * \param ctlFrom Object to copy from.
429  * \param ctlTo   Destination object.
430  */
431 void ldapctl_copy( const LdapControl *ctlFrom, LdapControl *ctlTo ) {
432         GList *node;
433
434         cm_return_if_fail( ctlFrom != NULL );
435         cm_return_if_fail( ctlTo != NULL );
436
437         debug_print("ldap controller copy\n");
438         /* Lock both objects */
439         pthread_mutex_lock( ctlFrom->mutexCtl );
440         pthread_mutex_lock( ctlTo->mutexCtl );
441
442         /* Clear our destination */
443         ldapctl_clear( ctlTo );
444
445         /* Copy strings */
446         ctlTo->hostName = g_strdup( ctlFrom->hostName );
447         ctlTo->baseDN = g_strdup( ctlFrom->baseDN );
448         ctlTo->bindDN = g_strdup( ctlFrom->bindDN );
449         ctlTo->bindPass = g_strdup( ctlFrom->bindPass );
450         ctlTo->attribEMail = g_strdup( ctlFrom->attribEMail );
451         ctlTo->attribCName = g_strdup( ctlFrom->attribCName );
452         ctlTo->attribFName = g_strdup( ctlFrom->attribFName );
453         ctlTo->attribLName = g_strdup( ctlFrom->attribLName );
454         ctlTo->attribDName = g_strdup( ctlFrom->attribDName );
455
456         /* Copy search criteria */
457         node = ctlFrom->listCriteria;
458         while( node ) {
459                 ctlTo->listCriteria = g_list_append(
460                         ctlTo->listCriteria, g_strdup( node->data ) );
461                 node = g_list_next( node );
462         }
463
464         /* Copy other members */
465         ctlTo->port = ctlFrom->port;
466         ctlTo->maxEntries = ctlFrom->maxEntries;
467         ctlTo->timeOut = ctlFrom->timeOut;
468         ctlTo->maxQueryAge = ctlFrom->maxQueryAge;
469         ctlTo->matchingOption = ctlFrom->matchingOption;
470         ctlTo->version = ctlFrom->version;
471         ctlTo->enableTLS = ctlFrom->enableTLS;
472         ctlTo->enableSSL = ctlFrom->enableSSL;
473
474         /* Unlock */
475         pthread_mutex_unlock( ctlTo->mutexCtl );
476         pthread_mutex_unlock( ctlFrom->mutexCtl );
477 }
478
479 /**
480  * Search criteria fragment - two terms - begin with (default).
481  */
482 static gchar *_criteria2BeginWith = "(&(givenName=%s*)(sn=%s*))";
483
484 /**
485  * Search criteria fragment - two terms - contains.
486  */
487 static gchar *_criteria2Contains  = "(&(givenName=*%s*)(sn=*%s*))";
488
489 /**
490  * Create an LDAP search criteria by parsing specified search term. The search
491  * term may contain two names separated by the first embedded space found in
492  * the search term. It is assumed that the two tokens are first name and last
493  * name, or vice versa. An appropriate search criteria will be constructed.
494  *
495  * \param  searchTerm   Reference to search term to process.
496  * \param  matchOption  Set to the following:
497  * <ul>
498  * <li><code>LDAPCTL_MATCH_BEGINWITH</code> for "begins with" search</li>
499  * <li><code>LDAPCTL_MATCH_CONTAINS</code> for "contains" search</li>
500  * </ul>
501  *
502  * \return Formatted search criteria, or <code>NULL</code> if there is no
503  *         embedded spaces. The search term should be g_free() when no
504  *         longer required.
505  */
506 static gchar *ldapctl_build_ldap_criteria(
507                 const gchar *searchTerm, const gint matchOption )
508 {
509         gchar *p;
510         gchar *t1;
511         gchar *t2 = NULL;
512         gchar *term;
513         gchar *crit = NULL;
514         gchar *criteriaFmt;
515
516         if( matchOption == LDAPCTL_MATCH_CONTAINS ) {
517                 criteriaFmt = _criteria2Contains;
518         }
519         else {
520                 criteriaFmt = _criteria2BeginWith;
521         }
522
523         term = g_strdup( searchTerm );
524         g_strstrip( term );
525
526         /* Find first space character */        
527         t1 = p = term;
528         while( *p ) {
529                 if( *p == ' ' ) {
530                         *p = '\0';
531                         t2 = g_strdup( 1 + p );
532                         break;
533                 }
534                 p++;
535         }
536
537         if( t2 ) {
538                 /* Format search criteria */
539                 gchar *p1, *p2;
540
541                 g_strstrip( t2 );
542                 p1 = g_strdup_printf( criteriaFmt, t1, t2 );
543                 p2 = g_strdup_printf( criteriaFmt, t2, t1 );
544                 crit = g_strdup_printf( "(&(|%s%s)(mail=*))", p1, p2 );
545
546                 g_free( t2 );
547                 g_free( p1 );
548                 g_free( p2 );
549         }
550         g_free( term );
551         debug_print("search criteria: %s\n", crit?crit:"null");
552         return crit;
553 }
554
555
556 /**
557  * Search criteria fragment - single term - begin with (default).
558  */
559 static gchar *_criteriaBeginWith = "(%s=%s*)";
560
561 /**
562  * Search criteria fragment - single term - contains.
563  */
564 static gchar *_criteriaContains  = "(%s=*%s*)";
565
566 /**
567  * Build a formatted LDAP search criteria string from criteria list.
568  * \param ctl  Control object to process.
569  * \param searchVal Value to search for.
570  * \return Formatted string. Should be g_free() when done.
571  */
572 gchar *ldapctl_format_criteria( LdapControl *ctl, const gchar *searchVal ) {
573         GList *node;
574         gchar *p1, *p2, *retVal;
575         gchar *criteriaFmt;
576
577         cm_return_val_if_fail( ctl != NULL, NULL );
578         cm_return_val_if_fail( searchVal != NULL, NULL );
579
580         /* Test whether there are more that one search terms */
581         retVal = ldapctl_build_ldap_criteria( searchVal, ctl->matchingOption );
582         if( retVal ) return retVal;
583
584         if( ctl->matchingOption ==  LDAPCTL_MATCH_CONTAINS ) {
585                 criteriaFmt = _criteriaContains;
586         }
587         else {
588                 criteriaFmt = _criteriaBeginWith;
589         }
590
591         /* No - just a simple search */
592         /* p1 contains previous formatted criteria */
593         /* p2 contains next formatted criteria */
594         retVal = p1 = p2 = NULL;
595         node = ctl->listCriteria;
596         while( node ) {
597                 gchar *attr, *tmp;
598                 attr = node->data;
599                 node = g_list_next( node );
600
601                 /* Switch pointers */
602                 tmp = p1; p1 = p2; p2 = tmp;
603
604                 if( p1 ) {
605                         /* Subsequent time through */
606                         gchar *crit;
607
608                         /* Format query criteria */
609                         crit = g_strdup_printf( criteriaFmt, attr, searchVal );
610
611                         /* Append to existing criteria */                       
612                         g_free( p2 );
613                         p2 = g_strdup_printf( "(|%s%s)", p1, crit );
614
615                         g_free( crit );
616                 }
617                 else {
618                         /* First time through - Format query criteria */
619                         p2 = g_strdup_printf( criteriaFmt, attr, searchVal );
620                 }
621         }
622
623         if( p2 == NULL ) {
624                 /* Nothing processed - format a default attribute */
625                 retVal = g_strdup_printf( "(%s=*)", LDAPCTL_ATTR_EMAIL );
626         }
627         else {
628                 /* We have something - free up previous result */
629                 retVal = p2;
630                 g_free( p1 );
631         }
632         debug_print("current search string: %s\n", retVal);
633         return retVal;
634 }
635
636 /**
637  * Return array of pointers to attributes for LDAP query.
638  * \param  ctl  Control object to process.
639  * \return NULL terminated list.
640  */
641 char **ldapctl_attribute_array( LdapControl *ctl ) {
642         char **ptrArray;
643         GList *node;
644         gint cnt, i;
645         cm_return_val_if_fail( ctl != NULL, NULL );
646
647         node = ctl->listCriteria;
648         cnt = g_list_length( ctl->listCriteria );
649         ptrArray = g_new0( char *, 1 + cnt );
650         i = 0;
651         while( node ) {
652                 ptrArray[ i++ ] = node->data;
653                 /*debug_print("adding search attribute: %s\n", (gchar *) node->data);*/
654                 node = g_list_next( node );
655         }
656         ptrArray[ i ] = NULL;
657         return ptrArray;
658 }
659
660 /**
661  * Return array of pointers to attributes for LDAP query.
662  * \param  ctl  Control object to process.
663  * \return NULL terminated list.
664  */
665 char **ldapctl_full_attribute_array( LdapControl *ctl ) {
666         char **ptrArray;
667         GList *node, *def;
668         GList *tmp = NULL;
669         gint cnt, i;
670         cm_return_val_if_fail( ctl != NULL, NULL );
671
672         def = ctl->listCriteria;
673         while (def) {
674                 tmp = g_list_append(tmp, g_strdup(def->data));
675                 def = def->next;
676         }
677
678         def = ldapctl_get_default_criteria_list();
679         
680         while (def) {
681                 if( g_list_find_custom(tmp, (gpointer)def->data, 
682                                 (GCompareFunc)strcmp2) == NULL) {
683                         tmp = g_list_append(tmp, g_strdup(def->data));
684                 }
685                 def = def->next;
686         }
687
688         node = tmp;
689         cnt = g_list_length( tmp );
690         ptrArray = g_new0( char *, 1 + cnt);
691         i = 0;
692         while( node ) {
693                 ptrArray[ i++ ] = node->data;
694                 /*debug_print("adding search attribute: %s\n", (gchar *) node->data);*/
695                 node = g_list_next( node );
696         }
697         ptrArray[ i ] = NULL;
698         return ptrArray;
699 }
700
701 /**
702  * Free array of pointers allocated by ldapctl_criteria_array().
703  * param ptrArray Array to clear.
704  */
705 void ldapctl_free_attribute_array( char **ptrArray ) {
706         gint i;
707
708         /* Clear array to NULL's */
709         for( i = 0; ptrArray[i] != NULL; i++ ) {
710                 ptrArray[i] = NULL;
711         }
712         g_free( ptrArray );
713 }       
714
715 /**
716  * Parse LDAP search string, building list of LDAP criteria attributes. This
717  * may be used to convert an old style Sylpheed LDAP search criteria to the
718  * new format. The old style uses a standard LDAP search string, for example:
719  * <pre>
720  *    (&(mail=*)(cn=%s*))
721  * </pre>
722  * This function extracts the two LDAP attributes <code>mail</code> and
723  * <code>cn</code>, adding each to a list.
724  *
725  * \param ctl Control object to process.
726  * \param criteria LDAP search criteria string.
727  */
728 void ldapctl_parse_ldap_search( LdapControl *ctl, gchar *criteria ) {
729         gchar *ptr;
730         gchar *pFrom;
731         gchar *attrib;
732         gint iLen;
733
734         cm_return_if_fail( ctl != NULL );
735
736         ldapctl_criteria_list_clear( ctl );
737         if( criteria == NULL ) return;
738
739         pFrom = NULL;
740         ptr = criteria;
741         while( *ptr ) {
742                 if( *ptr == '(' ) {
743                         pFrom = 1 + ptr;
744                 }
745                 if( *ptr == '=' ) {
746                         if( pFrom ) {
747                                 iLen = ptr - pFrom;
748                                 attrib = g_strndup( pFrom, iLen );
749                                 g_strstrip( attrib );
750                                 ldapctl_criteria_list_add( ctl, attrib );
751                                 g_free( attrib );
752                         }
753                         pFrom = NULL;
754                 }
755                 ptr++;
756         }
757 }
758
759 /**
760  * Return the default LDAP search criteria string.
761  * \return Formatted string or <i>""</i>. Should be g_free() when done.
762  */
763 gchar *ldapctl_get_default_criteria() {
764         gchar *retVal = g_strdup(LDAPCTL_DFL_ATTR_LIST);
765         const gchar **attrs = ATTRIBUTE; 
766
767         while (*attrs) {
768                 gchar *tmp = g_strdup_printf("%s, %s", retVal, *attrs++);
769                 g_free(retVal);
770                 retVal = tmp;
771         }
772         debug_print("default search criteria: %s\n", retVal);
773         return retVal;
774 }
775
776 /**
777  * Return the default LDAP search criteria list.
778  * \return GList or <i>NULL</i>.
779  */
780 GList *ldapctl_get_default_criteria_list() {
781         gchar *criteria, *item;
782         gchar **c_list, **w_list;
783         GList *attr_list = NULL;
784
785         criteria = ldapctl_get_default_criteria();
786         c_list = g_strsplit(criteria, " ", 0);
787         g_free(criteria);
788         criteria = NULL;
789         w_list = c_list;
790         while ((criteria = *w_list++) != 0) {
791                 /* copy string elimination <,> */
792                 if (*w_list)
793                         item = g_strndup(criteria, strlen(criteria) - 1);
794                 else
795                         item = g_strdup(criteria);
796                 debug_print("adding attribute to list: %s\n", item);
797                 attr_list = g_list_append(attr_list, g_strdup(item));
798                 g_free(item);
799         }
800         g_strfreev(c_list);
801         return attr_list;
802 }
803
804 /**
805  * Compare to GList for equality.
806  * \param l1 First GList
807  * \param l2 Second GList
808  * \Return TRUE or FALSE
809  */
810 gboolean ldapctl_compare_list(GList *l1, GList *l2) {
811         gchar *first, *second;
812         if (! l1 && ! l2)
813                 return TRUE;
814         if ((! l1 && l2) || (l1 && ! l2))
815                 return FALSE;
816         while (l1 && l2) {
817                 first = (gchar *) l1->data;
818                 second = (gchar *) l2->data;
819                 /*debug_print("comparing: %s = %s\n", first, second);*/
820                 if ( ! (first && second) || strcmp(first, second) != 0) {
821                         return FALSE;
822                 }
823                 l1 = g_list_next(l1);
824                 l2 = g_list_next(l2);
825         }
826         return TRUE;
827 }
828
829 #endif  /* USE_LDAP */
830
831 /*
832  * End of Source.
833  */
834