[Previous] [Next] [TOC] [Index]

Chapter 4
Searching the Directory

o find entries in the directory, you need to specify search criteria and call one of the LDAP API search functions. These functions return a list of matching entries. You can call other API functions to iterate through each entry in the results, retrieving the values of specific attributes in each entry.

This chapter contains the following sections, which explain how to create and execute a search of the directory:

Specifying Search Criteria

To search the directory, you need to specify the following information:

Figure 4.1 illustrates how the search criteria determines the section of the directory that you search.

Figure 4.1: Search criteria

Simple Search Syntax

When you search the directory, you use a search filter to define the search. Here is the basic syntax for a search filter:


(attribute operator value)
Here is a simple example of a search filter:


(cn=Barbara Jensen)
In this example, 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 slapd.at.conf file for your directory server.

Table 4.1 lists the valid operators you can use.
Table 4.1: 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"

Note that when comparing values containing letters, 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)

Advanced Search Syntax

Using Boolean operators and parentheses, you can combine different sets of conditions. Here is the syntax for combining search filters:

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

Table 4.2 lists the valid boolean operators you can use.
Table 4.2: Boolean operators for search filters
Boolean 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))

For example, you can use this filter to search for all entries with the last name Jensen or the last name Johnson:

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

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*)

Initiating a Search

To search the directory, call the ldap_search_s(), ldap_search_st(), or ldap_search() function.

For more information about the difference between synchronous and asynchronous functions, see "Synchronous and Asynchronous Functions".

Specifying the scope of the search

To specify the scope of the search, you pass one of the following values as the scope parameter:

Example of a subtree search

The following example initiates a search for all entries with the last name (or surname) "Jensen" in the Ace Industry organization. The search retrieves the names and values of the cn and mail attributes.


#include <stdio.h>
#include <ldap.h>
LDAP *ld;
LDAPMessage *result;
char *my_searchbase = "o=Ace Industry, c=US";
char *my_filter = "(sn=Jensen)";
char *get_attr[] = { "cn", "mail", NULL };
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_SUBTREE, my_filter, 
         get_attr, 0, &result ) != LDAP_SUCCESS ) {
   ldap_perror( ld, "ldap_search_s" );
   return( 1 );
}

Getting Search Results

When you call the ldap_search_s() or ldap_search_st() function to search the directory, the result parameter returns a handle to an LDAPMessage structure. This structure represents the search results.

If you use the asynchronous function ldap_search() instead, you need to call ldap_result() to get the handle to the LDAPMessage structure representing the search results.

The search results consist of a chain of entries that match the search criteria. To access individual results, you need to follow this general process:

  1. Get the entries from the chain, one at a time.

  2. Get the attribute names from each entry, one at a time.

  3. Get the values from each attribute.
Figure 4.5 illustrates the relationship between entries, attributes, values, and search results.

Figure 4.5: Search results in terms of entries, attributes, and values

Getting Entries from the Results

To get the first entry in the chain of search results, call the ldap_first_entry() function. The function returns a handle to an LDAPMessage structure representing the first entry in the chain of results.

To get subsequent entries, call the ldap_next_entry() function. The function returns a handle to the LDAP message representing the next entry in the chain of results. If the function returns a NULL, there are no more entries in the chain of search results.

If you want to iterate through the entries in the chain of search results, you need to get the number of results. To get this, call the ldap_count_entries() function. The function returns the number of entries in the search results (0 if no search results found).

The following section of code retrieves each entry in a set of search results.


#include <stdio.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result, *e;
char *my_searchbase = "o=Ace Industry, c=US";
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 );
}
/* Check whether any results were found. */
if ( ldap_count_entries( ld, result ) == 0 ) {
   printf( "No matching results found.\n" );
   return( 0 );
}

/* Retrieve each entry from the search results. */
for ( e = ldap_first_entry( ld, result ); e != NULL; 
         e = ldap_next_entry( ld, e ) ) {
   ...
   /* code for getting data from the entries */
   ...
}
/* Free the result when done. */
ldap_msgfree( result );
...

Getting Distinguished Names from the Results

Because the distinguished name of an entry differentiates it from other entries, you may want to access the distinguished names of entries in the search results. You may also want to parse the name into its individual components.

The LDAP API provides functions for both of these tasks. You can call functions to get the name of an individual entry and to split the name into its components.

Getting the Distinguished Name of an Entry

