Sun logo      Previous      Contents      Index      Next     

Sun Java System LDAP SDK for C Programming Guide

Chapter 5
Searching a LDAP Directory

This chapter explains how to call Sun™ Java System LDAP SDK for C functions to search a directory, retrieve the results, and get attributes and values from each entry in the search results. The chapter also provides examples of calling synchronous and asynchronous functions to search the directory. It contains the following sections:


Overview

The LDAP SDK for C provides functions that allow you to search a directory and retrieve results from the server. For example, you can send a search request by calling the synchronous ldap_search_ext_s() or the asynchronous ldap_search_ext() and the server will send back matching results. In the LDAPv3, a server can send three different types of results, represented by LDAPMessage structures, back to the client:

These results can be processed based on the following guidelines:


Sending a Search Request

To search the directory, call ldap_search_ext_s() or ldap_search_ext(). ldap_search_ext_s() is a synchronous function that blocks other work until all results have been received from the server. It is declared as illustrated in Code Example 5-1.

Code Example 5-1  ldap_search_ext_s() Search Request

LDAP_API(int) LDAP_CALL ldap_search_ext_s( LDAP *ld, const char *base,

int scope, const char *filter, char **attrs, int attrsonly,

LDAPControl **serverctrls, LDAPControl **clientctrls,

struct timeval *timeoutp, int sizelimit, LDAPMessage **res );

ldap_search_ext() is an asynchronous function that sends an LDAP search request to the server. You can do other work while checking to see if the server has returned any results. It is declared as illustrated in Code Example 5-2.

Code Example 5-2  ldap_search_ext() Search Request

LDAP_API(int) LDAP_CALL ldap_search_ext( LDAP *ld, const char *base,

int scope, const char *filter, char **attrs, int attrsonly,

LDAPControl **serverctrls, LDAPControl **clientctrls,

struct timeval *timeoutp, int sizelimit, int *msgidp );

Sample code for sending a search request can be found in "Sending A Search Request".

Search Parameters

For either of the functions illustrated in Code Example 5-1 and Code Example 5-2, you specify the search criteria using the parameters as detailed in Table 5-1.

Table 5-1  Search Criteria Parameters

Parameter Name

Description

base

Specifies the starting point in the directory, or the base DN, where the search begins. For example, to search entries under dc=example,dc=com, the base DN is dc=example,dc=com. See "Specifying the Base DN and Scope".

scope

Specifies which entries to search. You can narrow the scope of the search to search only the base DN, entries at one level under the base DN, or entries at all levels under the base DN. See "Specifying the Base DN and Scope".

filter

Specifies a search filter by defining what to search for. A search filter can be as simple as “find entries where the last name is Jensen” or as complex as “find entries that belong to Dept. #17 and whose first names start with the letter F.” See "Defining a Search Filter".

attrs
attrsonly

Specify the type of information that you want returned (which attributes you want to retrieve) and whether you want the attribute type only or the attribute type and its values. You can also specify to return the names of attributes only (and not the values) by passing a non-zero value for the attrsonly argument. See "Specifying Attributes to Retrieve".

serverctrls
clientctrls

Specify the LDAPv3 controls associated with this search operation. For details on LDAPv3 controls, see Chapter 13, "Working with LDAP Controls."

timeoutp
sizelimit

Specify search constraints that you want applied to the search. For example, you can specify a different time-out period or maximum number of results from the values already defined for the current session. See "Setting Search Preferences".

Specifying the Base DN and Scope

When sending a search request, you need to specify the base DN and scope of the search in order to identify the entries that you want searched. The base DN (the root argument) is the DN of the entry that serves as the starting point for the search. To specify the scope of the search, pass one of the following values as the scope parameter:

Defining a Search Filter

When you search the directory, you use a search filter to define the search. Table 5-2 illustrates the search filter syntax and a simple example.

Table 5-2  Search Filter Syntax

Basic Search Filter Syntax

Example of Search Filter

(attribute operator value)

(cn=Barbara Jensen)

By comparing the syntax to the example we note that cn is the attribute, = is the operator, and Barbara Jensen is the value. The filter finds entries with the common name Barbara Jensen. For a listing of valid attributes that you can use in your search filter, see the documentation or LDAP schema for the directory server you are using. For a listing of valid operators that you can use in your search filter, see Table 5-3.

Table 5-3  Basic Operators for Search Filters

Operator

Description

Example

=

