openldap/libraries/libldap/search.c
Kurt Zeilenga b5494457d8 Remove extern declarations of library functions from source.c.
This could cause problems on odd systems.  The generic
  headers should be extended as needed to include necessary
  system headers or, if necessary, make explicit declarations.
Extended ac/string.h header to look for string.h/strings.h if
  STDC_HEADERS is not defined.  Also provide basic declarations for
  str*() functions.  This could cause problems on odd systems.
Extended ac/unistd.h header to define basic declaration for misc
  functions that might be missing from headers.   This includes
  externs for getenv(), getopt(), mktemp(), tempname().
Protect fax500.h from multiple inclusion.  Moved includes of
  system/generic headers back to source files.
Made mail500 helper functions static.
Fixed includes of ctype.h, signal.h, etc. to use generics.
lutil/tempname.c: was including stdlib.h twice, one should stdio.h.
Wrapped <sys/resource.h> with HAVE_SYS_RESOURCE_H.
lber/io.c/ber_get_next(): Changed noctets back to signed.
  Used with BerRead which expects signed int as second arg and
  returns signed int.
1998-11-16 05:07:27 +00:00

552 lines
12 KiB
C

/*
* Copyright (c) 1990 Regents of the University of Michigan.
* All rights reserved.
*
* search.c
*/
#include "portable.h"
#ifndef lint
static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ac/ctype.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include "ldap-int.h"
static char *find_right_paren LDAP_P(( char *s ));
static char *put_complex_filter LDAP_P(( BerElement *ber, char *str,
unsigned long tag, int not ));
static int put_filter LDAP_P(( BerElement *ber, char *str ));
static int put_simple_filter LDAP_P(( BerElement *ber, char *str ));
static int put_substring_filter LDAP_P(( BerElement *ber, char *type, char *str ));
static int put_filter_list LDAP_P(( BerElement *ber, char *str ));
/*
* ldap_search - initiate an ldap (and X.500) search operation. Parameters:
*
* ld LDAP descriptor
* base DN of the base object
* scope the search scope - one of LDAP_SCOPE_BASE,
* LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
* filter a string containing the search filter
* (e.g., "(|(cn=bob)(sn=bob))")
* attrs list of attribute types to return for matches
* attrsonly 1 => attributes only 0 => attributes and values
*
* Example:
* char *attrs[] = { "mail", "title", 0 };
* msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
* attrs, attrsonly );
*/
int
ldap_search( LDAP *ld, char *base, int scope, char *filter,
char **attrs, int attrsonly )
{
BerElement *ber;
Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
if (( ber = ldap_build_search_req( ld, base, scope, filter, attrs,
attrsonly )) == NULLBER ) {
return( -1 );
}
#ifndef LDAP_NOCACHE
if ( ld->ld_cache != NULL ) {
if ( ldap_check_cache( ld, LDAP_REQ_SEARCH, ber ) == 0 ) {
ber_free( ber, 1 );
ld->ld_errno = LDAP_SUCCESS;
return( ld->ld_msgid );
}
ldap_add_request_to_cache( ld, LDAP_REQ_SEARCH, ber );
}
#endif /* LDAP_NOCACHE */
/* send the message */
return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber ));
}
BerElement *
ldap_build_search_req( LDAP *ld, char *base, int scope, char *filter,
char **attrs, int attrsonly )
{
BerElement *ber;
int err;
/*
* Create the search request. It looks like this:
* SearchRequest := [APPLICATION 3] SEQUENCE {
* baseObject DistinguishedName,
* scope ENUMERATED {
* baseObject (0),
* singleLevel (1),
* wholeSubtree (2)
* },
* derefAliases ENUMERATED {
* neverDerefaliases (0),
* derefInSearching (1),
* derefFindingBaseObj (2),
* alwaysDerefAliases (3)
* },
* sizelimit INTEGER (0 .. 65535),
* timelimit INTEGER (0 .. 65535),
* attrsOnly BOOLEAN,
* filter Filter,
* attributes SEQUENCE OF AttributeType
* }
* wrapped in an ldap message.
*/
/* create a message to send */
if ( (ber = ldap_alloc_ber_with_options( ld )) == NULLBER ) {
return( NULLBER );
}
if ( base == NULL ) {
base = "";
}
#ifdef LDAP_CONNECTIONLESS
if ( ld->ld_sb.sb_naddr > 0 ) {
err = ber_printf( ber, "{ist{seeiib", ++ld->ld_msgid,
ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
} else {
#endif /* LDAP_CONNECTIONLESS */
err = ber_printf( ber, "{it{seeiib", ++ld->ld_msgid,
LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
ld->ld_sizelimit, ld->ld_timelimit, attrsonly );
#ifdef LDAP_CONNECTIONLESS
}
#endif /* LDAP_CONNECTIONLESS */
if ( err == -1 ) {
ld->ld_errno = LDAP_ENCODING_ERROR;
ber_free( ber, 1 );
return( NULLBER );
}
filter = strdup( filter );
err = put_filter( ber, filter );
free( filter );
if ( err == -1 ) {
ld->ld_errno = LDAP_FILTER_ERROR;
ber_free( ber, 1 );
return( NULLBER );
}
if ( ber_printf( ber, "{v}}}", attrs ) == -1 ) {
ld->ld_errno = LDAP_ENCODING_ERROR;
ber_free( ber, 1 );
return( NULLBER );
}
return( ber );
}
static char *
find_right_paren( char *s )
{
int balance, escape;
balance = 1;
escape = 0;
while ( *s && balance ) {
if ( escape == 0 ) {
if ( *s == '(' )
balance++;
else if ( *s == ')' )
balance--;
}
if ( *s == '\\' && ! escape )
escape = 1;
else
escape = 0;
if ( balance )
s++;
}
return( *s ? s : NULL );
}
static char *
put_complex_filter( BerElement *ber, char *str, unsigned long tag, int not )
{
char *next;
/*
* We have (x(filter)...) with str sitting on
* the x. We have to find the paren matching
* the one before the x and put the intervening
* filters by calling put_filter_list().
*/
/* put explicit tag */
if ( ber_printf( ber, "t{", tag ) == -1 )
return( NULL );
/*
if ( !not && ber_printf( ber, "{" ) == -1 )
return( NULL );
*/
str++;
if ( (next = find_right_paren( str )) == NULL )
return( NULL );
*next = '\0';
if ( put_filter_list( ber, str ) == -1 )
return( NULL );
*next++ = ')';
/* flush explicit tagged thang */
if ( ber_printf( ber, "}" ) == -1 )
return( NULL );
/*
if ( !not && ber_printf( ber, "}" ) == -1 )
return( NULL );
*/
return( next );
}
static int
put_filter( BerElement *ber, char *str )
{
char *next, *tmp, *s, *d;
int parens, balance, escape, gotescape;
/*
* A Filter looks like this:
* Filter ::= CHOICE {
* and [0] SET OF Filter,
* or [1] SET OF Filter,
* not [2] Filter,
* equalityMatch [3] AttributeValueAssertion,
* substrings [4] SubstringFilter,
* greaterOrEqual [5] AttributeValueAssertion,
* lessOrEqual [6] AttributeValueAssertion,
* present [7] AttributeType,,
* approxMatch [8] AttributeValueAssertion
* }
*
* SubstringFilter ::= SEQUENCE {
* type AttributeType,
* SEQUENCE OF CHOICE {
* initial [0] IA5String,
* any [1] IA5String,
* final [2] IA5String
* }
* }
* Note: tags in a choice are always explicit
*/
Debug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 );
gotescape = parens = 0;
while ( *str ) {
switch ( *str ) {
case '(':
str++;
parens++;
switch ( *str ) {
case '&':
Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
0, 0, 0 );
if ( (str = put_complex_filter( ber, str,
LDAP_FILTER_AND, 0 )) == NULL )
return( -1 );
parens--;
break;
case '|':
Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
0, 0, 0 );
if ( (str = put_complex_filter( ber, str,
LDAP_FILTER_OR, 0 )) == NULL )
return( -1 );
parens--;
break;
case '!':
Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
0, 0, 0 );
if ( (str = put_complex_filter( ber, str,
LDAP_FILTER_NOT, 1 )) == NULL )
return( -1 );
parens--;
break;
default:
Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
0, 0, 0 );
balance = 1;
escape = 0;
next = str;
while ( *next && balance ) {
if ( escape == 0 ) {
if ( *next == '(' )
balance++;
else if ( *next == ')' )
balance--;
}
if ( *next == '\\' && ! escape )
gotescape = escape = 1;
else
escape = 0;
if ( balance )
next++;
}
if ( balance != 0 )
return( -1 );
*next = '\0';
tmp = strdup( str );
if ( gotescape ) {
escape = 0;
for ( s = d = tmp; *s; s++ ) {
if ( *s != '\\' || escape ) {
*d++ = *s;
escape = 0;
} else {
escape = 1;
}
}
*d = '\0';
}
if ( put_simple_filter( ber, tmp ) == -1 ) {
free( tmp );
return( -1 );
}
free( tmp );
*next++ = ')';
str = next;
parens--;
break;
}
break;
case ')':
Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0,
0 );
if ( ber_printf( ber, "]" ) == -1 )
return( -1 );
str++;
parens--;
break;
case ' ':
str++;
break;
default: /* assume it's a simple type=value filter */
Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0,
0 );
next = strchr( str, '\0' );
tmp = strdup( str );
if ( strchr( tmp, '\\' ) != NULL ) {
escape = 0;
for ( s = d = tmp; *s; s++ ) {
if ( *s != '\\' || escape ) {
*d++ = *s;
escape = 0;
} else {
escape = 1;
}
}
*d = '\0';
}
if ( put_simple_filter( ber, tmp ) == -1 ) {
free( tmp );
return( -1 );
}
free( tmp );
str = next;
break;
}
}
return( parens ? -1 : 0 );
}
/*
* Put a list of filters like this "(filter1)(filter2)..."
*/
static int
put_filter_list( BerElement *ber, char *str )
{
char *next;
char save;
Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 );
while ( *str ) {
while ( *str && isspace( *str ) )
str++;
if ( *str == '\0' )
break;
if ( (next = find_right_paren( str + 1 )) == NULL )
return( -1 );
save = *++next;
/* now we have "(filter)" with str pointing to it */
*next = '\0';
if ( put_filter( ber, str ) == -1 )
return( -1 );
*next = save;
str = next;
}
return( 0 );
}
static int
put_simple_filter( BerElement *ber, char *str )
{
char *s;
char *value, savechar;
unsigned long ftype;
int rc;
Debug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 );
if ( (s = strchr( str, '=' )) == NULL )
return( -1 );
value = s + 1;
*s-- = '\0';
savechar = *s;
switch ( *s ) {
case '<':
ftype = LDAP_FILTER_LE;
*s = '\0';
break;
case '>':
ftype = LDAP_FILTER_GE;
*s = '\0';
break;
case '~':
ftype = LDAP_FILTER_APPROX;
*s = '\0';
break;
default:
if ( strchr( value, '*' ) == NULL ) {
ftype = LDAP_FILTER_EQUALITY;
} else if ( strcmp( value, "*" ) == 0 ) {
ftype = LDAP_FILTER_PRESENT;
} else {
rc = put_substring_filter( ber, str, value );
*(value-1) = '=';
return( rc );
}
break;
}
if ( ftype == LDAP_FILTER_PRESENT ) {
rc = ber_printf( ber, "ts", ftype, str );
} else {
rc = ber_printf( ber, "t{ss}", ftype, str, value );
}
*s = savechar;
*(value-1) = '=';
return( rc == -1 ? rc : 0 );
}
static int
put_substring_filter( BerElement *ber, char *type, char *val )
{
char *nextstar, gotstar = 0;
unsigned long ftype;
Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type,
val, 0 );
if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 )
return( -1 );
while ( val != NULL ) {
if ( (nextstar = strchr( val, '*' )) != NULL )
*nextstar++ = '\0';
if ( gotstar == 0 ) {
ftype = LDAP_SUBSTRING_INITIAL;
} else if ( nextstar == NULL ) {
ftype = LDAP_SUBSTRING_FINAL;
} else {
ftype = LDAP_SUBSTRING_ANY;
}
if ( *val != '\0' ) {
if ( ber_printf( ber, "ts", ftype, val ) == -1 )
return( -1 );
}
gotstar = 1;
if ( nextstar != NULL )
*(nextstar-1) = '*';
val = nextstar;
}
if ( ber_printf( ber, "}}" ) == -1 )
return( -1 );
return( 0 );
}
int
ldap_search_st( LDAP *ld, char *base, int scope, char *filter, char **attrs,
int attrsonly, struct timeval *timeout, LDAPMessage **res )
{
int msgid;
if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
== -1 )
return( ld->ld_errno );
if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 )
return( ld->ld_errno );
if ( ld->ld_errno == LDAP_TIMEOUT ) {
(void) ldap_abandon( ld, msgid );
ld->ld_errno = LDAP_TIMEOUT;
return( ld->ld_errno );
}
return( ldap_result2error( ld, *res, 0 ) );
}
int
ldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
int attrsonly, LDAPMessage **res )
{
int msgid;
if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
== -1 )
return( ld->ld_errno );
if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, res ) == -1 )
return( ld->ld_errno );
return( ldap_result2error( ld, *res, 0 ) );
}