To get the distinguished name of an entry, call the ldap_get_dn() function. The function returns the distinguished name of the entry.

When you are finished working with the distinguished name returned by this function, you should free it from memory by calling the ldap_memfree() function.

The following section of code prints the distinguished name for each entry found in a search.


#include <stdio.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result, *e;
char *dn;
char *my_searchbase = "o=Ace Industry, c=US";
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 distinguished name or a relative distinguished name, call the ldap_explode_dn() and ldap_explode_rdn() functions.

Both functions return a NULL-terminated array of the components of the distinguished name. When you are done working with this array, you should free it by calling the ldap_value_free() function.

You can specify whether or not you want the names of the components included in the array by using the notypes parameter.

Getting Attributes from an Entry

To get the value of the first attribute in an entry, call the ldap_first_attribute() function.

This function returns the name of the first attribute in the entry. To get the value of this attribute, you need to pass the attribute name to the ldap_get_values() or ldap_get_values_len() functions. (See "Getting the Values of an Attribute" for details.)

To get the name of the next attribute, call the ldap_next_attribute() function.

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.

The following section of code retrieves each attribute of an entry.


#include <stdio.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a;
char *my_searchbase = "o=Ace Industry, c=US";
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).

To get the number of values in an attribute, call the ldap_count_values() or ldap_count_values_len() function. Both functions return the number of values in the attribute.

When you have finished working with the values of the attribute, you need to free the values from memory. To do this, call the ldap_value_free() or ldap_value_free_len() function.

The following section of code gets and prints the values of an attribute in an entry. This example assumes that all attributes have string values.


#include <stdio.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a;
char **vals;
char *my_searchbase = "o=Ace Industry, c=US";
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 );
}
...
The following section of code gets the first value of the jpegPhoto attribute and saves the JPEG data to a file.


#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 = "o=Ace Industry, c=US";
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 );
}
...

Freeing the Results of a Search

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. The ldap_msgfree function returns the type of the last message freed from memory.

Example: Searching the Directory

The following section of code prints out the values of all attributes in the entries returned by a search.

To see sample programs that search the directory, take a look at the asearch.c and search.c source files in the examples directory.


#include <stdio.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a, *dn;
char **vals;
char *my_searchbase = "o=Ace Industry, c=US";
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, 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 );

Sorting the Search Results

The LDAP API contains functions that you can use to sort entries and values in the search results. You can call functions to do the following:

Sorting Entries by an Attribute

To sort the search results by a particular attribute, call the ldap_sort_entries() function. Note that 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.

The following section of code sorts entries by the roomNumber attribute.


#include <stdio.h>
#include <string.h>
#include <ldap.h>
...
LDAP *ld;
LDAPMessage *result;
char *my_searchbase = "o=Ace Industry, c=US";
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 a particular attribute, call the ldap_multisort_entries() function. Note that 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.

The following section of code sorts entries first by the roomNumber attribute, then by the telephoneNumber attribute.


#include <stdio.h>
#include <string.h>
#include <ldap.h>
LDAP *ld;
LDAPMessage *res;
char *my_searchbase = "o=Ace Industry, c=US";
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_values() function.

In the ldap_sort_values() 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 *).

The following section of code sorts the values of attributes before printing them.


#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 = "o=Ace Industry, c=US";
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 );
      }
...

Reading an Entry

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 and specify (objectclass=*) for the search filter.

Using the LDAP_SCOPE_BASE scope to read an entry

The following section of code prints the attributes of the entry for Barbara Jensen.

To see a sample program that reads an entry in the directory, see the rdentry.c source file in the examples directory.


#include <stdio.h>
#include <ldap.h>
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a, *dn;
char **vals;
char *my_searchbase = "cn=Barbara Jensen, o=Ace Industry, c=US";
char *my_filter = "(objectclass=*)";
/* Search the directory. */
if ( ldap_search_s( ld, my_searchbase, LDAP_SCOPE_BASE, 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 );

Listing Subentries

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.

Using the LDAP_SCOPE_ONELEVEL scope to list subentries

The following section of code lists all entries directly under the Ace Industries entry.


#include <stdio.h>
#include <ldap.h>
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a, *dn;
char **vals;
char *my_searchbase = "o=Ace Industry, c=US";
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 );
...


[Previous] [Next] [TOC] [Index]

Last modified: March 31, 1997
Copyright © 1997 Netscape Communications Corporation