Returns entries whose attribute is equal to the value.

(cn=Barbara Jensen) finds the entry “cn=Barbara Jensen”

>=

Returns entries whose attribute is greater than or equal to the value.

(sn >= jensen) finds all entries from “sn=jensen” to “sn=z...”

<=

Returns entries whose attribute is less than or equal to the value.

(sn <= jensen) finds all entries from “sn=a...” to “sn=jensen”

=*

Returns entries that have a value set for that attribute.

(sn =*) finds all entries that have the sn attribute

~=

Returns entries whose attribute value approximately matches the specified value. Typically, this is an algorithm that matches words that sound alike.

(sn ~= jensen) finds the entry “sn = jansen”

Using boolean operators and parentheses, you can combine different sets of conditions into one filter. Table 5-4 illustrates the boolean search filter syntax for combining filters and a simple example.

Table 5-4  Boolean Search Filter Syntax

Boolean Search Filter Syntax

Example of Search Filter

( boolean_operator (filter1)(filter2)(filter3))

(|(sn=Jensen)(sn=Johnson))

The example uses the pipe (|) to signify a search for all entries with the last name Jensen or the last name Johnson. Table 5-5 is a listing of valid boolean operators that you can use in your search filter.

Table 5-5  Boolean Operators for Search Filters 

Operator

Description

&

Returns entries matching all specified filter criteria.

|

Returns entries matching one or more of the filter criteria.

!

Returns entries for which the filter is not true. You can only apply this operator to a single filter. For example, you can use:

(!(filter))

but not:

(!(filter1)(filter2))

You can also include wildcards (*) to search for entries that start with, contain, or end with a given value. For example, you can use this filter to search for all entries whose names begin with the letter F:

(givenName=F*)


Note

When comparing values containing letters, the value of the letter a is less than the value z. For example, the following filter finds all entries with last names beginning with a through Jensen:

(sn<=jensen)


Specifying Attributes to Retrieve

Using the attrs argument, you can either retrieve all attributes in the entries returned by the search, or you can specify the attributes from the search results that you want returned. To specify attributes, use the following guidelines:

Sorting Attributes

If you plan to sort the results on your client, you need to return the attributes that you plan to use for sorting. For example, if you plan to sort by email address, make sure that the mail attribute is returned in the search results. See "Sorting the Search Results" for more details.

Operational Attributes

Some attributes are used by servers for administering the directory. For example, the creatorsName attribute specifies the DN of the user who added the entry. These attributes are called operational attributes. Servers do not normally return operational attributes in search results unless you specify the attributes by name. For example, if you pass NULL for the attrs argument to retrieve all of the attributes in entries found by the search, the operational attribute creatorsName will not be returned to your client. You need to explicitly specify the creatorsName attribute in the attrs argument. To return all attributes in an entry and selected operational attributes, pass a NULL-terminated array containing LDAP_ALL_USER_ATTRS and the names of the operational attributes as the attrs argument. Table 5-6 lists some of the operational attributes and the information they contain.

Table 5-6  InformationAvailable in Operational Attributes

Attribute Name

Description of Values

createTimestamp

The time the entry was added to the directory

modifyTimestamp

The time the entry was last modified

creatorsName

DN of the user who added the entry to the directory

modifiersName

DN of the user who last modified the entry

subschemaSubentry

DN of the subschema entry, which controls the schema for this entry. (See "Getting Schema Information" of Chapter 10, "Retrieving Server Information" for details.)

Setting Search Preferences

For a given search, you can specify the maximum number of results to be returned or the maximum amount of time to wait for a search. Use the timeoutp and sizelimit arguments of the ldap_search_ext_s() or ldap_search_ext() functions with the following guidelines:

Code Example 5-3 sets these session preferences so that a search returns no more than 100 entries and takes no more than 30 seconds.

Code Example 5-3  Sample Code to Set Session Search Preferences 

#include <stdio.h>

#include "ldap.h"

...

LDAP *ld;

int max_ret, max_tim;

char *host = "localhost";

...

/* Initialize a session with the LDAP server ldap.netscape.com:389. */

if ( ( ld = ldap_init( host, LDAP_PORT ) ) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Set the maximum number of entries returned. */

max_ret = 100;

ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)&max_ret );

/* Set the maximum number of seconds to wait. */

max_tim = 30;

ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *)&max_tim );

...


Getting Search Results

In the LDAPv3, the server returns search results as a chain of LDAPMessage structures with each structure containing:

