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