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