To retrieve an individual result from a chain of LDAPMessage structures, you can call one of the following sets of functions:

Getting Results Synchronously

If you call ldap_search_ext_s() to search the directory synchronously, the function blocks processes until all results have been received. It then returns a chain of results in the result parameter (a handle to an LDAPMessage structure). Code Example 5-4 prints out the values of all attributes in the entries returned by a synchronous search.

Code Example 5-4  Synchronous Searching 

#include <stdio.h>

#include "ldap.h"

/* Change these as needed. */

#define HOSTNAME "localhost"

#define PORTNUMBER LDAP_PORT

#define BASEDN "dc=example,dc=com"

#define SCOPE LDAP_SCOPE_SUBTREE

#define FILTER "(sn=Jensen)"

int

main( int argc, char **argv )

{

LDAP *ld;

LDAPMessage *res, *msg;

LDAPControl **serverctrls;

BerElement *ber;

char *a, *dn, *matched_msg = NULL, *error_msg = NULL;

char **vals, **referrals;

int version, i, rc, parse_rc, msgtype, num_entries = 0,

num_refs = 0;

/* Get a handle to an LDAP connection. */

if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

version = LDAP_VERSION3;

if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ) !=

LDAP_SUCCESS ) {

rc = ldap_get_lderrno( ld, NULL, NULL );

fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Bind to the server anonymously. */

rc = ldap_simple_bind_s( ld, NULL, NULL );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );

ldap_get_lderrno( ld, &matched_msg, &error_msg );

if ( error_msg != NULL && *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg );

}

ldap_unbind_s( ld );

return( 1 );

}

/* Perform the search operation. */

rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );

if ( error_msg != NULL && *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg );

}

ldap_unbind_s( ld );

return( 1 );

}

num_entries = ldap_count_entries( ld, res );

num_refs = ldap_count_references( ld, res );

/* Iterate through the results. An LDAPMessage structure sent back from

a search operation can contain either an entry found by the search,

a search reference, or the final result of the search operation. */

for ( msg = ldap_first_message( ld, res ); msg != NULL; msg = ldap_next_message( ld, msg ) ) {

/* Determine what type of message was sent from the server. */

msgtype = ldap_msgtype( msg );

switch( msgtype ) {

/* If the result was an entry found by the search, get and print the

attributes and values of the entry. */

case LDAP_RES_SEARCH_ENTRY:

/* Get and print the DN of the entry. */

if (( dn = ldap_get_dn( ld, res )) != NULL ) {

printf( "dn: %s\n", dn );

ldap_memfree( dn );

}

/* Iterate through each attribute in the entry. */

for ( a = ldap_first_attribute( ld, res, &ber );

a != NULL; a = ldap_next_attribute( ld, res, ber ) ) {

/* Get and print all values for each attribute. */

if (( vals = ldap_get_values( ld, res, a )) != NULL ) {

for ( i = 0; vals[ i ] != NULL; i++ ) {

printf( "%s: %s\n", a, vals[ i ] );

}

ldap_value_free( vals );

}

ldap_memfree( a );

}

if ( ber != NULL ) {

ber_free( ber, 0 );

}

printf( "\n" );

break;

case LDAP_RES_SEARCH_REFERENCE:

/* The server sent a search reference encountered during the

search operation. */

/* Parse the result and print the search references.

Ideally, rather than print them out, you would follow the

references. */

parse_rc = ldap_parse_reference( ld, msg, &referrals, NULL, 0 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

if ( referrals != NULL ) {

for ( i = 0; referrals[ i ] != NULL; i++ ) {

printf( "Search reference: %s\n\n", referrals[ i ] );

}

ldap_value_free( referrals );

}

break;

case LDAP_RES_SEARCH_RESULT:

/* Parse the final result received from the server. Note the last

argument is a non-zero value, which indicates that the

LDAPMessage structure will be freed when done. (No need

to call ldap_msgfree().) */

parse_rc = ldap_parse_result( ld, msg, &rc, &matched_msg, &error_msg, NULL, &serverctrls, 0 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Check the results of the LDAP search operation. */

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );

if ( error_msg != NULL & *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg );

}

} else {

printf( "Search completed successfully.\n"

"Entries found: %d\n"

"Search references returned: %d\n",

num_entries, num_refs );

}

break;

default:

break;

}

}

/* Disconnect when done. */

ldap_unbind( ld );

