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