]> git.sur5r.net Git - openldap/blob - libraries/libldap/filter.c
6709540885ea4080d5b5431536588afd6201e46c
[openldap] / libraries / libldap / filter.c
1 /* search.c */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2007 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  */
19
20 #include "portable.h"
21
22 #include <stdio.h>
23
24 #include <ac/stdlib.h>
25
26 #include <ac/socket.h>
27 #include <ac/string.h>
28 #include <ac/time.h>
29
30 #include "ldap-int.h"
31
32 static int put_simple_vrFilter LDAP_P((
33         BerElement *ber,
34         char *str ));
35
36 static int put_vrFilter_list LDAP_P((
37         BerElement *ber,
38         char *str ));
39
40 static char *put_complex_filter LDAP_P((
41         BerElement *ber,
42         char *str,
43         ber_tag_t tag,
44         int not ));
45
46 static int put_simple_filter LDAP_P((
47         BerElement *ber,
48         char *str ));
49
50 static int put_substring_filter LDAP_P((
51         BerElement *ber,
52         char *type,
53         char *str ));
54
55 static int put_filter_list LDAP_P((
56         BerElement *ber,
57         char *str,
58         ber_tag_t tag ));
59
60 static int ldap_is_oid ( const char *str )
61 {
62         int i;
63
64         if( LDAP_ALPHA( str[0] )) {
65                 for( i=1; str[i]; i++ ) {
66                         if( !LDAP_LDH( str[i] )) {
67                                 return 0;
68                         }
69                 }
70                 return 1;
71
72         } else if LDAP_DIGIT( str[0] ) {
73                 int dot=0;
74                 for( i=1; str[i]; i++ ) {
75                         if( LDAP_DIGIT( str[i] )) {
76                                 dot=0;
77
78                         } else if ( str[i] == '.' ) {
79                                 if( dot ) return 0;
80                                 if( ++dot > 1 ) return 0;
81
82                         } else {
83                                 return 0;
84                         }
85                 }
86                 return !dot;
87         }
88
89         return 0;
90 }
91
92 static int ldap_is_desc ( const char *str )
93 {
94         int i;
95
96         if( LDAP_ALPHA( str[0] )) {
97                 for( i=1; str[i]; i++ ) {
98                         if( str[i] == ';' ) {
99                                 str = &str[i+1];
100                                 goto options;
101                         }
102
103                         if( !LDAP_LDH( str[i] )) {
104                                 return 0;
105                         }
106                 }
107                 return 1;
108
109         } else if LDAP_DIGIT( str[0] ) {
110                 int dot=0;
111                 for( i=1; str[i]; i++ ) {
112                         if( str[i] == ';' ) {
113                                 if( dot ) return 0;
114                                 str = &str[i+1];
115                                 goto options;
116                         }
117
118                         if( LDAP_DIGIT( str[i] )) {
119                                 dot=0;
120
121                         } else if ( str[i] == '.' ) {
122                                 if( dot ) return 0;
123                                 if( ++dot > 1 ) return 0;
124
125                         } else {
126                                 return 0;
127                         }
128                 }
129                 return !dot;
130         }
131
132         return 0;
133
134 options:
135         if( !LDAP_LDH( str[0] )) {
136                 return 0;
137         }
138         for( i=1; str[i]; i++ ) {
139                 if( str[i] == ';' ) {
140                         str = &str[i+1];
141                         goto options;
142                 }
143                 if( !LDAP_LDH( str[i] )) {
144                         return 0;
145                 }
146         }
147         return 1;
148 }
149
150 static char *
151 find_right_paren( char *s )
152 {
153         int     balance, escape;
154
155         balance = 1;
156         escape = 0;
157         while ( *s && balance ) {
158                 if ( !escape ) {
159                         if ( *s == '(' ) {
160                                 balance++;
161                         } else if ( *s == ')' ) {
162                                 balance--;
163                         }
164                 }
165
166                 escape = ( *s == '\\' && !escape );
167
168                 if ( balance ) s++;
169         }
170
171         return *s ? s : NULL;
172 }
173
174 static int hex2value( int c )
175 {
176         if( c >= '0' && c <= '9' ) {
177                 return c - '0';
178         }
179
180         if( c >= 'A' && c <= 'F' ) {
181                 return c + (10 - (int) 'A');
182         }
183
184         if( c >= 'a' && c <= 'f' ) {
185                 return c + (10 - (int) 'a');
186         }
187
188         return -1;
189 }
190
191 char *
192 ldap_pvt_find_wildcard( const char *s )
193 {
194         for( ; *s; s++ ) {
195                 switch( *s ) {
196                 case '*':       /* found wildcard */
197                         return (char *) s;
198
199                 case '(':
200                 case ')':
201                         return NULL;
202
203                 case '\\':
204                         if( s[1] == '\0' ) return NULL;
205
206                         if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) {
207                                 s+=2;
208
209                         } else switch( s[1] ) {
210                         default:
211                                 return NULL;
212
213                         /* allow RFC 1960 escapes */
214                         case '*':
215                         case '(':
216                         case ')':
217                         case '\\':
218                                 s++;
219                         }
220                 }
221         }
222
223         return (char *) s;
224 }
225
226 /* unescape filter value */
227 /* support both LDAP v2 and v3 escapes */
228 /* output can include nul characters! */
229 ber_slen_t
230 ldap_pvt_filter_value_unescape( char *fval )
231 {
232         ber_slen_t r, v;
233         int v1, v2;
234
235         for( r=v=0; fval[v] != '\0'; v++ ) {
236                 switch( fval[v] ) {
237                 case '(':
238                 case ')':
239                 case '*':
240                         return -1;
241
242                 case '\\':
243                         /* escape */
244                         v++;
245
246                         if ( fval[v] == '\0' ) {
247                                 /* escape at end of string */
248                                 return -1;
249                         }
250
251                         if (( v1 = hex2value( fval[v] )) >= 0 ) {
252                                 /* LDAPv3 escape */
253                                 if (( v2 = hex2value( fval[v+1] )) < 0 ) {
254                                         /* must be two digit code */
255                                         return -1;
256                                 }
257
258                                 fval[r++] = v1 * 16 + v2;
259                                 v++;
260
261                         } else {
262                                 /* LDAPv2 escape */
263                                 switch( fval[v] ) {
264                                 case '(':
265                                 case ')':
266                                 case '*':
267                                 case '\\':
268                                         fval[r++] = fval[v];
269                                         break;
270                                 default:
271                                         /* illegal escape */
272                                         return -1;
273                                 }
274                         }
275                         break;
276
277                 default:
278                         fval[r++] = fval[v];
279                 }
280         }
281
282         fval[r] = '\0';
283         return r;
284 }
285
286 static char *
287 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
288 {
289         char    *next;
290
291         /*
292          * We have (x(filter)...) with str sitting on
293          * the x.  We have to find the paren matching
294          * the one before the x and put the intervening
295          * filters by calling put_filter_list().
296          */
297
298         /* put explicit tag */
299         if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
300                 return NULL;
301         }
302
303         str++;
304         if ( (next = find_right_paren( str )) == NULL ) {
305                 return NULL;
306         }
307
308         *next = '\0';
309         if ( put_filter_list( ber, str, tag ) == -1 ) {
310                 return NULL;
311         }
312
313         /* close the '(' */
314         *next++ = ')';
315
316         /* flush explicit tagged thang */
317         if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
318                 return NULL;
319         }
320
321         return next;
322 }
323
324 int
325 ldap_pvt_put_filter( BerElement *ber, const char *str_in )
326 {
327         int rc;
328         char    *freeme;
329         char    *str;
330         char    *next;
331         int     parens, balance, escape;
332
333         /*
334          * A Filter looks like this (RFC 4511 as extended by RFC 4526):
335          *     Filter ::= CHOICE {
336          *         and             [0]     SET SIZE (0..MAX) OF filter Filter,
337          *         or              [1]     SET SIZE (0..MAX) OF filter Filter,
338          *         not             [2]     Filter,
339          *         equalityMatch   [3]     AttributeValueAssertion,
340          *         substrings      [4]     SubstringFilter,
341          *         greaterOrEqual  [5]     AttributeValueAssertion,
342          *         lessOrEqual     [6]     AttributeValueAssertion,
343          *         present         [7]     AttributeDescription,
344          *         approxMatch     [8]     AttributeValueAssertion,
345          *         extensibleMatch [9]     MatchingRuleAssertion,
346          *         ... }
347          *
348          *     SubstringFilter ::= SEQUENCE {
349          *         type         AttributeDescription,
350          *         substrings   SEQUENCE SIZE (1..MAX) OF substring CHOICE {
351          *             initial          [0] AssertionValue, -- only once
352          *             any              [1] AssertionValue,
353          *             final            [2] AssertionValue  -- only once
354          *             }
355          *         }
356          *
357          *         MatchingRuleAssertion ::= SEQUENCE {
358          *         matchingRule    [1] MatchingRuleId OPTIONAL,
359          *         type            [2] AttributeDescription OPTIONAL,
360          *         matchValue      [3] AssertionValue,
361          *         dnAttributes    [4] BOOLEAN DEFAULT FALSE }
362          *
363          * Note: tags in a CHOICE are always explicit
364          */
365
366         Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
367
368         freeme = LDAP_STRDUP( str_in );
369         if( freeme == NULL ) return LDAP_NO_MEMORY;
370         str = freeme;
371
372         parens = 0;
373         while ( *str ) {
374                 switch ( *str ) {
375                 case '(': /*')'*/
376                         str++;
377                         parens++;
378
379                         /* skip spaces */
380                         while( LDAP_SPACE( *str ) ) str++;
381
382                         switch ( *str ) {
383                         case '&':
384                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
385                                     0, 0, 0 );
386
387                                 str = put_complex_filter( ber, str,
388                                     LDAP_FILTER_AND, 0 );
389                                 if( str == NULL ) {
390                                         rc = -1;
391                                         goto done;
392                                 }
393
394                                 parens--;
395                                 break;
396
397                         case '|':
398                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
399                                     0, 0, 0 );
400
401                                 str = put_complex_filter( ber, str,
402                                     LDAP_FILTER_OR, 0 );
403                                 if( str == NULL ) {
404                                         rc = -1;
405                                         goto done;
406                                 }
407
408                                 parens--;
409                                 break;
410
411                         case '!':
412                                 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
413                                     0, 0, 0 );
414
415                                 str = put_complex_filter( ber, str,
416                                     LDAP_FILTER_NOT, 0 );
417                                 if( str == NULL ) {
418                                         rc = -1;
419                                         goto done;
420                                 }
421
422                                 parens--;
423                                 break;
424
425                         case '(':
426                                 rc = -1;
427                                 goto done;
428
429                         default:
430                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
431                                     0, 0, 0 );
432
433                                 balance = 1;
434                                 escape = 0;
435                                 next = str;
436
437                                 while ( *next && balance ) {
438                                         if ( escape == 0 ) {
439                                                 if ( *next == '(' ) {
440                                                         balance++;
441                                                 } else if ( *next == ')' ) {
442                                                         balance--;
443                                                 }
444                                         }
445
446                                         if ( *next == '\\' && ! escape ) {
447                                                 escape = 1;
448                                         } else {
449                                                 escape = 0;
450                                         }
451
452                                         if ( balance ) next++;
453                                 }
454
455                                 if ( balance != 0 ) {
456                                         rc = -1;
457                                         goto done;
458                                 }
459
460                                 *next = '\0';
461
462                                 if ( put_simple_filter( ber, str ) == -1 ) {
463                                         rc = -1;
464                                         goto done;
465                                 }
466
467                                 *next++ = /*'('*/ ')';
468
469                                 str = next;
470                                 parens--;
471                                 break;
472                         }
473                         break;
474
475                 case /*'('*/ ')':
476                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
477                                 0, 0, 0 );
478                         if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
479                                 rc = -1;
480                                 goto done;
481                         }
482                         str++;
483                         parens--;
484                         break;
485
486                 case ' ':
487                         str++;
488                         break;
489
490                 default:        /* assume it's a simple type=value filter */
491                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
492                                 0, 0, 0 );
493                         next = strchr( str, '\0' );
494                         if ( put_simple_filter( ber, str ) == -1 ) {
495                                 rc = -1;
496                                 goto done;
497                         }
498                         str = next;
499                         break;
500                 }
501                 if ( !parens )
502                         break;
503         }
504
505         rc = ( parens || *str ) ? -1 : 0;
506
507 done:
508         LDAP_FREE( freeme );
509         return rc;
510 }
511
512 /*
513  * Put a list of filters like this "(filter1)(filter2)..."
514  */
515
516 static int
517 put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
518 {
519         char    *next = NULL;
520         char    save;
521
522         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
523                 str, 0, 0 );
524
525         while ( *str ) {
526                 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
527                         str++;
528                 }
529                 if ( *str == '\0' ) break;
530
531                 if ( (next = find_right_paren( str + 1 )) == NULL ) {
532                         return -1;
533                 }
534                 save = *++next;
535
536                 /* now we have "(filter)" with str pointing to it */
537                 *next = '\0';
538                 if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
539                 *next = save;
540                 str = next;
541
542                 if( tag == LDAP_FILTER_NOT ) break;
543         }
544
545         if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
546                 return -1;
547         }
548
549         return 0;
550 }
551
552 static int
553 put_simple_filter(
554         BerElement *ber,
555         char *str )
556 {
557         char            *s;
558         char            *value;
559         ber_tag_t       ftype;
560         int             rc = -1;
561
562         Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
563                 str, 0, 0 );
564
565         str = LDAP_STRDUP( str );
566         if( str == NULL ) return -1;
567
568         if ( (s = strchr( str, '=' )) == NULL ) {
569                 goto done;
570         }
571
572         value = s + 1;
573         *s-- = '\0';
574
575         switch ( *s ) {
576         case '<':
577                 ftype = LDAP_FILTER_LE;
578                 *s = '\0';
579                 break;
580
581         case '>':
582                 ftype = LDAP_FILTER_GE;
583                 *s = '\0';
584                 break;
585
586         case '~':
587                 ftype = LDAP_FILTER_APPROX;
588                 *s = '\0';
589                 break;
590
591         case ':':
592                 /* RFC 4515 extensible filters are off the form:
593                  *              type [:dn] [:rule] := value
594                  * or   [:dn]:rule := value             
595                  */
596                 ftype = LDAP_FILTER_EXT;
597                 *s = '\0';
598
599                 {
600                         char *dn = strchr( str, ':' );
601                         char *rule = NULL;
602
603                         if( dn != NULL ) {
604                                 *dn++ = '\0';
605                                 rule = strchr( dn, ':' );
606
607                                 if( rule == NULL ) {
608                                         /* one colon */
609                                         if ( strcasecmp(dn, "dn") == 0 ) {
610                                                 /* must have attribute */
611                                                 if( !ldap_is_desc( str ) ) {
612                                                         goto done;
613                                                 }
614
615                                                 rule = "";
616
617                                         } else {
618                                           rule = dn;
619                                           dn = NULL;
620                                         }
621                                 
622                                 } else {
623                                         /* two colons */
624                                         *rule++ = '\0';
625
626                                         if ( strcasecmp(dn, "dn") != 0 ) {
627                                                 /* must have "dn" */
628                                                 goto done;
629                                         }
630                                 }
631
632                         }
633
634                         if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
635                                 /* must have either type or rule */
636                                 goto done;
637                         }
638
639                         if ( *str != '\0' && !ldap_is_desc( str ) ) {
640                                 goto done;
641                         }
642
643                         if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
644                                 goto done;
645                         }
646
647                         rc = ber_printf( ber, "t{" /*"}"*/, ftype );
648
649                         if( rc != -1 && rule && *rule != '\0' ) {
650                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
651                         }
652
653                         if( rc != -1 && *str != '\0' ) {
654                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
655                         }
656
657                         if( rc != -1 ) {
658                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
659
660                                 if( len >= 0 ) {
661                                         rc = ber_printf( ber, "to",
662                                                 LDAP_FILTER_EXT_VALUE, value, len );
663                                 } else {
664                                         rc = -1;
665                                 }
666                         }
667
668                         if( rc != -1 && dn ) {
669                                 rc = ber_printf( ber, "tb",
670                                         LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
671                         }
672
673                         if( rc != -1 ) { 
674                                 rc = ber_printf( ber, /*"{"*/ "N}" );
675                         }
676                 }
677                 goto done;
678
679         default:
680                 if( !ldap_is_desc( str ) ) {
681                         goto done;
682
683                 } else {
684                         char *nextstar = ldap_pvt_find_wildcard( value );
685
686                         if ( nextstar == NULL ) {
687                                 goto done;
688
689                         } else if ( *nextstar == '\0' ) {
690                                 ftype = LDAP_FILTER_EQUALITY;
691
692                         } else if ( strcmp( value, "*" ) == 0 ) {
693                                 ftype = LDAP_FILTER_PRESENT;
694
695                         } else {
696                                 rc = put_substring_filter( ber, str, value );
697                                 goto done;
698                         }
699                 } break;
700         }
701
702         if( !ldap_is_desc( str ) ) goto done;
703
704         if ( ftype == LDAP_FILTER_PRESENT ) {
705                 rc = ber_printf( ber, "ts", ftype, str );
706
707         } else {
708                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
709
710                 if( len >= 0 ) {
711                         rc = ber_printf( ber, "t{soN}",
712                                 ftype, str, value, len );
713                 }
714         }
715
716 done:
717         if( rc != -1 ) rc = 0;
718         LDAP_FREE( str );
719         return rc;
720 }
721
722 static int
723 put_substring_filter( BerElement *ber, char *type, char *val )
724 {
725         char *nextstar;
726         int gotstar = 0;
727         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
728
729         Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
730                 type, val, 0 );
731
732         if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
733                 return -1;
734         }
735
736         for( ; *val; val=nextstar ) {
737                 nextstar = ldap_pvt_find_wildcard( val );
738
739                 if ( nextstar == NULL ) {
740                         return -1;
741                 }
742                 
743                 if ( *nextstar == '\0' ) {
744                         ftype = LDAP_SUBSTRING_FINAL;
745                 } else {
746                         *nextstar++ = '\0';
747                         if ( gotstar++ == 0 ) {
748                                 ftype = LDAP_SUBSTRING_INITIAL;
749                         } else {
750                                 ftype = LDAP_SUBSTRING_ANY;
751                         }
752                 }
753
754                 if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
755                         ber_slen_t len = ldap_pvt_filter_value_unescape( val );
756
757                         if ( len < 0  ) {
758                                 return -1;
759                         }
760
761                         if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
762                                 return -1;
763                         }
764                 }
765         }
766
767         if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
768                 return -1;
769         }
770
771         return 0;
772 }
773
774 static int
775 put_vrFilter( BerElement *ber, const char *str_in )
776 {
777         int rc;
778         char    *freeme;
779         char    *str;
780         char    *next;
781         int     parens, balance, escape;
782
783         /*
784          * A ValuesReturnFilter looks like this:
785          *
786          *      ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
787          *      SimpleFilterItem ::= CHOICE {
788          *              equalityMatch   [3]     AttributeValueAssertion,
789          *              substrings      [4]     SubstringFilter,
790          *              greaterOrEqual  [5]     AttributeValueAssertion,
791          *              lessOrEqual     [6]     AttributeValueAssertion,
792          *              present         [7]     AttributeType,
793          *              approxMatch     [8]     AttributeValueAssertion,
794          *              extensibleMatch [9]     SimpleMatchingAssertion -- LDAPv3
795          *      }
796          *
797          *      SubstringFilter ::= SEQUENCE {
798          *              type               AttributeType,
799          *              SEQUENCE OF CHOICE {
800          *                      initial          [0] IA5String,
801          *                      any              [1] IA5String,
802          *                      final            [2] IA5String
803          *              }
804          *      }
805          *
806          *      SimpleMatchingAssertion ::= SEQUENCE {  -- LDAPv3
807          *              matchingRule    [1] MatchingRuleId OPTIONAL,
808          *              type            [2] AttributeDescription OPTIONAL,
809          *              matchValue      [3] AssertionValue }
810          *
811          * (Source: RFC 3876)
812          */
813
814         Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
815
816         freeme = LDAP_STRDUP( str_in );
817         if( freeme == NULL ) return LDAP_NO_MEMORY;
818         str = freeme;
819
820         parens = 0;
821         while ( *str ) {
822                 switch ( *str ) {
823                 case '(': /*')'*/
824                         str++;
825                         parens++;
826
827                         /* skip spaces */
828                         while( LDAP_SPACE( *str ) ) str++;
829
830                         switch ( *str ) {
831                         case '(':
832                                 if ( (next = find_right_paren( str )) == NULL ) {
833                                         rc = -1;
834                                         goto done;
835                                 }
836
837                                 *next = '\0';
838
839                                 if ( put_vrFilter_list( ber, str ) == -1 ) {
840                                         rc = -1;
841                                         goto done;
842                                 }
843
844                                 /* close the '(' */
845                                 *next++ = ')';
846
847                                 str = next;
848
849                                 parens--;
850                                 break;
851
852
853                         default:
854                                 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
855                                     0, 0, 0 );
856
857                                 balance = 1;
858                                 escape = 0;
859                                 next = str;
860
861                                 while ( *next && balance ) {
862                                         if ( escape == 0 ) {
863                                                 if ( *next == '(' ) {
864                                                         balance++;
865                                                 } else if ( *next == ')' ) {
866                                                         balance--;
867                                                 }
868                                         }
869
870                                         if ( *next == '\\' && ! escape ) {
871                                                 escape = 1;
872                                         } else {
873                                                 escape = 0;
874                                         }
875
876                                         if ( balance ) next++;
877                                 }
878
879                                 if ( balance != 0 ) {
880                                         rc = -1;
881                                         goto done;
882                                 }
883
884                                 *next = '\0';
885
886                                 if ( put_simple_vrFilter( ber, str ) == -1 ) {
887                                         rc = -1;
888                                         goto done;
889                                 }
890
891                                 *next++ = /*'('*/ ')';
892
893                                 str = next;
894                                 parens--;
895                                 break;
896                         }
897                         break;
898
899                 case /*'('*/ ')':
900                         Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n",
901                                 0, 0, 0 );
902                         if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
903                                 rc = -1;
904                                 goto done;
905                         }
906                         str++;
907                         parens--;
908                         break;
909
910                 case ' ':
911                         str++;
912                         break;
913
914                 default:        /* assume it's a simple type=value filter */
915                         Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
916                                 0, 0, 0 );
917                         next = strchr( str, '\0' );
918                         if ( put_simple_vrFilter( ber, str ) == -1 ) {
919                                 rc = -1;
920                                 goto done;
921                         }
922                         str = next;
923                         break;
924                 }
925         }
926
927         rc = parens ? -1 : 0;
928
929 done:
930         LDAP_FREE( freeme );
931         return rc;
932 }
933
934 int
935 ldap_put_vrFilter( BerElement *ber, const char *str_in )
936 {
937         int rc =0;
938         
939         if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
940                 rc = -1;
941         }
942         
943         rc = put_vrFilter( ber, str_in );
944
945         if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
946                 rc = -1;
947         }
948         
949         return rc;
950 }
951
952 static int
953 put_vrFilter_list( BerElement *ber, char *str )
954 {
955         char    *next = NULL;
956         char    save;
957
958         Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
959                 str, 0, 0 );
960
961         while ( *str ) {
962                 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
963                         str++;
964                 }
965                 if ( *str == '\0' ) break;
966
967                 if ( (next = find_right_paren( str + 1 )) == NULL ) {
968                         return -1;
969                 }
970                 save = *++next;
971
972                 /* now we have "(filter)" with str pointing to it */
973                 *next = '\0';
974                 if ( put_vrFilter( ber, str ) == -1 ) return -1;
975                 *next = save;
976                 str = next;
977         }
978
979         return 0;
980 }
981
982 static int
983 put_simple_vrFilter(
984         BerElement *ber,
985         char *str )
986 {
987         char            *s;
988         char            *value;
989         ber_tag_t       ftype;
990         int             rc = -1;
991
992         Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
993                 str, 0, 0 );
994
995         str = LDAP_STRDUP( str );
996         if( str == NULL ) return -1;
997
998         if ( (s = strchr( str, '=' )) == NULL ) {
999                 goto done;
1000         }
1001
1002         value = s + 1;
1003         *s-- = '\0';
1004
1005         switch ( *s ) {
1006         case '<':
1007                 ftype = LDAP_FILTER_LE;
1008                 *s = '\0';
1009                 break;
1010
1011         case '>':
1012                 ftype = LDAP_FILTER_GE;
1013                 *s = '\0';
1014                 break;
1015
1016         case '~':
1017                 ftype = LDAP_FILTER_APPROX;
1018                 *s = '\0';
1019                 break;
1020
1021         case ':':
1022                 /* According to ValuesReturnFilter control definition
1023                  * extensible filters are off the form:
1024                  *              type [:rule] := value
1025                  * or   :rule := value          
1026                  */
1027                 ftype = LDAP_FILTER_EXT;
1028                 *s = '\0';
1029
1030                 {
1031                         char *rule = strchr( str, ':' );
1032
1033                         if( rule == NULL ) {
1034                                 /* must have attribute */
1035                                 if( !ldap_is_desc( str ) ) {
1036                                         goto done;
1037                                 }
1038                                 rule = "";
1039                         } else {
1040                                 *rule++ = '\0';
1041                         }
1042
1043                         if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
1044                                 /* must have either type or rule */
1045                                 goto done;
1046                         }
1047
1048                         if ( *str != '\0' && !ldap_is_desc( str ) ) {
1049                                 goto done;
1050                         }
1051
1052                         if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
1053                                 goto done;
1054                         }
1055
1056                         rc = ber_printf( ber, "t{" /*"}"*/, ftype );
1057
1058                         if( rc != -1 && rule && *rule != '\0' ) {
1059                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
1060                         }
1061
1062                         if( rc != -1 && *str != '\0' ) {
1063                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
1064                         }
1065
1066                         if( rc != -1 ) {
1067                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1068
1069                                 if( len >= 0 ) {
1070                                         rc = ber_printf( ber, "to",
1071                                                 LDAP_FILTER_EXT_VALUE, value, len );
1072                                 } else {
1073                                         rc = -1;
1074                                 }
1075                         }
1076
1077                         if( rc != -1 ) { 
1078                                 rc = ber_printf( ber, /*"{"*/ "N}" );
1079                         }
1080                 }
1081                 goto done;
1082
1083         default:
1084                 if( !ldap_is_desc( str ) ) {
1085                         goto done;
1086
1087                 } else {
1088                         char *nextstar = ldap_pvt_find_wildcard( value );
1089
1090                         if ( nextstar == NULL ) {
1091                                 goto done;
1092
1093                         } else if ( *nextstar == '\0' ) {
1094                                 ftype = LDAP_FILTER_EQUALITY;
1095
1096                         } else if ( strcmp( value, "*" ) == 0 ) {
1097                                 ftype = LDAP_FILTER_PRESENT;
1098
1099                         } else {
1100                                 rc = put_substring_filter( ber, str, value );
1101                                 goto done;
1102                         }
1103                 } break;
1104         }
1105
1106         if( !ldap_is_desc( str ) ) goto done;
1107
1108         if ( ftype == LDAP_FILTER_PRESENT ) {
1109                 rc = ber_printf( ber, "ts", ftype, str );
1110
1111         } else {
1112                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1113
1114                 if( len >= 0 ) {
1115                         rc = ber_printf( ber, "t{soN}",
1116                                 ftype, str, value, len );
1117                 }
1118         }
1119
1120 done:
1121         if( rc != -1 ) rc = 0;
1122         LDAP_FREE( str );
1123         return rc;
1124 }
1125