return( 0 );

}

Getting Results Asynchronously

If you use the asynchronous function ldap_search_ext(), you need to call ldap_result() (as illustrated in Code Example 5-5) to first determine if the server sent back any results.

Code Example 5-5  Declaring ldap_result()

LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all,

struct timeval *timeout, LDAPMessage **result );

You can specify how you want to get asynchronous results.

Code Example 5-7 prints out the values of all attributes in the entries returned by an asynchronous search.

Code Example 5-7  Printing Results of Asynchronous Search 

#include <stdio.h>

#include "ldap.h"

void do_other_work();

int global_counter = 0;

/* Change these as needed. */

#define HOSTNAME "localhost"

#define PORTNUMBER LDAP_PORT

#define BASEDN "dc=example,dc=com"

#define SCOPE LDAP_SCOPE_SUBTREE

#define FILTER "(sn=Jensen)"

int

main( int argc, char **argv )

{

LDAP *ld;

LDAPMessage *res;

BerElement *ber;

LDAPControl **serverctrls;

char *a, *dn, *matched_msg = NULL, *error_msg = NULL;

char **vals, **referrals;

int version, i, msgid, rc, parse_rc, finished = 0,

num_entries = 0, num_refs = 0;

struct timeval zerotime;

zerotime.tv_sec = zerotime.tv_usec = 0L;

/* Get a handle to an LDAP connection. */

if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

version = LDAP_VERSION3;

if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ) != LDAP_SUCCESS ) {

rc = ldap_get_lderrno( ld, NULL, NULL );

fprintf( stderr, "ldap_set_option: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Bind to the server anonymously. */

rc = ldap_simple_bind_s( ld, NULL, NULL );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );

ldap_get_lderrno( ld, &matched_msg, &error_msg );

if ( error_msg != NULL && *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

/* If the server cannot find an entry,

print the portion of the DN that matches

an existing entry. (For details, see

“Receiving the Portion of the DN Matching an Entry.”) */

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr,

"Part of the DN that matches an existing entry: %s\n",

matched_msg );

}

ldap_unbind_s( ld );

return( 1 );

}

/* Send the LDAP search request. */

rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msgid );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Poll the server for the results of the search operation.

Passing LDAP_MSG_ONE indicates that you want to receive

the entries one at a time, as they come in. If the next

entry that you retrieve is NULL, there are no more entries. */

while ( !finished ) {

rc = ldap_result( ld, msgid, LDAP_MSG_ONE, &zerotime, &res );

/* The server can return three types of results back to the client,

and the return value of ldap_result() indicates the result type:

LDAP_RES_SEARCH_ENTRY identifies an entry found by the search,

LDAP_RES_SEARCH_REFERENCE identifies a search reference returned

by the server, and LDAP_RES_SEARCH_RESULT is the last result

sent from the server to the client after the operation completes.

You need to check for each of these types of results. */

switch ( rc ) {

case -1:

/* An error occurred. */

rc = ldap_get_lderrno( ld, NULL, NULL );

fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return( 1 );

case 0:

/* The timeout period specified by zerotime was exceeded.

This means that the server has still not yet sent the

results of the search operation back to your client.

Break out of this switch statement, and continue calling

ldap_result() to poll for results. */

break;

case LDAP_RES_SEARCH_ENTRY:

/* The server sent one of the entries found by the search

operation. Print the DN, attributes, and values of the entry. */

/* Keep track of the number of entries found. */

num_entries++;

/* Get and print the DN of the entry. */

if (( dn = ldap_get_dn( ld, res )) != NULL ) {

printf( "dn: %s\n", dn );

ldap_memfree( dn );

}

/* Iterate through each attribute in the entry. */

for ( a = ldap_first_attribute( ld, res, &ber );

a != NULL; a = ldap_next_attribute( ld, res, ber ) ) {

/* Get and print all values for each attribute. */

if (( vals = ldap_get_values( ld, res, a )) != NULL ) {

for ( i = 0; vals[ i ] != NULL; i++ ) {

printf( "%s: %s\n", a, vals[ i ] );

}

ldap_value_free( vals );

}

ldap_memfree( a );

}

if ( ber != NULL ) {

ber_free( ber, 0 );

}

printf( "\n" );

ldap_msgfree( res );

break;

case LDAP_RES_SEARCH_REFERENCE:

/* The server sent a search reference encountered during the

search operation. */

/* Keep track of the number of search references returned from

the server. */

num_refs++;

/* Parse the result and print the search references.

Ideally, rather than print them out, you would follow the

references. */

parse_rc = ldap_parse_reference( ld, res, &referrals, NULL, 1 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

if ( referrals != NULL ) {

for ( i = 0; referrals[ i ] != NULL; i++ ) {

printf( "Search reference: %s\n\n", referrals[ i ] );

}

ldap_value_free( referrals );

}

break;

case LDAP_RES_SEARCH_RESULT:

/* Parse the final result received from the server. Note the last

argument is a non-zero value, which indicates that the

LDAPMessage structure will be freed when done. (No need

to call ldap_msgfree().) */

finished = 1;

parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg, &error_msg, NULL, &serverctrls, 1 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n", ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

/* Check the results of the LDAP search operation. */

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );

ldap_get_lderrno( ld, &matched_msg, &error_msg );

if ( error_msg != NULL & *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr, "Part of the DN that matches an existing entry: %s\n", matched_msg );

}

} else {

printf( "Search completed successfully.\n"

"Entries found: %d\n"

"Search references returned: %d\n"

"Counted to %d while waiting for the search operation.\n",

num_entries, num_refs, global_counter );

}

