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