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