break;

default:

break;

}

/* Do other work here while waiting for the search operation to complete. */

if ( !finished ) {

do_other_work();

}

}

/* Disconnect when done. */

ldap_unbind( ld );

return( 0 );

}

/*

* Perform other work while polling for results. This doesn't do anything

* useful, but it could.

*/

static void

do_other_work()

{

global_counter++;

}

Determing Search Result Types

To determine what type of result was returned, call the ldap_msgtype() function. A search result can be one of the following types:

Code Example 5-8 retrieves each result in a chain and determines its type.

Code Example 5-8  Retrieving a Chain of Results 

#include <stdio.h>

#include "ldap.h"

...

#define BASEDN "dc=example,dc=com"

#define SCOPE LDAP_SCOPE_SUBTREE

#define FILTER "(sn=Jensen)"

...

LDAP *ld;

LDAPMessage *res, *msg;

BerElement *ber;

char *matched_msg = NULL, *error_msg = NULL;

int rc, msgtype, num_entries = 0, num_refs = 0;

...

/* Perform the search operation. */

rc = ldap_search_ext_s( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL,

NULL, LDAP_NO_LIMIT, &res );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );

if ( error_msg != NULL && *error_msg != '\0' ) {

fprintf( stderr, "%s\n", error_msg );

}

/* If the server cannot find an entry and returns the portion of the DN

that can find an entry, print it out. (For details, see

“Receiving the Portion of the DN Matching an Entry.”) */

if ( matched_msg != NULL && *matched_msg != '\0' ) {

fprintf( stderr,

"Part of the DN that matches an existing entry: %s\n",

matched_msg );

}

ldap_unbind_s( ld );

return( 1 );

}

...

num_entries = ldap_count_entries( ld, res );

num_refs = ldap_count_references( ld, res );

...

/* Iterate through the results. */

for ( msg = ldap_first_message( ld, res ); msg != NULL;

msg = ldap_next_message( ld, msg ) ) {

/* Determine what type of message was sent from the server. */

msgtype = ldap_msgtype( msg );

switch( msgtype ) {

case LDAP_RES_SEARCH_ENTRY:

/* The result is an entry. */

...

case LDAP_RES_SEARCH_REFERENCE:

/* The result is a search reference. */

...

case LDAP_RES_SEARCH_RESULT:

/* The result is the final result sent by the server. */

...

}

...

}

...

Getting Distinguished Names for Each Entry

Because the DN of an entry differentiates it from other entries, you may want to access the DN in search results. You may also want to parse the name into its individual components. The SDK provides functions for both of these tasks.

Getting the Distinguished Name of an Entry

To get the DN of an entry, call the ldap_get_dn() function. When finished with the DN, you should free it from memory by calling the ldap_memfree() function. Code Example 5-9 prints the DN for each entry found in a search.

Code Example 5-9  Obtaining DN for Search Result Entries 

#include <stdio.h>

#include <ldap.h>

...

LDAP *ld;

LDAPMessage *result, *e;

char *dn;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* For each matching entry found, print the name of the entry.*/

for ( e = ldap_first_entry( ld, result ); e != NULL;

e = ldap_next_entry( ld, e ) ) {

if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) {

printf( "dn: %s\n", dn );

/* Free the memory used for the DN when done */

ldap_memfree( dn );

}

}

