richm%stanfordalumni.org dd758b072f initial import of docbook contribution from Sun
git-svn-id: svn://10.0.0.236/trunk@228382 18797224-902f-48f8-a5cc-f745e15eee43
2007-06-20 14:26:52 +00:00

1739 lines
70 KiB
Plaintext

<!--
Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
Portions copyright 1999 Netscape Communications Corporation. All
Rights Reserved.
The contents of this document are subject to the terms of the
Creative Commons Attribution-ShareAlike 2.5 license or any later
version (the "License"). You may not use this document except in
compliance with the License.
See the License for the specific language governing
permissions and limitations under the License. You can obtain
a copy of the License at
http://creativecommons.org/licenses/by-sa/2.5/legalcode.
-->
<chapter id="csdk-searching"><title>Searching the Directory With &DirectorySDKForC;
</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
</indexterm><indexterm>
<primary>searching the directory</primary>
<secondary>with C SDK<?Pub Caret></secondary>
</indexterm><highlights>
<para>This chapter explains how to use the LDAP C API to search the directory
and to retrieve entries.</para>
<itemizedlist>
<para>This chapter covers the following topics:</para>
<listitem><para><olink targetptr="bdaed">Overview of Search Functions for
Directory SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdaee">Sending a Search Request With Directory
SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdaem">Getting the Search Results With Directory
SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdaew">Sorting the Search Results With Directory
SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdafc">Freeing the Search Results With Directory
SDK for C</olink></para></listitem>
<listitem><para><olink targetptr="bdafd">Examples of Search Operations With
Directory SDK for C</olink></para></listitem>
</itemizedlist>
</highlights>
<sect1 id="bdaed"><title>Overview of Search Functions for Directory SDK for
C</title>
<para>&DirectorySDKForC; provides functions that allow you to search a
directory and to retrieve results from the server. For example, you can send
a search request by calling the synchronous <function>ldap_search_ext_s</function> function
or the asynchronous <function>ldap_search_ext</function> function and the
server sends back matching results.</para>
<itemizedlist>
<para>In LDAP v3, a server can send three different types of results, represented
by <structname>LDAPMessage</structname> structures, back to the client:</para>
<listitem><para>Directory entries found by the search</para></listitem>
<listitem><para>Search references found within the scope of the search</para>
<para>A <firstterm>search reference</firstterm> is a reference to another
LDAP server.</para></listitem>
<listitem><para>An LDAP result code that specifies the result of the search
operation</para></listitem>
</itemizedlist>
<note><para>To receive search references from LDAP v3 servers, you must identify
your client as LDAP v3 enabled. If you do not, the server returns the LDAP
error code <errorcode>LDAP_PARTIAL_RESULTS</errorcode>, and a set of referrals.
See <olink targetptr="bdaci">Specifying the LDAP Version of Your Client</olink> for
details.</para></note>
<itemizedlist>
<para>These results can be processed based on the following guidelines:</para>
<listitem><para>If you are retrieving the results sequentially, call <function>ldap_result
</function>. This function returns each result, an <structname>LDAPMessage</structname> structure,
and determines the type of result. A result can be either an entry or a search
reference.</para></listitem>
<listitem><para>If you are retrieving a chain of results, you can call <function>
ldap_first_message</function> and <function>ldap_next_message</function> to
iterate through the results in the chain.</para><para>If you are just interested
in entries, you can call <function>ldap_first_entry</function> and <function>ldap_next_entry
</function>.</para><para>If you are just interested in search references,
you can call <function>ldap_first_reference</function> and <function>ldap_next_refrence
</function>.</para></listitem>
<listitem><para>To get an entry from a result, an <structname>LDAPMessage</structname> structure,
call <function>ldap_parse_entry</function>.</para></listitem>
<listitem><para>To get a search reference from a result, an <structname>LDAPMessage
</structname> structure, call <function>ldap_parse_reference</function>.</para>
</listitem>
<listitem><para>To get the LDAP result code for the search operation from
a result, an <structname>LDAPMessage</structname> structure, call <function>ldap_parse_result
</function>.</para></listitem>
</itemizedlist>
<tip><para>To access data from entries found by the search, you need to follow
this general process in your code.</para>
<orderedlist>
<listitem><para>Get each entry in the results.</para></listitem>
<listitem><para>Get the attributes from each entry.</para></listitem>
<listitem><para>Get the values from each attribute.</para></listitem>
</orderedlist>
</tip>
</sect1>
<sect1 id="bdaee"><title>Sending a Search Request With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>request</tertiary>
</indexterm>
<para>To search the directory, call <function>ldap_search_ext_s</function> or <function>
ldap_search_ext</function>. <function>ldap_search_ext_s</function> is a synchronous
function that blocks other work until all results have been received from
the server. The function is declared as shown here.</para>
<example id="ldap-search-ext-s-prototype"><title><function>ldap_search_ext_s</function> Prototype
</title>
<programlisting>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 );</programlisting>
</example>
<para><function>ldap_search_ext</function> 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. The function is declared as
shown here.</para>
<example id="ldap-search-ext-prototype"><title><function>ldap_search_ext</function> Prototype
</title>
<programlisting>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 );</programlisting>
</example>
<para>Sample code for sending a search request can be found in <olink targetptr="bdafg">Sending Search Request Using Directory SDK for C</olink>.</para>
<sect2 id="bdaef"><title>Search Parameters for Directory SDK for C</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>parameters</tertiary>
</indexterm>
<para>For either of the functions illustrated in <olink targetptr="ldap-search-ext-s-prototype">Example 9&ndash;1</olink> and <olink targetptr="ldap-search-ext-prototype">Example 9&ndash;2</olink>, you specify
the search criteria by using the parameters as detailed in the following table.</para>
<table frame="topbot" pgwide="1" id="search-criteria-parameters"><title>Search
Criteria Parameters</title>
<tgroup cols="2"><colspec colnum="1" colwidth="16.50*"><colspec colnum="2"
colwidth="83.50*">
<thead>
<row>
<entry>
<para>Parameter Name</para></entry>
<entry>
<para>Description</para></entry>
</row>
</thead>
<tbody>
<row>
<entry>
<para><literal>base</literal></para></entry>
<entry>
<para>Specifies the starting point in the directory, or the base DN, where
the search begins. For example, to search entries under <literal>dc=example,dc=com
</literal>, the base DN is <literal>dc=example,dc=com</literal>. See <olink targetptr="bdaeg">Specifying the Base DN and the Scope With Directory SDK
for C</olink>.</para></entry>
</row>
<row>
<entry>
<para><literal>scope</literal></para></entry>
<entry>
<para>Specifies which entries to search. The search can address the base DN
only, entries one level under the base DN, or all entries under the base DN.
See <olink targetptr="bdaeg">Specifying the Base DN and the Scope With Directory
SDK for C</olink>.</para></entry>
</row>
<row>
<entry>
<para><literal>filter</literal></para></entry>
<entry>
<para>Specifies a search filter by defining what to search for. A search filter
can be as simple as &ldquo;find entries with the last name of Jensen&rdquo;
or as complex as &ldquo;find entries that belong to Dept. #17 and whose first
names start with the letter F.&rdquo; See <olink targetptr="bdaeh">Specifying
a Search Filter With Directory SDK for C</olink>.</para></entry>
</row>
<row>
<entry>
<para><literal>attrsattrsonly</literal></para></entry>
<entry>
<para>Specify what to return. Options are the type of information to return,
and the attributes to retrieve. Also, options include whether to return only
attribute types, or both types and values. You can also specify to return
the names of attributes only, and not the values, by passing a nonzero value
for the <literal>attrsonly</literal> argument. See <olink targetptr="bdaei">Specifying
the Attributes to Retrieve With Directory SDK for C</olink>.</para></entry>
</row>
<row>
<entry>
<para><literal>serverctrlsclientctrls</literal></para></entry>
<entry>
<para>Specify the LDAP v3 controls that are associated with this search operation.
For details on LDAP v3 controls, see <olink targetptr="csdk-controls">Chapter&nbsp;16,
LDAP Controls With Directory SDK for C</olink>.</para></entry>
</row>
<row>
<entry>
<para><literal>timeoutpsizelimit</literal></para></entry>
<entry>
<para>Specify search constraints that you want applied to the search. For
example, you can specify a different timeout period or maximum number of results
from the values already defined for the current session. See <olink targetptr="bdael">Setting Search Preferences With Directory SDK for C</olink>.</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 id="bdaeg"><title>Specifying the Base DN and the Scope With &DirectorySDKForC;
</title>
<para>When sending a search request, you need to specify the base DN and the
search scope 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.</para>
<itemizedlist>
<para>To specify the scope of the search, pass one of the following values
as the <literal>scope</literal> parameter:</para>
<listitem><para><literal>LDAP_SCOPE_SUBTREE</literal> searches the base entry
and all entries at all levels under the base entry.</para></listitem>
<listitem><para><literal>LDAP_SCOPE_ONELEVEL</literal> searches all entries
one level under the base entry. The base entry is not included in the search.
Use this setting if you just want to list the entries under a given entry.</para>
</listitem>
<listitem><para><literal>LDAP_SCOPE_BASE</literal> searches only the base
entry. Use this setting if you just want to read the attributes of the base
entry.</para></listitem>
</itemizedlist>
</sect2>
<sect2 id="bdaeh"><title>Specifying a Search Filter With Directory SDK for
C</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>filters</tertiary>
</indexterm>
<para>When you search the directory, you use a search filter to define the
search. The following example illustrates the search filter syntax:</para>
<variablelist>
<varlistentry><term>(<replaceable>attribute</replaceable> <replaceable>operator</replaceable> <replaceable>
value</replaceable>)</term>
<listitem><para>(cn=Barbara Jensen)</para>
</listitem>
</varlistentry>
</variablelist>
<para>Compare the syntax to the example. You see that <literal>cn</literal> is
the attribute, that <literal>=</literal> is the operator, and that <literal>Barbara
Jensen</literal> is the value. The filter finds entries with the common name <literal>
Barbara Jensen</literal>. For a list of valid attributes for your search filter,
see the LDAP schema for the directory server you are using. For a list of
valid operators that you can use in your search filter, see the following
table.</para>
<table frame="topbot" pgwide="1" id="search-filter-operators"><title>Basic
Operators for Search Filters</title>
<tgroup cols="3"><colspec colnum="1" colwidth="12.04*"><colspec colnum="2"
colwidth="58.87*"><colspec colnum="3" colwidth="56.91*">
<thead>
<row rowsep="1">
<entry colsep="0">
<para>Operator</para></entry>
<entry colsep="0">
<para>Description</para></entry>
<entry colsep="0">
<para>Example</para></entry>
</row>
</thead>
<tbody>
<row rowsep="1">
<entry colsep="0">
<para><literal>=</literal></para></entry>
<entry colsep="0">
<para>Returns entries whose attribute is equal to the value.</para></entry>
<entry colsep="0">
<para><literal>(cn=Barbara Jensen)</literal> finds the entry with RDN <literal>cn=Barbara
Jensen</literal>.</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>>=</literal></para></entry>
<entry colsep="0">
<para>Returns entries whose attribute is greater than or equal to the value.</para>
</entry>
<entry colsep="0">
<para><literal>(sn >= jensen)</literal> finds all entries with surname (SN)
from <literal>jensen</literal> to the end of the alphabetical list.</para>
</entry>
</row>
<row>
<entry colsep="0">
<para><literal>&lt;=</literal></para></entry>
<entry colsep="0">
<para>Returns entries whose attribute is less than or equal to the value.</para>
</entry>
<entry colsep="0">
<para><literal>(sn &lt;= jensen)</literal> finds all entries with SN from
the beginning of the alphabetical list to <literal>jensen</literal>.</para>
</entry>
</row>
<row>
<entry colsep="0">
<para><literal>=*</literal></para></entry>
<entry colsep="0">
<para>Returns entries that have a value set for that attribute.</para></entry>
<entry colsep="0">
<para><literal>(sn =*)</literal> finds all entries that have the <literal>sn</literal> attribute.
</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>~=</literal></para></entry>
<entry colsep="0">
<para>Returns entries whose attribute value approximately matches the specified
value. Typically, the algorithm matches words that sound alike.</para></entry>
<entry colsep="0">
<para><literal>(sn ~= jensen)</literal> finds entries with <literal>sn = jensen</literal> but
also <literal>sn = jansen</literal>.</para></entry>
</row>
</tbody>
</tgroup>
</table>
<para>With boolean operators and with parentheses, you can combine different
sets of conditions into one filter. The following shows boolean search filter
syntax for combining filters, and a simple example:</para>
<variablelist>
<varlistentry><term>(<replaceable>boolean-operator</replaceable>(<replaceable>filter1
</replaceable>)(<replaceable>filter2</replaceable>)(<replaceable>filter3</replaceable>))
</term>
<listitem><para>(|(sn=Jensen)(sn=Johnson))</para>
</listitem>
</varlistentry>
</variablelist>
<para>The example uses the boolean or operator, <literal>|</literal>, to signify
a search for all entries with the last name <literal>Jensen</literal> or the
last name <literal>Johnson</literal>. The following table describes the valid
boolean operators that you can use in your search filter.</para>
<table frame="topbot" pgwide="1" id="search-filter-booleans"><title>Boolean
Operators for Search Filters</title>
<tgroup cols="2"><colspec colnum="1" colwidth="9.43*"><colspec colnum="2"
colwidth="90.57*">
<thead>
<row rowsep="1">
<entry colsep="0">
<para>Operator</para></entry>
<entry colsep="0">
<para>Description</para></entry>
</row>
</thead>
<tbody>
<row rowsep="1">
<entry colsep="0">
<para><literal>&amp;</literal></para></entry>
<entry colsep="0">
<para>Returns entries that match all specified filter criteria.</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>|</literal></para></entry>
<entry colsep="0">
<para>Returns entries that match one or more of the filter criteria.</para>
</entry>
</row>
<row>
<entry colsep="0">
<para><literal>!</literal></para></entry>
<entry colsep="0">
<para>Returns entries for which the filter is not true. You can only apply
this operator to a single filter. For example: You can use:</para>
<programlisting>(!(<replaceable>filter</replaceable>))</programlisting>
<para>You cannot use:</para>
<programlisting>(!(<replaceable>filter1</replaceable>)(<replaceable>filter2</replaceable>))
</programlisting>
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>You can also include wildcard characters, <literal>*</literal>, 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 <literal>F</literal>:</para>
<programlisting>(givenName=F*)</programlisting>
<para>When comparing values with letters, the value of the letter <literal>a</literal> is
less than the value <literal>z</literal>. For example, the following filter
finds all entries with last names beginning with <literal>a</literal> through <literal>
Jensen</literal>:</para>
<programlisting>(sn&lt;=jensen)</programlisting>
</sect2>
<sect2 id="bdaei"><title>Specifying the Attributes to Retrieve With Directory
SDK for C</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>attributes</tertiary>
</indexterm>
<para>With the <literal>attrs</literal> argument, you can retrieve all attributes
in the entries returned by the search. Alternatively, you can specify the
attributes from the search results.</para>
<itemizedlist>
<para>To specify attributes, use the following guidelines:</para>
<listitem><para>To return selected attributes, pass an array of the attribute
names as the <literal>attrs</literal> argument. For example, to return only
email addresses and phone numbers, pass the <literal>NULL</literal> terminated
array <literal>{"mail", "telephoneNumber", NULL}</literal> as the <literal>attrs</literal> argument.
</para></listitem>
<listitem><para>To return all attributes in an entry, pass <literal>NULL</literal> as
the <literal>attrs</literal> argument.</para></listitem>
<listitem><para>To return no attributes from an entry, pass <literal>LDAP_NO_ATTRS
</literal> as the <literal>attrs</literal> argument.</para></listitem>
</itemizedlist>
<sect3 id="bdaej"><title>Sorting Attributes</title>
<para>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 <literal>mail</literal> attribute is
returned in the search results. See <olink targetptr="bdaew">Sorting the Search
Results With Directory SDK for C</olink>.</para></sect3>
<sect3 id="bdaek"><title>Operational Attributes</title>
<para>Some attributes are used by servers for administering the directory.
For example, the <literal>creatorsName</literal> attribute specifies the DN
of the user who added the entry. These attributes are called <firstterm>operational
attributes</firstterm>.</para>
<para>Servers do not normally return operational attributes in search results
unless you specify the attributes by name. For example, you can pass <literal>NULL
</literal> for the <literal>attrs</literal> argument to retrieve all of the
attributes in entries found by the search. When you pass this value, the operational
attribute <literal>creatorsName</literal> is not returned to your client.
You need to explicitly specify the <literal>creatorsName</literal> attribute
in the <literal>attrs</literal> argument. You can retrieve all attributes
in an entry, as well as selected operational attributes. Pass a <literal>NULL</literal> terminated
array that contains <literal>LDAP_ALL_USER_ATTRS</literal>. Also, pass the
names of the operational attributes as the <literal>attrs</literal> argument.
The following table lists operational attributes and explains the meaning
of their values.</para>
<table frame="topbot" pgwide="1" id="search-operational-attributes"><title>Operational
Attributes and Descriptions of Their Values</title>
<tgroup cols="2"><colspec colnum="1" colwidth="17.20*"><colspec colnum="2"
colwidth="82.80*">
<thead>
<row rowsep="1">
<entry colsep="0">
<para>Attribute Name</para></entry>
<entry colsep="0">
<para>Description of Values</para></entry>
</row>
</thead>
<tbody>
<row rowsep="1">
<entry colsep="0">
<para><literal>createTimestamp</literal></para></entry>
<entry colsep="0">
<para>The time that the entry was added to the directory</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>modifyTimestamp</literal></para></entry>
<entry colsep="0">
<para>The time that the entry was last modified</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>creatorsName</literal></para></entry>
<entry colsep="0">
<para>DN of the user who added the entry to the directory</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>modifiersName</literal></para></entry>
<entry colsep="0">
<para>DN of the user who last modified the entry</para></entry>
</row>
<row>
<entry colsep="0">
<para><literal>subschemaSubentry</literal></para></entry>
<entry colsep="0">
<para>DN of the subschema entry, which controls the schema for this entry</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
</sect2>
<sect2 id="bdael"><title>Setting Search Preferences With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>preferences</tertiary>
</indexterm>
<para>For a given search, you can specify the maximum number of results to
be returned. Alternatively, you can specify the maximum amount of time to
wait for a search.</para>
<itemizedlist>
<para>Use the <literal>timeoutp</literal> and <literal>sizelimit</literal> arguments
of the <function>ldap_search_ext_s</function> or the <function>ldap_search_ext</function> functions
with the following guidelines:</para>
<listitem><para>To specify an infinite time limit, in other words, no limit,
create a <literal>timeval</literal> structure with <literal>tv_sec = tv_usec
= 0</literal>. Then pass a pointer to the structure as the <literal>timeoutp</literal> argument.
</para></listitem>
<listitem><para>To use the time limit specified by the <literal>LDAP_OPT_TIMELIMIT
</literal> option for this connection, pass <literal>NULL</literal> as the <literal>
timeoutp</literal> argument.</para></listitem>
<listitem><para>To specify an infinite size limit, in other words, no limit,
pass <literal>LDAP_NO_LIMIT</literal> as the <literal>sizelimit</literal> argument.
</para></listitem>
<listitem><para>To use the size limit specified by the <literal>LDAP_OPT_SIZELIMIT
</literal> option for this connection, pass <literal>NULL</literal> as the <literal>
sizelimit</literal> argument.</para></listitem>
<listitem><para>To specify preferences for all searches under the current
connection, call <function>ldap_set_option</function> and set the <literal>LDAP_OPT_SIZELIMIT
</literal> and <literal>LDAP_OPT_TIMELIMIT</literal> options. </para><para>If
you do not want to specify a limit, in other words, no limit, set the value
of each option to <literal>LDAP_NO_LIMIT</literal>.</para></listitem>
</itemizedlist>
<note><para>The LDAP server administrator might already have configured time
limits and size constraints that you cannot override.</para></note>
<para>The following example sets these session preferences so that a search
returns no more than 100 entries, and takes no more than 30 seconds.</para>
<example id="set-session-search-preferences-example"><title>Sample Code to
Set Session Search Preferences</title>
<programlisting>#include &lt;stdio.h>
#include "ldap.h"
...
LDAP *ld;
int max_ret, max_tim;
char *host = "ldap.example.com";
...
/* Initialize a session with the LDAP server ldap.example.com:389. */
/* Use prldap_init() for IPv6 support. */
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 *)&amp;max_ret );
/* Set the maximum number of seconds to wait. */
max_tim = 30;
ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *)&amp;max_tim );
...</programlisting>
</example>
</sect2>
</sect1>
<sect1 id="bdaem"><title>Getting the Search Results With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>results</tertiary>
</indexterm>
<itemizedlist>
<para>In LDAP v3, the server returns search results as a chain of <structname>LDAPMessage
</structname> structures, with each structure containing the following:</para>
<listitem><para>The directory entries found by the search. In other words,
those entries that match the search criteria.</para></listitem>
<listitem><para>Any search references found within the scope of the search.
A <firstterm>search reference</firstterm> is a reference to another LDAP server.</para>
</listitem>
<listitem><para>An LDAP result code that specifies the result of the search
operation.</para></listitem>
</itemizedlist>
<note><para>Because results are represented as a chain, do not free individual <structname>
LDAPMessage</structname> structures within the chain. When you are done working
with the results, free the chain, rather than the individual structures. If
you free individual <structname>LDAPMessage</structname> structures from memory,
you might lose all of the results.</para></note>
<itemizedlist>
<para>To retrieve an individual result from a chain of <structname>LDAPMessage</structname> structures,
you can call one of the following sets of functions:</para>
<listitem><para>To get each entry and each search reference in the result,
call <function>ldap_first_message</function> and <function>ldap_next_message</function>.
Both of these functions return a pointer to an <structname>LDAPMessage</structname> structure
that represents an entry, search reference, or LDAP result code. You can get
the count of the structures in the chain by calling <function>ldap_count_messages
</function>.</para></listitem>
<listitem><para>If you want to retrieve just the entries from the chain, call <function>
ldap_first_entry</function> and <function>ldap_next_entry</function>. Both
of these functions return a pointer to an <structname>LDAPMessage</structname> structure
that represents an entry. You can get the count of the entries in the chain
by calling <function>ldap_count_entries</function>.</para></listitem>
<listitem><para>If you want to retrieve just the search references from the
chain, call <function>ldap_first_reference</function> and <function>ldap_next_reference
</function>. Both of these functions return a pointer to an <structname>LDAPMessage
</structname> structure that represents a search reference. You can get the
count of the search references in the chain by calling <function>ldap_count_references
</function>.</para></listitem>
</itemizedlist>
<sect2 id="bdaen"><title>Getting Results Synchronously</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>synchronous functions</tertiary>
</indexterm>
<para>If you call <function>ldap_search_ext_s</function> to search the directory
synchronously, the function blocks processes until all results have been received.
The function then returns a chain of results in the <literal>result</literal> parameter,
a handle to an <structname>LDAPMessage</structname> structure. The following
example prints the values of all attributes in the entries returned by a synchronous
search.</para>
<example id="search-sync-example"><title>Synchronous Searching</title>
<programlisting>#include &lt;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. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
perror( "ldap_init" );
return( 1 );
}
version = LDAP_VERSION3;
if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &amp;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, &amp;matched_msg, &amp;error_msg );
if ( error_msg != NULL &amp;&amp; *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
if ( matched_msg != NULL &amp;&amp; *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, &amp;res );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
if ( error_msg != NULL &amp;&amp; *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
if ( matched_msg != NULL &amp;&amp; *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, &amp;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, &amp;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, &amp;rc,
&amp;matched_msg, &amp;error_msg, NULL, &amp;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 &amp; *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
if ( matched_msg != NULL &amp;&amp; *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 );
}</programlisting>
</example>
</sect2>
<sect2 id="bdaeo"><title>Getting Results Asynchronously</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>asynchronous functions</tertiary>
</indexterm>
<para>If you use the asynchronous function <function>ldap_search_ext</function>,
you first need to call <function>ldap_result</function> to determine if the
server sent back any results.</para>
<example><title><function>ldap_result</function> Prototype</title>
<programlisting>LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all,
struct timeval *timeout, LDAPMessage **result );</programlisting>
</example>
<para>You can specify how you want to get asynchronous results.</para>
<example id="search-async-example"><title>Retrieving Search Results Individually</title>
<para>To get the results individually as the client receives the results from
the server, pass <literal>LDAP_MSG_ONE</literal> as the <literal>all</literal> argument.
</para>
<programlisting>#include &lt;stdio.h>
#include "ldap.h"
...
#define BASEDN "dc=example,dc=com"
#define SCOPE LDAP_SCOPE_SUBTREE
#define FILTER "(sn=Jensen)"
...
LDAP *ld;
LDAPMessage *res;
int msgid, rc, parse_rc, finished = 0;
struct timeval zerotime;
zerotime.tv_sec = zerotime.tv_usec = 0L;
...
/* Send the LDAP search request. */
rc = ldap_search_ext( ld, BASEDN, SCOPE, FILTER, NULL, 0, NULL, NULL,
NULL, LDAP_NO_LIMIT, &amp;msgid );
...
/* Poll the server for the results of the search operation. */
while ( !finished ) {
rc = ldap_result( ld, msgid, LDAP_MSG_ONE, &amp;zerotime, &amp;res );
switch ( rc ) {
case -1:
/* An error occurred. */
...
case 0:
/* The timeout period specified by zerotime was exceeded. */
...
case LDAP_RES_SEARCH_ENTRY:
/* The server sent one of the entries found by the search. */
...
case LDAP_RES_SEARCH_REFERENCE:
/* The server sent a search reference .*/
...
case LDAP_RES_SEARCH_RESULT:
/* Parse the final result received from the server. */
...
}
...
}
...</programlisting>
<itemizedlist>
<listitem><para>To get the results all at once, in other words, to block processes
until all results are received, pass <literal>LDAP_MSG_ALL</literal> as the <literal>
all</literal> argument.</para></listitem>
<listitem><para>To get the results received thus far, pass <literal>LDAP_MSG_RECEIVED
</literal> as the <literal>all</literal> argument.</para></listitem>
</itemizedlist>
</example>
<para>If you specify either <literal>LDAP_MSG_ALL</literal> or <literal>LDAP_MSG_RECEIVED
</literal>, the function passes back a chain of search results as the <literal>result
</literal> argument. If you specify <literal>LDAP_MSG_ONE</literal>, the function
passes back a single search result as the <literal>result</literal> argument.
The function normally returns the type of the first search result. When the
function returns the type, as only one result is returned, the function returns
the type of that result.</para>
<para>The following example prints the values of all attributes in the entries
returned by an asynchronous search.</para>
<example id="search-async-display-example"><title>Printing Results of an Asynchronous
Search</title>
<programlisting>#include &lt;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. Use prldap_init() for IPv6. */
if ( (ld = ldap_init( HOSTNAME, PORTNUMBER )) == NULL ) {
perror( "ldap_init" );
return( 1 );
}
version = LDAP_VERSION3;
if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &amp;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, &amp;matched_msg, &amp;error_msg );
if ( error_msg != NULL &amp;&amp; *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. */
if ( matched_msg != NULL &amp;&amp; *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, &amp;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, &amp;zerotime, &amp;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, &amp;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, &amp;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, &amp;rc, &amp;matched_msg,
&amp;error_msg, NULL, &amp;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, &amp;matched_msg, &amp;error_msg );
if ( error_msg != NULL &amp; *error_msg != '\0' ) {
fprintf( stderr, "%s\n", error_msg );
}
if ( matched_msg != NULL &amp;&amp; *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++;
}</programlisting>
</example>
</sect2>
<sect2 id="bdaep"><title>Determining Search Result Types</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>result types</tertiary>
</indexterm>
<para>To determine what type of result was returned, call the <function>ldap_msgtype
</function> function.</para>
<itemizedlist>
<para>A search result can be one of the following types:</para>
<listitem><para><literal>LDAP_RES_SEARCH_ENTRY</literal> indicates that the
result is an entry that is found in the search. You can pass the <structname>LDAPMessage
</structname> structure that represents the entry to <function>ldap_get_dn</function> to
get the DN of the entry. You can pass the structure to <function>ldap_first_attribute
</function> and <function>ldap_next_attribute</function> to get the attributes
of the entry. For details, see <olink targetptr="bdaeq">Getting Distinguished
Names for Each Entry</olink> and <olink targetptr="bdaet">Getting Attribute
Types From an Entry</olink>.</para></listitem>
<listitem><para><literal>LDAP_RES_SEARCH_REFERENCE</literal> indicates that
the result is a search reference that is found within the scope of the search.
You can pass the <structname>LDAPMessage</structname> structure representing
the search reference to the <function>ldap_parse_reference</function> function
to get the referrals, LDAP URLs, to other servers. For details, see <olink targetptr="bdaev">Getting Referrals From Search References</olink>.</para><para>To
receive search references from an LDAP v3 server, you must identify your client
as LDAP v3 enabled. If not, the server returns the error code <errorcode>LDAP_PARTIAL_RESULTS
</errorcode> and a set of referrals. See <olink targetptr="bdaci">Specifying
the LDAP Version of Your Client</olink> for details.</para></listitem>
<listitem><para><literal>LDAP_RES_SEARCH_RESULT</literal> indicates that the
result is the final data sent by the server to indicate the end of the LDAP
search operation. You can pass the <structname>LDAPMessage</structname> structure
that represents the result to the <function>ldap_parse_result</function> function
to get the LDAP result code for the search operation. For a list of possible
result codes for an LDAP search operation, see the <function>ldap_search_ext_s
3ldap</function> man page. For details on parsing the result, see <olink targetptr="bdadl">Getting Information From an LDAPMessage Structure</olink>.</para>
</listitem>
</itemizedlist>
<example id="search-chain-results-example"><title>Retrieving a Chain of Results</title>
<para>This example retrieves each result in a chain. The example then determines
its type.</para>
<programlisting>#include &lt;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, &amp;res );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
if ( error_msg != NULL &amp;&amp; *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. */
if ( matched_msg != NULL &amp;&amp; *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. */
...
}
...
}
...</programlisting>
</example>
</sect2>
<sect2 id="bdaeq"><title>Getting Distinguished Names for Each Entry</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>retrieving DNs</tertiary>
</indexterm>
<para>Because the DN of an entry differentiates the entry from other entries,
you might want to access the DN in search results. You might also want to
parse the name into its individual components. The SDK provides functions
for both of these tasks.</para>
<sect3 id="bdaer"><title>Getting the Distinguished Name of an Entry</title>
<para>To get the DN of an entry, call the <function>ldap_get_dn</function> function.
When finished with the DN, free the DN from memory by calling the <function>ldap_memfree
</function> function. </para>
<example id="search-get-dn-example"><title>Obtaining the DN for Search Result
Entries</title>
<programlisting>#include &lt;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, &amp;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 );</programlisting>
</example>
</sect3>
<sect3 id="bdaes"><title>Getting the Components of a Distinguished Name</title>
<para>If you want to access individual components of a DN or relative DN,
call the <function>ldap_explode_dn</function> or <function>ldap_explode_rdn</function> function,
respectively. Both functions return a <literal>NULL</literal> terminated array
that contains the components of the DN. When you are done working with this
array, free the array by calling the <function>ldap_value_free</function> function.
</para>
<para>You can also specify whether or not you want the attribute names included
in the array, by using the <literal>notypes</literal> parameter. </para>
<itemizedlist>
<listitem><para>Set <literal>notypes</literal> to <literal>0</literal> if
you want to include attribute names, as in this function call:</para></listitem>
</itemizedlist>
<programlisting>ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 0 )</programlisting>
<para>This function then returns this array:</para>
<programlisting>{ "uid=bjensen", "ou=People", "dc=example,dc=com", NULL }</programlisting>
<itemizedlist>
<listitem><para>Set <literal>notypes</literal> to <literal>1</literal> if
you do not want to include the attribute names in the array, as in this function
call:</para></listitem>
</itemizedlist>
<programlisting>ldap_explode_dn( "uid=bjensen,ou=People,dc=example,dc=com", 1 )</programlisting>
<para>This function then returns this array:</para>
<programlisting>{ "bjensen", "People", "example.com", NULL }</programlisting>
</sect3>
</sect2>
<sect2 id="bdaet"><title>Getting Attribute Types From an Entry</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>retrieving attribute types</tertiary>
</indexterm>
<para>To retrieve the type, also called the name, of the first attribute in
an entry, call the <function>ldap_first_attribute</function> function. To
get the type of the next attribute, call the <function>ldap_next_attribute</function> function.
</para>
<note><para>Operational attributes such as <literal>creatorsName</literal> and <literal>
modifyTimestamp</literal> are not normally returned in search results. You
must explicitly specify operational attibutes by type in the search request.
For more details, see <olink targetptr="bdaek">Operational Attributes</olink>.</para>
</note>
<para>When you are finished iterating through the attributes, you need to
free the <structname>BerElement</structname> structure allocated by the <function>
ldap_first_attribute</function> function, if the structure is not <literal>NULL</literal>.
To free this structure, call the <function>ldap_ber_free</function> function.
You should also free the attribute type returned by the <function>ldap_first_attribute
</function> function. To free the attribute type, call the <function>ldap_memfree
</function> function. The following example shows how to do this.</para>
<example id="search-get-attrs-example"><title>Retrieving Entry Attribute Types</title>
<programlisting>#include &lt;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, &amp;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, &amp;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 );
}
...</programlisting>
</example>
</sect2>
<sect2 id="bdaeu"><title>Getting the Values of an Attribute</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>retrieving attribute values</tertiary>
</indexterm>
<para>The values of an attribute are represented by a <literal>NULL</literal> terminated
array. If the attribute contains string data, such as a name or phone number,
the values are a list of strings. If the attribute contains binary data, the
values are a list of <structname>berval</structname> structures, such as JPEG
files or audio files. Use the following guidelines to retrieve the values
of an attribute:</para>
<itemizedlist>
<listitem><para>To get the values of an attribute that contains string data,
call the <function>ldap_get_values</function> function. The <function>ldap_get_values
</function> function returns a <literal>NULL</literal> terminated array of
strings that represent the value of the attribute.</para></listitem>
<listitem><para>To get the values of an attribute that contains binary data,
call the <function>ldap_get_values_len</function> function. The <function>ldap_get_values_len
</function> function returns a <literal>NULL</literal> terminated array of <structname>
berval</structname> structures that represent the value of the attribute.</para>
</listitem>
<listitem><para>To get the number of values in an attribute, call either the <function>
ldap_count_values</function> or <function>ldap_count_values_len</function> function.
Both functions return the number of values in the attribute.</para></listitem>
</itemizedlist>
<para>When you have finished working with the values of the attribute, you
need to free the values from memory. To free the values, call <function>ldap_free_value
</function> or <function>ldap_free_value_len</function>. The following example
gets, then prints the values of an attribute in an entry. The function assumes
that all attributes have string values.</para>
<example id="search-get-attr-values-example"><title>Retrieving Attribute Values</title>
<programlisting>#include &lt;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, &amp;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, &amp;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 );
}
...</programlisting>
</example>
<para>The following example gets the first value of the <literal>jpegPhoto</literal> attribute
and saves the JPEG data to a file.</para>
<example id="search-get-attr-value-example"><title>Getting and Saving an Attribute
Value</title>
<programlisting>#include &lt;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, &amp;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, &amp;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 );
}
...</programlisting>
</example>
</sect2>
<sect2 id="bdaev"><title>Getting Referrals From Search References</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>retrieving referrals</tertiary>
</indexterm><indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>retrieving search references</tertiary>
</indexterm>
<para>A search reference returned from the server contains one or more <firstterm>
referrals</firstterm>, which are LDAP URLs that identify other LDAP servers.
To retrieve these referrals, you need to call the <function>ldap_parse_reference</function> function.
The following example gets and prints the referrals from a search reference.</para>
<example id="search-get-referral-example"><title>Obtaining a Referral</title>
<programlisting>#include &lt;stdio.h>
#include "ldap.h"
...
LDAP *ld;
LDAPMessage *msg;
char **referrals;
int i, rc, parse_rc;
...
parse_rc = ldap_parse_reference( ld, msg, &amp;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 );
}
...</programlisting>
</example>
</sect2>
</sect1>
<sect1 id="bdaew"><title>Sorting the Search Results With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>client-side sorting</tertiary>
</indexterm><indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>server-side sorting</tertiary>
</indexterm>
<para>&DirectorySDKForC; offers 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.</para>
<sect2 id="bdaex"><title>Server-Side Sorting</title>
<para>To sort results on the server, you need to send a server-side sorting
control with the search request. For details, see <olink targetptr="bdaip">Using
the Server-Side Sorting Control With Directory SDK for C</olink> for details.</para>
</sect2>
<sect2 id="bdaey"><title>Client-Side Sorting</title>
<para>First, you need to retrieve the attributes that you plan to use for
sorting. For example, you might plan to sort the results by email address.
Make sure that the <literal>mail</literal> attribute is one of the attributes
returned in the search.</para>
<sect3 id="bdaez"><title>Sorting Entries by an Attribute</title>
<para>To sort the search results by a particular attribute, call the <function>ldap_sort_entries
</function> function. If you do no&rsquo;t specify an attribute for sorting,
that is, if you pass <literal>NULL</literal> for the <literal>attr</literal> parameter,
the entries are sorted by DN.</para>
<example id="search-client-sort-by-attr-value-example"><title>Sorting Entries
by an Attribute</title>
<para>This example sorts entries by the <literal>roomNumber</literal> attribute.</para>
<programlisting>#include &lt;stdio.h>
#include &lt;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, &amp;result ) !=
LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_search_s" );
return( 1 );
}
/* Sort the results by room number, using strcasecmp. */
if ( ldap_sort_entries(ld, &amp;result, sortby, strcasecmp) !=
LDAP_SUCCESS ){
ldap_perror( ld, "ldap_sort_entries" );
return( 1 );
}
...</programlisting>
</example>
</sect3>
<sect3 id="bdafa"><title>Sorting Entries by Multiple Attributes</title>
<para>To sort the search results by multiple attributes, call the <function>ldap_multisort_entries
</function> function. If you do not specify a set of attributes for sorting,
the entries are sorted by DN. To sort entries by DN, pass <literal>NULL</literal> for
the <literal>attr</literal> parameter.</para>
<example id="search-client-sort-by-multiple-values-example"><title>Sorting
Entries by Multiple Attributes</title>
<para>This example sorts entries first by the <literal>roomNumber</literal> attribute,
then by the <literal>telephoneNumber</literal> attribute.</para>
<programlisting>#include &lt;stdio.h>
#include &lt;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, &amp;res ) !=
LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_search_s" );
return( 1 );
}
/* Sort the results, using strcasecmp. */
if ( ldap_multisort_entries(ld,&amp;res,attrs, strcasecmp) !=
LDAP_SUCCESS ){
ldap_perror( ld, "ldap_sort_entries" );
return( 1 );
}</programlisting>
</example>
</sect3>
<sect3 id="bdafb"><title>Sorting the Values of an Attribute</title>
<para>You can also sort the values of a particular attribute. To sort the
values, call the <function>ldap_sort_strcasecmp</function> function. In this
function, the comparison function must pass parameters of the type <literal>char
**</literal>. You should use the <function>ldap_sort_strcasecmp</function> function,
rather than a function like <function>strcasecmp</function>, which passes
parameters of the type <literal>char *</literal>. The following example sorts
the values of attributes before printing the values.</para>
<example id="search-client-sort-attr-values-example"><title>Sorting Attribute
Values</title>
<programlisting>#include &lt;stdio.h>
#include &lt;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 );
}
...</programlisting>
</example>
</sect3>
</sect2>
</sect1>
<sect1 id="bdafc"><title>Freeing the Search Results With &DirectorySDKForC;</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>freeing results</tertiary>
</indexterm>
<para>The results of the search are returned in an <structname>LDAPMessage</structname> structure.
After you are done working with the search results, you should free this structure
from memory. To free the search results, call the <function>ldap_msgfree</function> function,
which returns the type of the last message freed from memory.</para></sect1>
<sect1 id="bdafd"><title>Examples of Search Operations With &DirectorySDKForC;
</title>
<indexterm>
<primary>C SDK</primary>
<secondary>search LDAP directory</secondary>
<tertiary>examples</tertiary>
</indexterm>
<para>This section contains sample code for various search operations.</para>
<sect2 id="bdafe"><title>Reading an Entry With a Search</title>
<para>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. Also,
set the scope of the search to <literal>LDAP_SCOPE_BASE</literal>, specifying <literal>
(objectclass=*)</literal> as the search filter, as shown in the following
example.</para>
<example id="search-read-entry-example"><title>Reading a Specific Entry With
a Search</title>
<programlisting>#include &lt;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. Use prldap_init() for IPv6. */
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, &amp;result ) ) !=
LDAP_SUCCESS ) {
fprintf( stderr, "ldap_search_ext_s: %s\n", ldap_err2string( rc ) );
return( 1 );
}
/* Since this is 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, &amp;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 );
}</programlisting>
</example>
</sect2>
<sect2 id="bdaff"><title>Listing Subentries With a Search</title>
<para>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. Also, set the scope of the search to <literal>LDAP_SCOPE_ONELEVEL
</literal>. The following lists all entries one level under the <literal>dc=example,dc=com
</literal> entry in the directory hierarchy.</para>
<example id="search-list-subentries-example"><title>Listing Subentries</title>
<programlisting>#include &lt;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, &amp;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, &amp;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 );
...</programlisting>
</example>
</sect2>
<sect2 id="bdafg"><title>Sending Search Request Using &DirectorySDKForC;</title>
<para>The following sample code shows how to search for all entries with the
last name (surname) <literal>Jensen</literal> in the <literal>example.com</literal> organization.
</para>
<example id="search-send-request-example"><title>Sending a Search Request</title>
<programlisting>#include &lt;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, &amp;msgid );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_search_ext: %s\n", ldap_err2string( rc ) );
ldap_unbind( ld );
return( 1 );
}
...</programlisting>
</example>
</sect2>
</sect1>
</chapter>