1739 lines
70 KiB
Plaintext
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–1</olink> and <olink targetptr="ldap-search-ext-prototype">Example 9–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 “find entries with the last name of Jensen”
|
|
or as complex as “find entries that belong to Dept. #17 and whose first
|
|
names start with the letter F.” 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 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><=</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 <= 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>&</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<=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 <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 *)&max_ret );
|
|
|
|
|
|
/* Set the maximum number of seconds to wait. */
|
|
max_tim = 30;
|
|
ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *)&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 <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, &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 );
|
|
}</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 <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, &msgid );
|
|
...
|
|
/* Poll the server for the results of the search operation. */
|
|
while ( !finished ) {
|
|
rc = ldap_result( ld, msgid, LDAP_MSG_ONE, &zerotime, &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 <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, &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. */
|
|
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++;
|
|
}</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 <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. */
|
|
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. */
|
|
...
|
|
}
|
|
...
|
|
}
|
|
...</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 <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 );</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 <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 );
|
|
}
|
|
...</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 <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 );
|
|
}
|
|
...</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 <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 );
|
|
}
|
|
...</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 <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 );
|
|
}
|
|
...</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’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 <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 );
|
|
}
|
|
...</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 <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 );
|
|
}</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 <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 );
|
|
}
|
|
...</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 <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, &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, &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 <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 );
|
|
...</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 <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 );
|
|
}
|
|
...</programlisting>
|
|
</example>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter>
|