/* Free the result from memory when done. */

ldap_msgfree( result );

Getting the Components of a Distinguished Name

If you want to access individual components of a DN or relative DN, call the ldap_explode_dn() and ldap_explode_rdn() functions, respectively. Both functions return a NULL-terminated array containing the components of the DN. When you are done working with this array, free it by calling the ldap_value_free() function.

You can also specify whether or not you want the attribute names included in the array by using the notypes parameter. Set notypes to 0 if you want to include attribute names as in:

ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 0 )

This function then returns this array:

{ "uid=bjensen", "ou=People", "dc=example,dc=com", NULL }

Set notypes to 1 if you don’t want to include the attribute names in the array as in:

ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 1 )

This function then returns this array:

{ "bjensen", "People", "example.com", NULL }

Getting Attribute Names from an Entry

To retrieve the name of the first attribute in an entry, call the ldap_first_attribute() function. To get the name of the next attribute, call the ldap_next_attribute() function.


Note

Operational attributes such as creatorsName and modifyTimestamp are not normally returned in search results unless you explicitly specify them by name in the search request. For more details, see "Operational Attributes".


When you are finished iterating through the attributes, you need to free the BerElement structure allocated by the ldap_first_attribute() function, if the structure is not NULL. To free this structure, call the ldap_ber_free() function. You should also free the attribute name returned by the ldap_first_attribute() function. To free the attribute name, call the ldap_memfree() function. Code Example 5-10 illustrates how to retrieve each attribute of an entry.

Code Example 5-10  Retrieving Entry Attributes 

#include <stdio.h>

#include <ldap.h>

...

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* Get the first matching entry.*/

e = ldap_first_entry( ld, result );

/* Retrieve the attributes of the entry. */

for (a = ldap_first_attribute(ld, e, &ber); a != NULL;

a = ldap_next_attribute(ld, e, ber)){

...

/* Code to get and manipulate attribute values */

...

}

ldap_memfree( a );

}

/* Free the BerElement structure from memory when done. */

if ( ber != NULL ) {

ldap_ber_free( ber, 0 );

}

...

Getting the Values of an Attribute

The values of an attribute are represented by a NULL-terminated array. The values are either a list of strings (if the attribute contains string data, such as a name or phone number) or a list of berval structures (if the attribute contains binary data, such as a JPEG file or an audio file). Use the following guidelines to retrieve the values of an attribute.

When you have finished working with the values of the attribute, you need to free them from memory. To do this, call ldap_value_free() or ldap_value_free_len(). Code Example 5-11 gets and prints the values of an attribute in an entry. It assumes that all attributes have string values.

Code Example 5-11  Retrieving Attribute Values 

#include <stdio.h>

#include <ldap.h>

...

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a;

char **vals;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

int i;

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* Get the first matching entry.*/

e = ldap_first_entry( ld, result );

/* Get the first matching attribute. */

a = ldap_first_attribute( ld, e, &ber );

/* Get the values of the attribute. */

if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {

for ( i = 0; vals[i] != NULL; i++ ) {

/* Print the name of the attribute and each value */

printf( "%s: %s\n", a, vals[i] );

}

/* Free the attribute values from memory when done. */

ldap_value_free( vals );

}

...

Code Example 5-12 gets the first value of the jpegPhoto attribute and saves the JPEG data to a file.

Code Example 5-12  Getting and Saving Attribute Value 

#include <stdio.h>

#include <ldap.h>

...

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a;

struct berval photo_data;

struct berval **list_of_photos;

FILE *out;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* Get the first matching entry.*/

e = ldap_first_entry( ld, result );

/* Find the jpegPhoto attribute. */

a = ldap_first_attribute( ld, e, &ber );

while ( strcasecmp( a, "jpegphoto" ) != 0 ) {

a = ldap_next_attribute( ld, e, ber );

}

/* Get the value of the attribute. */

if ( ( list_of_photos = ldap_get_values_len( ld, e, a ) ) != NULL ) {

/* Prepare to write the JPEG data to a file */

if ( ( out = fopen( "photo.jpg", "wb" ) ) == NULL ) {

perror( "fopen" );

return( 1 );

}

/* Get the first JPEG. */

photo_data = *list_of_photos[0];

/* Write the JPEG data to a file */

fwrite( photo_data.bv_val, photo_data.bv_len, 1, out );

fclose( out );

/* Free the attribute values from memory when done. */

ldap_value_free_len( list_of_photos );

}

