]> git.sur5r.net Git - openldap/blob - libraries/libldap/filter.c
7d40d1ceb472588ee886da4c89df09f19c7a18b5
[openldap] / libraries / libldap / filter.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 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", LDAP_LEVEL_ARGS, "ldap_pvt_put_filter: \"%s\"\n",
361                 str_in ));
362 #else
363         Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
364 #endif
365
366         freeme = LDAP_STRDUP( str_in );
367         if( freeme == NULL ) return LDAP_NO_MEMORY;
368         str = freeme;
369
370         parens = 0;
371         while ( *str ) {
372                 switch ( *str ) {
373                 case '(': /*')'*/
374                         str++;
375                         parens++;
376
377                         /* skip spaces */
378                         while( LDAP_SPACE( *str ) ) str++;
379
380                         switch ( *str ) {
381                         case '&':
382 #ifdef NEW_LOGGING
383                                 LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
384                                         "ldap_pvt_put_filter: AND\n" ));
385 #else
386                                 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
387                                     0, 0, 0 );
388 #endif
389
390                                 str = put_complex_filter( ber, str,
391                                     LDAP_FILTER_AND, 0 );
392                                 if( str == NULL ) {
393                                         rc = -1;
394                                         goto done;
395                                 }
396
397                                 parens--;
398                                 break;
399
400                         case '|':
401 #ifdef NEW_LOGGING
402                                 LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
403                                         "ldap_pvt_put_filter: OR\n" ));
404 #else
405                                 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
406                                     0, 0, 0 );
407 #endif
408
409                                 str = put_complex_filter( ber, str,
410                                     LDAP_FILTER_OR, 0 );
411                                 if( str == NULL ) {
412                                         rc = -1;
413                                         goto done;
414                                 }
415
416                                 parens--;
417                                 break;
418
419                         case '!':
420 #ifdef NEW_LOGGING
421                                 LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
422                                         "ldap_pvt_put_filter: NOT\n" ));
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", LDAP_LEVEL_DETAIL1, 
441                                         "ldap_pvt_put_filter: simple\n" ));
442 #else
443                                 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
444                                     0, 0, 0 );
445 #endif
446
447                                 balance = 1;
448                                 escape = 0;
449                                 next = str;
450
451                                 while ( *next && balance ) {
452                                         if ( escape == 0 ) {
453                                                 if ( *next == '(' ) {
454                                                         balance++;
455                                                 } else if ( *next == ')' ) {
456                                                         balance--;
457                                                 }
458                                         }
459
460                                         if ( *next == '\\' && ! escape ) {
461                                                 escape = 1;
462                                         } else {
463                                                 escape = 0;
464                                         }
465
466                                         if ( balance ) next++;
467                                 }
468
469                                 if ( balance != 0 ) {
470                                         rc = -1;
471                                         goto done;
472                                 }
473
474                                 *next = '\0';
475
476                                 if ( put_simple_filter( ber, str ) == -1 ) {
477                                         rc = -1;
478                                         goto done;
479                                 }
480
481                                 *next++ = /*'('*/ ')';
482
483                                 str = next;
484                                 parens--;
485                                 break;
486                         }
487                         break;
488
489                 case /*'('*/ ')':
490 #ifdef NEW_LOGGING
491                         LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
492                                 "ldap_pvt_put_filter: end\n" ));
493 #else
494                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
495                                 0, 0, 0 );
496 #endif
497                         if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
498                                 rc = -1;
499                                 goto done;
500                         }
501                         str++;
502                         parens--;
503                         break;
504
505                 case ' ':
506                         str++;
507                         break;
508
509                 default:        /* assume it's a simple type=value filter */
510 #ifdef NEW_LOGGING
511                         LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
512                                 "ldap_pvt_put_filter: default\n" ));
513 #else
514                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
515                                 0, 0, 0 );
516 #endif
517                         next = strchr( str, '\0' );
518                         if ( put_simple_filter( ber, str ) == -1 ) {
519                                 rc = -1;
520                                 goto done;
521                         }
522                         str = next;
523                         break;
524                 }
525         }
526
527         rc = parens ? -1 : 0;
528
529 done:
530         LDAP_FREE( freeme );
531         return rc;
532 }
533
534 /*
535  * Put a list of filters like this "(filter1)(filter2)..."
536  */
537
538 static int
539 put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
540 {
541         char    *next = NULL;
542         char    save;
543
544 #ifdef NEW_LOGGING
545         LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
546                                 "put_filter_list \"%s\"\n", str ));
547 #else
548         Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
549                 str, 0, 0 );
550 #endif
551
552         while ( *str ) {
553                 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
554                         str++;
555                 }
556                 if ( *str == '\0' ) break;
557
558                 if ( (next = find_right_paren( str + 1 )) == NULL ) {
559                         return -1;
560                 }
561                 save = *++next;
562
563                 /* now we have "(filter)" with str pointing to it */
564                 *next = '\0';
565                 if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
566                 *next = save;
567                 str = next;
568
569                 if( tag == LDAP_FILTER_NOT ) break;
570         }
571
572         if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
573                 return -1;
574         }
575
576         return 0;
577 }
578
579 static int
580 put_simple_filter(
581         BerElement *ber,
582         char *str )
583 {
584         char            *s;
585         char            *value;
586         ber_tag_t       ftype;
587         int             rc = -1;
588
589 #ifdef NEW_LOGGING
590         LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
591                                 "put_simple_filter: \"%s\"\n", str ));
592 #else
593         Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
594                 str, 0, 0 );
595 #endif
596
597         str = LDAP_STRDUP( str );
598         if( str == NULL ) return -1;
599
600         if ( (s = strchr( str, '=' )) == NULL ) {
601                 goto done;
602         }
603
604         value = s + 1;
605         *s-- = '\0';
606
607         switch ( *s ) {
608         case '<':
609                 ftype = LDAP_FILTER_LE;
610                 *s = '\0';
611                 break;
612
613         case '>':
614                 ftype = LDAP_FILTER_GE;
615                 *s = '\0';
616                 break;
617
618         case '~':
619                 ftype = LDAP_FILTER_APPROX;
620                 *s = '\0';
621                 break;
622
623         case ':':
624                 /* RFC2254 extensible filters are off the form:
625                  *              type [:dn] [:rule] := value
626                  * or   [:dn]:rule := value             
627                  */
628                 ftype = LDAP_FILTER_EXT;
629                 *s = '\0';
630
631                 {
632                         char *dn = strchr( str, ':' );
633                         char *rule = NULL;
634
635                         if( dn != NULL ) {
636                                 *dn++ = '\0';
637                                 rule = strchr( dn, ':' );
638
639                                 if( rule == NULL ) {
640                                         /* one colon */
641                                         if ( strcmp(dn, "dn") == 0 ) {
642                                                 /* must have attribute */
643                                                 if( !ldap_is_desc( str ) ) {
644                                                         goto done;
645                                                 }
646
647                                                 rule = "";
648
649                                         } else {
650                                           rule = dn;
651                                           dn = NULL;
652                                         }
653                                 
654                                 } else {
655                                         /* two colons */
656                                         *rule++ = '\0';
657
658                                         if ( strcmp(dn, "dn") != 0 ) {
659                                                 /* must have "dn" */
660                                                 goto done;
661                                         }
662                                 }
663
664                         }
665
666                         if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
667                                 /* must have either type or rule */
668                                 goto done;
669                         }
670
671                         if ( *str != '\0' && !ldap_is_desc( str ) ) {
672                                 goto done;
673                         }
674
675                         if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
676                                 goto done;
677                         }
678
679                         rc = ber_printf( ber, "t{" /*"}"*/, ftype );
680
681                         if( rc != -1 && rule && *rule != '\0' ) {
682                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
683                         }
684
685                         if( rc != -1 && *str != '\0' ) {
686                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
687                         }
688
689                         if( rc != -1 ) {
690                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
691
692                                 if( len >= 0 ) {
693                                         rc = ber_printf( ber, "to",
694                                                 LDAP_FILTER_EXT_VALUE, value, len );
695                                 } else {
696                                         rc = -1;
697                                 }
698                         }
699
700                         if( rc != -1 && dn ) {
701                                 rc = ber_printf( ber, "tb",
702                                         LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
703                         }
704
705                         if( rc != -1 ) { 
706                                 rc = ber_printf( ber, /*"{"*/ "N}" );
707                         }
708                 }
709                 goto done;
710
711         default:
712                 if( !ldap_is_desc( str ) ) {
713                         goto done;
714
715                 } else {
716                         char *nextstar = ldap_pvt_find_wildcard( value );
717
718                         if ( nextstar == NULL ) {
719                                 goto done;
720
721                         } else if ( *nextstar == '\0' ) {
722                                 ftype = LDAP_FILTER_EQUALITY;
723
724                         } else if ( strcmp( value, "*" ) == 0 ) {
725                                 ftype = LDAP_FILTER_PRESENT;
726
727                         } else {
728                                 rc = put_substring_filter( ber, str, value );
729                                 goto done;
730                         }
731                 } break;
732         }
733
734         if( !ldap_is_desc( str ) ) goto done;
735
736         if ( ftype == LDAP_FILTER_PRESENT ) {
737                 rc = ber_printf( ber, "ts", ftype, str );
738
739         } else {
740                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
741
742                 if( len >= 0 ) {
743                         rc = ber_printf( ber, "t{soN}",
744                                 ftype, str, value, len );
745                 }
746         }
747
748 done:
749         if( rc != -1 ) rc = 0;
750         LDAP_FREE( str );
751         return rc;
752 }
753
754 static int
755 put_substring_filter( BerElement *ber, char *type, char *val )
756 {
757         char *nextstar;
758         int gotstar = 0;
759         ber_tag_t       ftype = LDAP_FILTER_SUBSTRINGS;
760
761 #ifdef NEW_LOGGING
762         LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
763                                 "put_substring_filter \"%s=%s\"\n", type, val ));
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 int
812 ldap_pvt_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", LDAP_LEVEL_ARGS, "ldap_pvt_put_vrFilter: \"%s\"\n",
851                 str_in ));
852 #else
853         Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
854 #endif
855
856         freeme = LDAP_STRDUP( str_in );
857         if( freeme == NULL ) return LDAP_NO_MEMORY;
858         str = freeme;
859
860         parens = 0;
861         while ( *str ) {
862                 switch ( *str ) {
863                 case '(': /*')'*/
864                         str++;
865                         parens++;
866
867                         /* skip spaces */
868                         while( LDAP_SPACE( *str ) ) str++;
869
870                         switch ( *str ) {
871                         case '(':
872                                 if ( (next = find_right_paren( str )) == NULL ) {
873                                         rc = -1;
874                                         goto done;
875                                 }
876
877                                 *next = '\0';
878
879                                 if ( put_vrFilter_list( ber, str ) == -1 ) {
880                                         rc = -1;
881                                         goto done;
882                                 }
883
884                                 /* close the '(' */
885                                 *next++ = ')';
886
887                                 str = next;
888
889                                 parens--;
890                                 break;
891
892
893                         default:
894 #ifdef NEW_LOGGING
895                                 LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
896                                         "ldap_pvt_put_vrFilter: simple\n" ));
897 #else
898                                 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
899                                     0, 0, 0 );
900 #endif
901
902                                 balance = 1;
903                                 escape = 0;
904                                 next = str;
905
906                                 while ( *next && balance ) {
907                                         if ( escape == 0 ) {
908                                                 if ( *next == '(' ) {
909                                                         balance++;
910                                                 } else if ( *next == ')' ) {
911                                                         balance--;
912                                                 }
913                                         }
914
915                                         if ( *next == '\\' && ! escape ) {
916                                                 escape = 1;
917                                         } else {
918                                                 escape = 0;
919                                         }
920
921                                         if ( balance ) next++;
922                                 }
923
924                                 if ( balance != 0 ) {
925                                         rc = -1;
926                                         goto done;
927                                 }
928
929                                 *next = '\0';
930
931                                 if ( put_simple_vrFilter( ber, str ) == -1 ) {
932                                         rc = -1;
933                                         goto done;
934                                 }
935
936                                 *next++ = /*'('*/ ')';
937
938                                 str = next;
939                                 parens--;
940                                 break;
941                         }
942                         break;
943
944                 case /*'('*/ ')':
945 #ifdef NEW_LOGGING
946                         LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
947                                 "ldap_pvt_put_filter: end\n" ));
948 #else
949                         Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
950                                 0, 0, 0 );
951 #endif
952                         if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
953                                 rc = -1;
954                                 goto done;
955                         }
956                         str++;
957                         parens--;
958                         break;
959
960                 case ' ':
961                         str++;
962                         break;
963
964                 default:        /* assume it's a simple type=value filter */
965 #ifdef NEW_LOGGING
966                         LDAP_LOG (( "filter", LDAP_LEVEL_DETAIL1, 
967                                 "ldap_pvt_put_filter: default\n" ));
968 #else
969                         Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
970                                 0, 0, 0 );
971 #endif
972                         next = strchr( str, '\0' );
973                         if ( put_simple_filter( ber, str ) == -1 ) {
974                                 rc = -1;
975                                 goto done;
976                         }
977                         str = next;
978                         break;
979                 }
980         }
981
982         rc = parens ? -1 : 0;
983
984 done:
985         LDAP_FREE( freeme );
986         return rc;
987 }
988
989 int
990 put_vrFilter( BerElement *ber, const char *str_in )
991 {
992         int rc =0;
993         
994         if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
995                 rc = -1;
996         }
997         
998         rc = ldap_pvt_put_vrFilter( ber, str_in );
999
1000         if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
1001                 rc = -1;
1002         }
1003         
1004         return rc;
1005 }
1006
1007 static int
1008 put_vrFilter_list( BerElement *ber, char *str )
1009 {
1010         char    *next = NULL;
1011         char    save;
1012
1013 #ifdef NEW_LOGGING
1014         LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
1015                 "put_vrFilter_list \"%s\"\n", str ));
1016 #else
1017         Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
1018                 str, 0, 0 );
1019 #endif
1020
1021         while ( *str ) {
1022                 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
1023                         str++;
1024                 }
1025                 if ( *str == '\0' ) break;
1026
1027                 if ( (next = find_right_paren( str + 1 )) == NULL ) {
1028                         return -1;
1029                 }
1030                 save = *++next;
1031
1032                 /* now we have "(filter)" with str pointing to it */
1033                 *next = '\0';
1034                 if ( ldap_pvt_put_vrFilter( ber, str ) == -1 ) return -1;
1035                 *next = save;
1036                 str = next;
1037         }
1038
1039         return 0;
1040 }
1041
1042 static int
1043 put_simple_vrFilter(
1044         BerElement *ber,
1045         char *str )
1046 {
1047         char            *s;
1048         char            *value;
1049         ber_tag_t       ftype;
1050         int             rc = -1;
1051
1052 #ifdef NEW_LOGGING
1053         LDAP_LOG (( "filter", LDAP_LEVEL_ARGS, 
1054                 "put_simple_vrFilter: \"%s\"\n", str ));
1055 #else
1056         Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
1057                 str, 0, 0 );
1058 #endif
1059
1060         str = LDAP_STRDUP( str );
1061         if( str == NULL ) return -1;
1062
1063         if ( (s = strchr( str, '=' )) == NULL ) {
1064                 goto done;
1065         }
1066
1067         value = s + 1;
1068         *s-- = '\0';
1069
1070         switch ( *s ) {
1071         case '<':
1072                 ftype = LDAP_FILTER_LE;
1073                 *s = '\0';
1074                 break;
1075
1076         case '>':
1077                 ftype = LDAP_FILTER_GE;
1078                 *s = '\0';
1079                 break;
1080
1081         case '~':
1082                 ftype = LDAP_FILTER_APPROX;
1083                 *s = '\0';
1084                 break;
1085
1086         case ':':
1087                 /* According to ValuesReturnFilter control definition
1088                  * extensible filters are off the form:
1089                  *              type [:rule] := value
1090                  * or   :rule := value          
1091                  */
1092                 ftype = LDAP_FILTER_EXT;
1093                 *s = '\0';
1094
1095                 {
1096                         char *rule = strchr( str, ':' );
1097                         *rule++ = '\0';
1098
1099                         if( rule == NULL ) {
1100                                 /* must have attribute */
1101                                 if( !ldap_is_desc( str ) ) {
1102                                         goto done;
1103                                 }
1104                                 rule = "";
1105                         
1106                         } else {
1107                                 *rule++ = '\0';
1108                         }
1109
1110
1111                         if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
1112                                 /* must have either type or rule */
1113                                 goto done;
1114                         }
1115
1116                         if ( *str != '\0' && !ldap_is_desc( str ) ) {
1117                                 goto done;
1118                         }
1119
1120                         if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
1121                                 goto done;
1122                         }
1123
1124                         rc = ber_printf( ber, "t{" /*"}"*/, ftype );
1125
1126                         if( rc != -1 && rule && *rule != '\0' ) {
1127                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
1128                         }
1129
1130                         if( rc != -1 && *str != '\0' ) {
1131                                 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
1132                         }
1133
1134                         if( rc != -1 ) {
1135                                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1136
1137                                 if( len >= 0 ) {
1138                                         rc = ber_printf( ber, "to",
1139                                                 LDAP_FILTER_EXT_VALUE, value, len );
1140                                 } else {
1141                                         rc = -1;
1142                                 }
1143                         }
1144
1145                         if( rc != -1 ) { 
1146                                 rc = ber_printf( ber, /*"{"*/ "N}" );
1147                         }
1148                 }
1149                 goto done;
1150
1151         default:
1152                 if( !ldap_is_desc( str ) ) {
1153                         goto done;
1154
1155                 } else {
1156                         char *nextstar = ldap_pvt_find_wildcard( value );
1157
1158                         if ( nextstar == NULL ) {
1159                                 goto done;
1160
1161                         } else if ( *nextstar == '\0' ) {
1162                                 ftype = LDAP_FILTER_EQUALITY;
1163
1164                         } else if ( strcmp( value, "*" ) == 0 ) {
1165                                 ftype = LDAP_FILTER_PRESENT;
1166
1167                         } else {
1168                                 rc = put_substring_filter( ber, str, value );
1169                                 goto done;
1170                         }
1171                 } break;
1172         }
1173
1174         if( !ldap_is_desc( str ) ) goto done;
1175
1176         if ( ftype == LDAP_FILTER_PRESENT ) {
1177                 rc = ber_printf( ber, "ts", ftype, str );
1178
1179         } else {
1180                 ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1181
1182                 if( len >= 0 ) {
1183                         rc = ber_printf( ber, "t{soN}",
1184                                 ftype, str, value, len );
1185                 }
1186         }
1187
1188 done:
1189         if( rc != -1 ) rc = 0;
1190         LDAP_FREE( str );
1191         return rc;
1192 }
1193