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