...

Getting Referrals from Search References

A search reference returned from the server contains one or more referrals (LDAP URLs that identify other LDAP servers). To retrieve these referrals, you need to call the ldap_parse_reference() function. Code Example 5-13 gets and prints the referrals from a search reference.

Code Example 5-13  Obtaining a Referral 

#include <stdio.h>

#include "ldap.h"

...

LDAP *ld;

LDAPMessage *msg;

char **referrals;

int i, rc, parse_rc;

...

parse_rc = ldap_parse_reference( ld, msg, &referrals, NULL, 0 );

if ( parse_rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_parse_result: %s\n",

ldap_err2string( parse_rc ) );

ldap_unbind( ld );

return( 1 );

}

if ( referrals != NULL ) {

for ( i = 0; referrals[ i ] != NULL; i++ ) {

printf( "Search reference: %s\n\n", referrals[ i ] );

}

ldap_value_free( referrals );

}

...


Sorting the Search Results

The LDAP SDK for C contains functions to sort entries and values in the search results. You can either specify that the server return sorted results or you can sort entries on your client.

Server-Side Sorting

To sort results on the server, you need to send a server-side sorting control with the search request. For details, see "Using the Server-Side Sorting Control" in Chapter 13, "Working with LDAP Controls."

Client-Side Sorting

First, you need to retrieve the attributes that you plan to use for sorting. For example, if you plan to sort the results by email address, make sure that the mail attribute is one of the attributes returned in the search.

Sorting Entries by an Attribute

To sort the search results by a particular attribute, call the ldap_sort_entries() function. If you don’t specify an attribute for sorting (that is, if you pass NULL for the attr parameter), the entries are sorted by DN. Code Example 5-14 sorts entries by the roomNumber attribute:

Code Example 5-14  Sorting Entries by Attribute Value 

#include <stdio.h>

#include <string.h>

#include <ldap.h>

...

LDAP *ld;

LDAPMessage *result;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

char *sortby = "roomNumber";

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* Sort the results by room number, using strcasecmp. */

if (ldap_sort_entries(ld, &result, sortby, strcasecmp) != LDAP_SUCCESS){

ldap_perror( ld, "ldap_sort_entries" );

return( 1 );

}

...

Sorting Entries by Multiple Attributes

To sort the search results by multiple attributes, call the ldap_multisort_entries() function. If you don’t specify a set of attributes for sorting (that is, if you pass NULL for the attr parameter), the entries are sorted by DN. Code Example 5-15 sorts entries first by the roomNumber attribute, then by the telephoneNumber attribute.

Code Example 5-15  Sorting Entries by Multiple Values

#include <stdio.h>

#include <string.h>

#include <ldap.h>

LDAP *ld;

LDAPMessage *res;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

char *attrs[2];

attrs[0] = “roomNumber”;

attrs[1] = “telephoneNumber”;

attrs[2] = NULL;

...

/* Search the directory. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter,

NULL, 0, &res ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* Sort the results, using strcasecmp. */

if (ldap_multisort_entries(ld,&res,attrs, strcasecmp) != LDAP_SUCCESS){

ldap_perror( ld, "ldap_sort_entries" );

return( 1 );

}

Sorting the Values of an Attribute

You can also sort the values of a particular attribute. To do this, call the ldap_sort_strcasecmp() function. In this function, the comparison function must pass parameters of the type char **. You should use the ldap_sort_strcasecmp() function, rather than a function like strcasecmp() (which passes parameters of the type char *). Code Example 5-16 sorts the values of attributes before printing them.

Code Example 5-16  Sorting Attribute Values 

#include <stdio.h>

#include <string.h>

#include <ldap.h>

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a, *dn;

char **vals;

int i;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(sn=Jensen)";

...

if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {

/* Sort the values of the attribute */

if (ldap_sort_values(ld, vals, strcasecmp)) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_sort_values" );

return( 1 );

}

/* Print the values of the attribute. */

for ( i = 0; vals[i] != NULL; i++ ) {

printf( "%s: %s\n", a, vals[i] );

}

/* Free the values from memory. */

ldap_value_free( vals );

}

...


Freeing the Search Results

The results of the search are returned in an LDAPMessage structure. After you are done working with the search results, you should free this structure from memory. To free the search results, call the ldap_msgfree() function which returns the type of the last message freed from memory.


