filter.c revision 1.1.1.4
1/*	$NetBSD: filter.c,v 1.1.1.4 2014/05/28 09:58:41 tron Exp $	*/
2
3/* search.c */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2014 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* Portions Copyright (c) 1990 Regents of the University of Michigan.
19 * All rights reserved.
20 */
21
22#include "portable.h"
23
24#include <stdio.h>
25
26#include <ac/stdlib.h>
27
28#include <ac/socket.h>
29#include <ac/string.h>
30#include <ac/time.h>
31
32#include "ldap-int.h"
33
34static int put_simple_vrFilter LDAP_P((
35	BerElement *ber,
36	char *str ));
37
38static int put_vrFilter_list LDAP_P((
39	BerElement *ber,
40	char *str ));
41
42static char *put_complex_filter LDAP_P((
43	BerElement *ber,
44	char *str,
45	ber_tag_t tag,
46	int not ));
47
48static int put_simple_filter LDAP_P((
49	BerElement *ber,
50	char *str ));
51
52static int put_substring_filter LDAP_P((
53	BerElement *ber,
54	char *type,
55	char *str,
56	char *nextstar ));
57
58static int put_filter_list LDAP_P((
59	BerElement *ber,
60	char *str,
61	ber_tag_t tag ));
62
63static 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
95static 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
137options:
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
153static char *
154find_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
177static 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
194char *
195ldap_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! */
232ber_slen_t
233ldap_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
289static char *
290put_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
327int
328ldap_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 (RFC 4511 as extended by RFC 4526):
338	 *     Filter ::= CHOICE {
339	 *         and             [0]     SET SIZE (0..MAX) OF filter Filter,
340	 *         or              [1]     SET SIZE (0..MAX) OF filter Filter,
341	 *         not             [2]     Filter,
342	 *         equalityMatch   [3]     AttributeValueAssertion,
343	 *         substrings      [4]     SubstringFilter,
344	 *         greaterOrEqual  [5]     AttributeValueAssertion,
345	 *         lessOrEqual     [6]     AttributeValueAssertion,
346	 *         present         [7]     AttributeDescription,
347	 *         approxMatch     [8]     AttributeValueAssertion,
348	 *         extensibleMatch [9]     MatchingRuleAssertion,
349	 *         ... }
350	 *
351	 *     SubstringFilter ::= SEQUENCE {
352	 *         type         AttributeDescription,
353	 *         substrings   SEQUENCE SIZE (1..MAX) OF substring CHOICE {
354	 *             initial          [0] AssertionValue, -- only once
355	 *             any              [1] AssertionValue,
356	 *             final            [2] AssertionValue  -- only once
357	 *             }
358	 *         }
359	 *
360	 *	   MatchingRuleAssertion ::= SEQUENCE {
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	Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
370
371	freeme = LDAP_STRDUP( str_in );
372	if( freeme == NULL ) return LDAP_NO_MEMORY;
373	str = freeme;
374
375	parens = 0;
376	while ( *str ) {
377		switch ( *str ) {
378		case '(': /*')'*/
379			str++;
380			parens++;
381
382			/* skip spaces */
383			while( LDAP_SPACE( *str ) ) str++;
384
385			switch ( *str ) {
386			case '&':
387				Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
388				    0, 0, 0 );
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				Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
402				    0, 0, 0 );
403
404				str = put_complex_filter( ber, str,
405				    LDAP_FILTER_OR, 0 );
406				if( str == NULL ) {
407					rc = -1;
408					goto done;
409				}
410
411				parens--;
412				break;
413
414			case '!':
415				Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
416				    0, 0, 0 );
417
418				str = put_complex_filter( ber, str,
419				    LDAP_FILTER_NOT, 0 );
420				if( str == NULL ) {
421					rc = -1;
422					goto done;
423				}
424
425				parens--;
426				break;
427
428			case '(':
429				rc = -1;
430				goto done;
431
432			default:
433				Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
434				    0, 0, 0 );
435
436				balance = 1;
437				escape = 0;
438				next = str;
439
440				while ( *next && balance ) {
441					if ( escape == 0 ) {
442						if ( *next == '(' ) {
443							balance++;
444						} else if ( *next == ')' ) {
445							balance--;
446						}
447					}
448
449					if ( *next == '\\' && ! escape ) {
450						escape = 1;
451					} else {
452						escape = 0;
453					}
454
455					if ( balance ) next++;
456				}
457
458				if ( balance != 0 ) {
459					rc = -1;
460					goto done;
461				}
462
463				*next = '\0';
464
465				if ( put_simple_filter( ber, str ) == -1 ) {
466					rc = -1;
467					goto done;
468				}
469
470				*next++ = /*'('*/ ')';
471
472				str = next;
473				parens--;
474				break;
475			}
476			break;
477
478		case /*'('*/ ')':
479			Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
480				0, 0, 0 );
481			if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
482				rc = -1;
483				goto done;
484			}
485			str++;
486			parens--;
487			break;
488
489		case ' ':
490			str++;
491			break;
492
493		default:	/* assume it's a simple type=value filter */
494			Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
495				0, 0, 0 );
496			next = strchr( str, '\0' );
497			if ( put_simple_filter( ber, str ) == -1 ) {
498				rc = -1;
499				goto done;
500			}
501			str = next;
502			break;
503		}
504		if ( !parens )
505			break;
506	}
507
508	rc = ( parens || *str ) ? -1 : 0;
509
510done:
511	LDAP_FREE( freeme );
512	return rc;
513}
514
515/*
516 * Put a list of filters like this "(filter1)(filter2)..."
517 */
518
519static int
520put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
521{
522	char	*next = NULL;
523	char	save;
524
525	Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
526		str, 0, 0 );
527
528	while ( *str ) {
529		while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
530			str++;
531		}
532		if ( *str == '\0' ) break;
533
534		if ( (next = find_right_paren( str + 1 )) == NULL ) {
535			return -1;
536		}
537		save = *++next;
538
539		/* now we have "(filter)" with str pointing to it */
540		*next = '\0';
541		if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
542		*next = save;
543		str = next;
544
545		if( tag == LDAP_FILTER_NOT ) break;
546	}
547
548	if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
549		return -1;
550	}
551
552	return 0;
553}
554
555static int
556put_simple_filter(
557	BerElement *ber,
558	char *str )
559{
560	char		*s;
561	char		*value;
562	ber_tag_t	ftype;
563	int		rc = -1;
564
565	Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
566		str, 0, 0 );
567
568	str = LDAP_STRDUP( str );
569	if( str == NULL ) return -1;
570
571	if ( (s = strchr( str, '=' )) == NULL ) {
572		goto done;
573	}
574
575	value = s + 1;
576	*s-- = '\0';
577
578	switch ( *s ) {
579	case '<':
580		ftype = LDAP_FILTER_LE;
581		*s = '\0';
582		break;
583
584	case '>':
585		ftype = LDAP_FILTER_GE;
586		*s = '\0';
587		break;
588
589	case '~':
590		ftype = LDAP_FILTER_APPROX;
591		*s = '\0';
592		break;
593
594	case ':':
595		/* RFC 4515 extensible filters are off the form:
596		 *		type [:dn] [:rule] := value
597		 * or	[:dn]:rule := value
598		 */
599		ftype = LDAP_FILTER_EXT;
600		*s = '\0';
601
602		{
603			char *dn = strchr( str, ':' );
604			char *rule = NULL;
605
606			if( dn != NULL ) {
607				*dn++ = '\0';
608				rule = strchr( dn, ':' );
609
610				if( rule == NULL ) {
611					/* one colon */
612					if ( strcasecmp(dn, "dn") == 0 ) {
613						/* must have attribute */
614						if( !ldap_is_desc( str ) ) {
615							goto done;
616						}
617
618						rule = "";
619
620					} else {
621					  rule = dn;
622					  dn = NULL;
623					}
624
625				} else {
626					/* two colons */
627					*rule++ = '\0';
628
629					if ( strcasecmp(dn, "dn") != 0 ) {
630						/* must have "dn" */
631						goto done;
632					}
633				}
634
635			}
636
637			if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
638				/* must have either type or rule */
639				goto done;
640			}
641
642			if ( *str != '\0' && !ldap_is_desc( str ) ) {
643				goto done;
644			}
645
646			if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
647				goto done;
648			}
649
650			rc = ber_printf( ber, "t{" /*"}"*/, ftype );
651
652			if( rc != -1 && rule && *rule != '\0' ) {
653				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
654			}
655
656			if( rc != -1 && *str != '\0' ) {
657				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
658			}
659
660			if( rc != -1 ) {
661				ber_slen_t len = ldap_pvt_filter_value_unescape( value );
662
663				if( len >= 0 ) {
664					rc = ber_printf( ber, "to",
665						LDAP_FILTER_EXT_VALUE, value, len );
666				} else {
667					rc = -1;
668				}
669			}
670
671			if( rc != -1 && dn ) {
672				rc = ber_printf( ber, "tb",
673					LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
674			}
675
676			if( rc != -1 ) {
677				rc = ber_printf( ber, /*"{"*/ "N}" );
678			}
679		}
680		goto done;
681
682	default:
683		if( !ldap_is_desc( str ) ) {
684			goto done;
685
686		} else {
687			char *nextstar = ldap_pvt_find_wildcard( value );
688
689			if ( nextstar == NULL ) {
690				goto done;
691
692			} else if ( *nextstar == '\0' ) {
693				ftype = LDAP_FILTER_EQUALITY;
694
695			} else if ( strcmp( value, "*" ) == 0 ) {
696				ftype = LDAP_FILTER_PRESENT;
697
698			} else {
699				rc = put_substring_filter( ber, str, value, nextstar );
700				goto done;
701			}
702		} break;
703	}
704
705	if( !ldap_is_desc( str ) ) goto done;
706
707	if ( ftype == LDAP_FILTER_PRESENT ) {
708		rc = ber_printf( ber, "ts", ftype, str );
709
710	} else {
711		ber_slen_t len = ldap_pvt_filter_value_unescape( value );
712
713		if( len >= 0 ) {
714			rc = ber_printf( ber, "t{soN}",
715				ftype, str, value, len );
716		}
717	}
718
719done:
720	if( rc != -1 ) rc = 0;
721	LDAP_FREE( str );
722	return rc;
723}
724
725static int
726put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar )
727{
728	int gotstar = 0;
729	ber_tag_t	ftype = LDAP_FILTER_SUBSTRINGS;
730
731	Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
732		type, val, 0 );
733
734	if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
735		return -1;
736	}
737
738	for( ; *val; val=nextstar ) {
739		if ( gotstar )
740			nextstar = ldap_pvt_find_wildcard( val );
741
742		if ( nextstar == NULL ) {
743			return -1;
744		}
745
746		if ( *nextstar == '\0' ) {
747			ftype = LDAP_SUBSTRING_FINAL;
748		} else {
749			*nextstar++ = '\0';
750			if ( gotstar++ == 0 ) {
751				ftype = LDAP_SUBSTRING_INITIAL;
752			} else {
753				ftype = LDAP_SUBSTRING_ANY;
754			}
755		}
756
757		if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
758			ber_slen_t len = ldap_pvt_filter_value_unescape( val );
759
760			if ( len <= 0  ) {
761				return -1;
762			}
763
764			if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
765				return -1;
766			}
767		}
768	}
769
770	if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
771		return -1;
772	}
773
774	return 0;
775}
776
777static int
778put_vrFilter( BerElement *ber, const char *str_in )
779{
780	int rc;
781	char	*freeme;
782	char	*str;
783	char	*next;
784	int	parens, balance, escape;
785
786	/*
787	 * A ValuesReturnFilter looks like this:
788	 *
789	 *	ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
790	 *      SimpleFilterItem ::= CHOICE {
791	 *              equalityMatch   [3]     AttributeValueAssertion,
792	 *              substrings      [4]     SubstringFilter,
793	 *              greaterOrEqual  [5]     AttributeValueAssertion,
794	 *              lessOrEqual     [6]     AttributeValueAssertion,
795	 *              present         [7]     AttributeType,
796	 *              approxMatch     [8]     AttributeValueAssertion,
797	 *		extensibleMatch [9]	SimpleMatchingAssertion -- LDAPv3
798	 *      }
799	 *
800	 *      SubstringFilter ::= SEQUENCE {
801	 *              type               AttributeType,
802	 *              SEQUENCE OF CHOICE {
803	 *                      initial          [0] IA5String,
804	 *                      any              [1] IA5String,
805	 *                      final            [2] IA5String
806	 *              }
807	 *      }
808	 *
809	 *	SimpleMatchingAssertion ::= SEQUENCE {	-- LDAPv3
810	 *		matchingRule    [1] MatchingRuleId OPTIONAL,
811	 *		type            [2] AttributeDescription OPTIONAL,
812	 *		matchValue      [3] AssertionValue }
813	 *
814	 * (Source: RFC 3876)
815	 */
816
817	Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
818
819	freeme = LDAP_STRDUP( str_in );
820	if( freeme == NULL ) return LDAP_NO_MEMORY;
821	str = freeme;
822
823	parens = 0;
824	while ( *str ) {
825		switch ( *str ) {
826		case '(': /*')'*/
827			str++;
828			parens++;
829
830			/* skip spaces */
831			while( LDAP_SPACE( *str ) ) str++;
832
833			switch ( *str ) {
834			case '(':
835				if ( (next = find_right_paren( str )) == NULL ) {
836					rc = -1;
837					goto done;
838				}
839
840				*next = '\0';
841
842				if ( put_vrFilter_list( ber, str ) == -1 ) {
843					rc = -1;
844					goto done;
845				}
846
847				/* close the '(' */
848				*next++ = ')';
849
850				str = next;
851
852				parens--;
853				break;
854
855
856			default:
857				Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
858				    0, 0, 0 );
859
860				balance = 1;
861				escape = 0;
862				next = str;
863
864				while ( *next && balance ) {
865					if ( escape == 0 ) {
866						if ( *next == '(' ) {
867							balance++;
868						} else if ( *next == ')' ) {
869							balance--;
870						}
871					}
872
873					if ( *next == '\\' && ! escape ) {
874						escape = 1;
875					} else {
876						escape = 0;
877					}
878
879					if ( balance ) next++;
880				}
881
882				if ( balance != 0 ) {
883					rc = -1;
884					goto done;
885				}
886
887				*next = '\0';
888
889				if ( put_simple_vrFilter( ber, str ) == -1 ) {
890					rc = -1;
891					goto done;
892				}
893
894				*next++ = /*'('*/ ')';
895
896				str = next;
897				parens--;
898				break;
899			}
900			break;
901
902		case /*'('*/ ')':
903			Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n",
904				0, 0, 0 );
905			if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
906				rc = -1;
907				goto done;
908			}
909			str++;
910			parens--;
911			break;
912
913		case ' ':
914			str++;
915			break;
916
917		default:	/* assume it's a simple type=value filter */
918			Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
919				0, 0, 0 );
920			next = strchr( str, '\0' );
921			if ( put_simple_vrFilter( ber, str ) == -1 ) {
922				rc = -1;
923				goto done;
924			}
925			str = next;
926			break;
927		}
928	}
929
930	rc = parens ? -1 : 0;
931
932done:
933	LDAP_FREE( freeme );
934	return rc;
935}
936
937int
938ldap_put_vrFilter( BerElement *ber, const char *str_in )
939{
940	int rc =0;
941
942	if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
943		rc = -1;
944	}
945
946	rc = put_vrFilter( ber, str_in );
947
948	if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
949		rc = -1;
950	}
951
952	return rc;
953}
954
955static int
956put_vrFilter_list( BerElement *ber, char *str )
957{
958	char	*next = NULL;
959	char	save;
960
961	Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
962		str, 0, 0 );
963
964	while ( *str ) {
965		while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
966			str++;
967		}
968		if ( *str == '\0' ) break;
969
970		if ( (next = find_right_paren( str + 1 )) == NULL ) {
971			return -1;
972		}
973		save = *++next;
974
975		/* now we have "(filter)" with str pointing to it */
976		*next = '\0';
977		if ( put_vrFilter( ber, str ) == -1 ) return -1;
978		*next = save;
979		str = next;
980	}
981
982	return 0;
983}
984
985static int
986put_simple_vrFilter(
987	BerElement *ber,
988	char *str )
989{
990	char		*s;
991	char		*value;
992	ber_tag_t	ftype;
993	int		rc = -1;
994
995	Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
996		str, 0, 0 );
997
998	str = LDAP_STRDUP( str );
999	if( str == NULL ) return -1;
1000
1001	if ( (s = strchr( str, '=' )) == NULL ) {
1002		goto done;
1003	}
1004
1005	value = s + 1;
1006	*s-- = '\0';
1007
1008	switch ( *s ) {
1009	case '<':
1010		ftype = LDAP_FILTER_LE;
1011		*s = '\0';
1012		break;
1013
1014	case '>':
1015		ftype = LDAP_FILTER_GE;
1016		*s = '\0';
1017		break;
1018
1019	case '~':
1020		ftype = LDAP_FILTER_APPROX;
1021		*s = '\0';
1022		break;
1023
1024	case ':':
1025		/* According to ValuesReturnFilter control definition
1026		 * extensible filters are off the form:
1027		 *		type [:rule] := value
1028		 * or	:rule := value
1029		 */
1030		ftype = LDAP_FILTER_EXT;
1031		*s = '\0';
1032
1033		{
1034			char *rule = strchr( str, ':' );
1035
1036			if( rule == NULL ) {
1037				/* must have attribute */
1038				if( !ldap_is_desc( str ) ) {
1039					goto done;
1040				}
1041				rule = "";
1042			} else {
1043				*rule++ = '\0';
1044			}
1045
1046			if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
1047				/* must have either type or rule */
1048				goto done;
1049			}
1050
1051			if ( *str != '\0' && !ldap_is_desc( str ) ) {
1052				goto done;
1053			}
1054
1055			if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
1056				goto done;
1057			}
1058
1059			rc = ber_printf( ber, "t{" /*"}"*/, ftype );
1060
1061			if( rc != -1 && rule && *rule != '\0' ) {
1062				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
1063			}
1064
1065			if( rc != -1 && *str != '\0' ) {
1066				rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
1067			}
1068
1069			if( rc != -1 ) {
1070				ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1071
1072				if( len >= 0 ) {
1073					rc = ber_printf( ber, "to",
1074						LDAP_FILTER_EXT_VALUE, value, len );
1075				} else {
1076					rc = -1;
1077				}
1078			}
1079
1080			if( rc != -1 ) {
1081				rc = ber_printf( ber, /*"{"*/ "N}" );
1082			}
1083		}
1084		goto done;
1085
1086	default:
1087		if( !ldap_is_desc( str ) ) {
1088			goto done;
1089
1090		} else {
1091			char *nextstar = ldap_pvt_find_wildcard( value );
1092
1093			if ( nextstar == NULL ) {
1094				goto done;
1095
1096			} else if ( *nextstar == '\0' ) {
1097				ftype = LDAP_FILTER_EQUALITY;
1098
1099			} else if ( strcmp( value, "*" ) == 0 ) {
1100				ftype = LDAP_FILTER_PRESENT;
1101
1102			} else {
1103				rc = put_substring_filter( ber, str, value, nextstar );
1104				goto done;
1105			}
1106		} break;
1107	}
1108
1109	if( !ldap_is_desc( str ) ) goto done;
1110
1111	if ( ftype == LDAP_FILTER_PRESENT ) {
1112		rc = ber_printf( ber, "ts", ftype, str );
1113
1114	} else {
1115		ber_slen_t len = ldap_pvt_filter_value_unescape( value );
1116
1117		if( len >= 0 ) {
1118			rc = ber_printf( ber, "t{soN}",
1119				ftype, str, value, len );
1120		}
1121	}
1122
1123done:
1124	if( rc != -1 ) rc = 0;
1125	LDAP_FREE( str );
1126	return rc;
1127}
1128
1129