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