Search Examples

This section contains sample code for some search operations.

Reading an Entry with a Search

You can use the search functions to read a specific entry in the directory. To read an entry, set the starting point of the search to the entry, and set the scope of the search to LDAP_SCOPE_BASE, specifying (objectclass=*) as the search filter. Code Example 5-17 prints the attributes of the entry for Barbara Jensen.

Code Example 5-17  Reading a Specific Entry with a Search 

#include <stdio.h>

#include "ldap.h"

/* Change these as needed. */

#define HOSTNAME "localhost"

#define PORT_NUMBER LDAP_PORT

#define FIND_DN "uid=bjensen,ou=People,dc=example,dc=com"

int

main( int argc, char **argv )

{

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a;

char **vals;

int i, rc;

/* Get a handle to an LDAP connection. */

if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) {

perror( "ldap_init" );

return( 1 );

}

/* Bind anonymously to the LDAP server. */

if ( ( rc = ldap_simple_bind_s( ld, NULL, NULL ) ) != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );

return( 1 );

}

/* Search for the entry. */

if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, "(objectclass=*)",

NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result ) ) != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );

return( 1 );

}

/* Since we are doing a base search, there should be only one matching entry. */

e = ldap_first_entry( ld, result );

if ( e != NULL ) {

printf( "\nFound %s:\n\n", FIND_DN );

/* Iterate through each attribute in the entry. */

for ( a = ldap_first_attribute( ld, e, &ber );

a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {

/* For each attribute, print the attribute name and values. */

if ((vals = ldap_get_values( ld, e, a)) != NULL ) {

for ( i = 0; vals[i] != NULL; i++ ) {

printf( "%s: %s\n", a, vals[i] );

}

ldap_value_free( vals );

}

ldap_memfree( a );

}

if ( ber != NULL ) {

ber_free( ber, 0 );

}

}

ldap_msgfree( result );

ldap_unbind( ld );

return( 0 );

}

Listing Subentries with a Search

You can use the search functions to list the subentries under a specific entry in the directory. To list the subentries, set the starting point of the search to the entry, and set the scope of the search to LDAP_SCOPE_ONELEVEL. Code Example 5-18 lists all entries one level below the dc=example,dc=com entry in the directory hierarchy.

Code Example 5-18  Listing Subentries 

#include <stdio.h>

#include <ldap.h>

LDAP *ld;

LDAPMessage *result, *e;

BerElement *ber;

char *a, *dn;

char **vals;

char *my_searchbase = "dc=example,dc=com";

char *my_filter = "(objectclass=*)"

/* Search one level under the starting point. */

if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_ONELEVEL, my_filter,

NULL, 0, &result ) != LDAP_SUCCESS ) {

ldap_perror( ld, "ldap_search_s" );

return( 1 );

}

/* For each matching entry, print the entry name and its attributes. */

for ( e = ldap_first_entry( ld, result ); e != NULL;

e = ldap_next_entry( ld, e ) ) {

if ( ( dn = ldap_get_dn( ld, e ) ) != NULL ) {

printf( "dn: %s\n", dn );

ldap_memfree( dn );

}

for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL;

a = ldap_next_attribute( ld, e, ber ) ) {

if ( ( vals = ldap_get_values( ld, e, a ) ) != NULL ) {

for ( i = 0; vals[i] != NULL; i++ ) {

printf( "%s: %s\n", a, vals[i] );

}

ldap_value_free( vals );

}

ldap_memfree( a );

}

if ( ber != NULL ) {

ldap_ber_free( ber, 0 );

}

printf( "\n" );

}

ldap_msgfree( result );

...

Sending A Search Request

Code Example 5-19 illustrates how to send a search request to the server for all entries with the last name (or surname) Jensen in the example.com organization.

Code Example 5-19  Sample Code to Send a Search Request 

#include <stdio.h>

#include "ldap.h"

...

#define BASEDN "dc=example,dc=com"

#define SCOPE LDAP_SCOPE_SUBTREE

#define FILTER "(sn=Jensen)"

...

LDAP *ld;

int msgid, rc;

...

/* Send the search request. */

rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL,

NULL, NULL, LDAP_NO_LIMIT, &msgid );

if ( rc != LDAP_SUCCESS ) {

fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );

ldap_unbind( ld );

return( 1 );

}

...



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.