Sun ONE logo     Previous      Contents      Index      Next     
Sun ONE Directory Server 5.2 Plug-In API Programming Guide



Chapter 4   Working with Entries

This chapter covers plug-in API features for handling directory entries, attributes, attribute values, and Distinguished Names (DNs). It also deals with converting entries to and from LDAP Data Interchange Format (LDIF) strings, and with checking whether entries comply with LDAP schema.

Code excerpts used in this chapter can be found in the ServerRoot/plugins/slapd/slapi/examples/entries.c sample plug-in and the ServerRoot/plugins/slapd/slapi/examples/dns.c sample plug-in.

Refer to the Sun ONE Directory Server Plug-In API Reference for complete reference information concerning the plug-in API functions used in this chapter.

Creating Entries

This section demonstrates how to create a new entry in the directory. Create a new entry either by allocating space and creating a wholly new entry, or by duplicating an existing entry and modifying its values.

New Entries

Create a wholly new entry using slapi_entry_alloc() to allocate the memory required, as shown in Code Example 4-1.



Code Example 4-1    Creating a New Entry (entries.c) 

#include "slapi-plugin.h"

int
test_ldif()
{
Slapi_Entry * entry = NULL; /* Entry to hold LDIF */

/* Allocate the Slapi_Entry structure. */
entry = slapi_entry_alloc();

/* Add code that fills the Slapi_Entry structure. */

/* Add code that uses the Slapi_Entry structure. */

/* Release memory allocated for the entry. */
slapi_entry_free(entry);

return (0);
}


Copies of Entries

Create a copy of an entry with slapi_entry_dup() as shown in Code Example 4-2.



Code Example 4-2    Copying an Existing Entry (entries.c) 

#include "slapi-plugin.h"

int
test_create_entry()
{
Slapi_Entry * entry = NULL; /* Original entry */
Slapi_Entry * ecopy = NULL; /* Copy entry */

entry = slapi_entry_alloc();

/* Add code that fills the new entry before making a copy. */

ecopy = slapi_entry_dup(entry);

/* Add code that uses the Slapi_Entry structure. */

/* Release memory allocated for the entries. */
slapi_entry_free(entry);
slapi_entry_free(ecopy);

return (0);
}


Converting To and From LDIF Representations

This section shows you how to use the plug-in API to convert from entries represented in LDIF to Slapi_Entry structures and from Slapi_Entry structures to LDIF strings.

LDIF files appear as a series of human-readable representations of directory entries, optionally with access control instructions as well. Entries represented in LDIF start with a line for the distinguished name, and continue with optional lines for the attributes as shown in Code Example 4-3.



Code Example 4-3    LDIF Syntax Representing an Entry 

dn:[:] dn-value\n
[attribute:[:] value\n]
[attribute:[:] value\n]
[single-space continued-value\n]*

A double colon, ::, indicates that the value is base-64 encoded. This makes it possible, for example, to have a line break in the midst of an attribute value.

As shown in Code Example 4-3, LDIF lines can be folded by leaving a single space at the beginning of the continued line.

Sample LDIF files can be found under ServerRoot/slapd-serverID/ldif/. Code Example 4-4 shows two entries represented in LDIF.



Code Example 4-4    LDIF Representation of Two Entries (Example-Plugin.ldif) 

dn: ou=People,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: People
aci: (target = "ldap:///ou=People,dc=example,dc=com")
(targetattr = "userpassword")(version 3.0; acl
"Allow people to change their password";
allow(write)(userdn = "ldap:///self");)

dn: uid=yyorgens,ou=People,dc=example,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: yyorgens
givenName: Yolanda
sn: Yorgenson
cn: Yolanda Yorgenson
mail: yyorgens@example.com
userPassword: yyorgens
secretary: uid=bcubbins,ou=People,dc=example,dc=com


Refer to the Sun ONE Directory Server Reference Manual for details on LDIF syntax.

Converting an LDIF String to a Slapi_Entry Structure

This can be done using slapi_str2entry(). The function takes as its two arguments the string to convert and an int holding flags of the form SLAPI_STR2ENTRY_* in slapi-plugin.h. It returns a pointer to a Slapi_Entry if successful, NULL otherwise, as in Code Example 4-5.



Code Example 4-5    Converting To and From LDIF Strings (entries.c) 

#include "slapi-plugin.h"

#define LDIF_STR "dn: dc=example,dc=com\nobjectclass: \
top\nobjectclass: domain\ndc: example\n"

int
test_ldif()
{
char * ldif = NULL; /* Example LDIF string */
Slapi_Entry * entry = NULL; /* Entry to hold LDIF */
char * str = NULL; /* String to hold entry */
int len; /* Length of entry as LDIF */

/* LDIF to Slapi_Entry */
entry = slapi_entry_alloc();
ldif = slapi_ch_strdup(LDIF_STR);
entry = slapi_str2entry(ldif, SLAPI_STR2ENTRY_ADDRDNVALS);
slapi_ch_free_string(&ldif);
if (entry == NULL) return (-1);

/* Slapi_Entry to LDIF */
str = slapi_entry2str(entry, &len);
if (str == NULL) return (-1);
slapi_log_info_ex(
SLAPI_LOG_INFO_AREA_PLUGIN,
SLAPI_LOG_INFO_LEVEL_DEFAULT,
SLAPI_LOG_NO_MSGID,
SLAPI_LOG_NO_CONNID,
SLAPI_LOG_NO_OPID,
"test_ldif in test-entries plug-in",
"\nOriginal entry:\n%sEntry length: %d\n", str, len
);

slapi_entry_free(entry);

return (0);
}


Here SLAPI_STR2ENTRY_ADDRDNVALS adds any missing Relative Distinguished Name (RDN) values, as specified in slapi-plugin.h where supported flags for slapi_str2entry() are listed.

Converting a Slapi_Entry Structure to an LDIF String

This can be done using slapi_entry2str(). This function takes as its two arguments the entry to convert and an int to hold the length of the string returned. It returns a char * to the LDIF if successful, NULL otherwise, as in Code Example 4-5.

Getting Entry Attributes and Attribute Values

This section demonstrates how to iterate through real attributes of an entry. Real attributes contrast with virtual attributes generated by Directory Server for advanced entry management using roles and Class of Service (CoS). You may use this technique when looking through the list of attributes belonging to an entry. If you know the entry contains a particular attribute, slapi_entry_attr_find() returns a pointer to the attribute directly.

Iteration through attribute values can be done using slapi_attr_first_value() and slapi_attr_next_value() together as shown in Code Example 4-6.



Code Example 4-6    Iterating Through Attributes of an Entry (testpreop.c) 

#include "slapi-plugin.h"

int
testpreop_add(Slapi_PBlock * pb)
{
Slapi_Entry * entry; /* Entry to add */
Slapi_Attr * attribute; /* Entry attributes */
Slapi_Value * value; /* Attribute values */
int i, rc = 0;
char * tmp; /* Holder for new desc. */
const char * string; /* Holder for current desc.*/

rc |= slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);

rc |= slapi_entry_attr_find(entry, "description", &attribute);
if (rc == 0) { /* Found a description, so... */
for ( /* ...loop for value... */
i = slapi_attr_first_value(attribute, &value);
i != -1;
i = slapi_attr_next_value(attribute, i, &value)
) { /* ...prepend "ADD ". */
string = slapi_value_get_string(value);
tmp = slapi_ch_malloc(5+strlen(string));
strcpy(tmp, "ADD ");
strcat(tmp+4, string);
slapi_value_set_string(value, tmp);
slapi_ch_free((void **)&tmp);
}
}
return 0;
}


When working with virtual attributes such as those used for CoS, you may use slapi_vattr_list_attrs() to obtain the complete list of both real and virtual attributes belonging to an entry.

Adding and Removing Attribute Values

This section demonstrates how to add and remove attributes and their values.

Adding Attribute Values

If the attribute name and value are strings, you may be able to use slapi_entry_add_string(), as in Code Example 4-7 that builds an entry by first setting the DN, then adding attributes with string values.



Code Example 4-7    Adding String Attribute Values (entries.c) 

#include "slapi-plugin.h"

int
test_create_entry()
{
Slapi_Entry * entry = NULL; /* Original entry */

entry = slapi_entry_alloc();
slapi_entry_set_dn(entry, slapi_ch_strdup("dc=example,dc=com"));
slapi_entry_add_string(entry, "objectclass", "top");
slapi_entry_add_string(entry, "objectclass", "domain");
slapi_entry_add_string(entry, "dc", "example");

/* Add code using the entry you created..... */

slapi_entry_free(entry);

return (0);
}


When working with values that already exist or are not strings, you may use slapi_entry_add_values_sv() to add new values to an attribute of an entry, or slapi_entry_attr_merge_sv() to merge new values with the existing values of an attribute. The function slapi_entry_add_values_sv() returns an error when duplicates of the new values already exist; slapi_entry_attr_merge_sv() does not. Code Example 4-8 demonstrates slapi_entry_attr_merge_sv().



Code Example 4-8    Merging Attribute Values (entries.c) 

#include "slapi-plugin.h"

int
test_ldif()
{
Slapi_Entry * entry = NULL; /* Entry to hold LDIF */
Slapi_Value * values[2]; /* Attribute values */

entry = slapi_entry_alloc();

/* Add code to transform an LDIF representation into an entry.*/

/* Add a description by setting the value of the attribute.
* Although this is overkill when manipulating string values,
* it can be handy when manipulating binary values. */
values[0] = slapi_value_new_string("Description for the entry.");
values[1] = NULL;
if (slapi_entry_attr_merge_sv(entry,"description",values) != 0)
/* Merge did not work if we arrive here. */ ;

slapi_entry_free(entry);

return (0);
}


If the attribute exists in the entry, slapi_entry_attr_merge_sv() merges the specified values into the set of existing values. If the attribute does not exist, slapi_entry_attr_merge_sv() adds the attribute with the specified values.

Removing Attribute Values

Remove attribute values using slapi_entry_delete_string() or slapi_entry_delete_values_sv().

Verifying Schema Compliance for an Entry

This section demonstrates how to check that an entry is valid with respect to the directory schema known to Directory Server. Verify schema compliance for an entry using slapi_entry_check(). Its two arguments are a pointer to a parameter block and a pointer to the entry, as shown in Code Example 4-9.



Code Example 4-9    Checking Schema Compliance (entries.c) 

#include "slapi-plugin.h"

int
test_create_entry()
{
Slapi_Entry * entry = NULL; /* Original entry */
Slapi_Entry * ecopy = NULL; /* Copy entry */

entry = slapi_entry_alloc();

/* Add code to fill the entry, setting the DN and attributes. */

ecopy = slapi_entry_dup(entry);
slapi_entry_set_dn(ecopy, slapi_ch_strdup("dc=example,dc=org"));
slapi_entry_add_string(ecopy, "description", "A copy of the orig.");

/* Does the resulting copy comply with our schema? */
if (slapi_entry_schema_check(NULL, ecopy) == 0) {
/* Resulting entry does comply. */ ;
} else {
/* Resulting entry does not comply. */ ;
}

slapi_entry_free(entry);
slapi_entry_free(ecopy);

return (0);
}


Notice the parameter block pointer argument is NULL. Leave the parameter block pointer argument NULL unless the plug-in is used in a replicated environment and you do not want schema compliance verification carried out for replicated operations.

Handling Entry Distinguished Names (DNs)

This section demonstrates how to work with Distinguished Names to:

  • Determine the parent DN of a given DN
  • Determine the suffix DN with which the entry DN is associated
  • Identify whether a root suffix is served by the backend
  • Set the DN of the entry
  • Normalize the DN of the entry for comparison with other entries
  • Determine whether the entry DN is that of the directory superuser

Getting the Parent and Suffix DNs

This can be done using slapi_dn_parent(). This function returns the parent, whereas slapi_dn_beparent() returns the suffix associated with the entry, as shown in Code Example 4-10.



Code Example 4-10    Determining the Parent and Suffix of an Entry (dns.c) 

#include "slapi-plugin.h"

int
test_dns(Slapi_PBlock * pb)
{
char * bind_DN; /* DN being used to bind */
char * parent_DN; /* DN of parent entry */
char * suffix_DN; /* DN of suffix entry */
int connId, opId, rc = 0;
long msgId;

rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &bind_DN);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId);
rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId);
if (rc != 0) return (-1);

/* Get the parent DN of the DN being used to bind. */
parent_DN = slapi_dn_parent(bind_DN);
slapi_log_info_ex(
SLAPI_LOG_INFO_AREA_PLUGIN,
SLAPI_LOG_INFO_LEVEL_DEFAULT,
msgId,
connId,
opId,
"test_dns in test-dns plug-in",
"Parent DN of %s: %s\n", bind_DN, parent_DN
);

/* Get the suffix DN of the DN being used to bind. */
suffix_DN = slapi_dn_beparent(pb, bind_DN);
if (suffix_DN != NULL) {
slapi_log_info_ex(
SLAPI_LOG_INFO_AREA_PLUGIN,
SLAPI_LOG_INFO_LEVEL_DEFAULT,
msgId,
connId,
opId,
"test_dns in test-dns plug-in",
"Suffix for user (%s) is (%s).\n", bind_DN, suffix_DN
);
}

return rc;
}


Notice the suffix and parent DNs may be the same if the tree is shallow. For example, if the bind_DN here is uid=yyorgens,ou=People,dc=example,dc=com, then the parent is ou=People,dc=example,dc=com, and so is the suffix.

Checking Whether a Suffix is Served Locally

This can be done using slapi_dn_isbesuffix(), which takes as its two arguments a parameter block and a char * to the root suffix DN, as demonstrated in Code Example 4-11.



Code Example 4-11    Checking Whether a Suffix is Local (dns.c) 

#include "slapi-plugin.h"

int
test_dns(Slapi_PBlock * pb)
{
char * bind_DN; /* DN being used to bind */
char * parent_DN; /* DN of parent entry */
char * suffix_DN; /* DN of suffix entry */

slapi_pblock_get(pb, SLAPI_BIND_TARGET, &bind_DN);

/* Get the suffix DN of the DN being used to bind. */
suffix_DN = slapi_dn_beparent(pb, bind_DN);

/* Climb the tree to the top suffix and check if it is local. */
while (suffix_DN != NULL && !slapi_dn_isbesuffix(pb, suffix_DN)) {
suffix_DN = slapi_dn_parent(suffix_DN);
}
if (suffix_DN != NULL) {
/* Suffix is served locally. */ ;
} else {
/* Suffix is not served locally. */ ;
}

return 0;
}


Notice we use slapi_dn_isbesuffix() to climb the tree. Notice also that slapi_dn_parent() keeps gnawing away at the DN until it is NULL. The parent does not necessarily correspond to an actual entry.

Getting and Setting Entry DNs

This can be done using slapi_entry_get_dn() and slapi_entry_set_dn(). Code Example 4-9 demonstrates changing the DN of a copy of an entry, using slapi_entry_set_dn(). Notice the use of slapi_ch_strdup() to ensure the old DN in the copy is not used.

Normalizing a DN

Before comparing DNs, you may want to normalize the DNs to prevent the comparison from failing due to extra white space or different capitalization. You may normalize a DN using slapi_dn_normalize(), or slapi_dn_normalize_case() as shown in Code Example 4-12.



Code Example 4-12    Normalizing a DN (dns.c) 

#include "slapi-plugin.h"

int
test_norm()
{
char * test_DN; /* Original, not normalized */
char * copy_DN; /* Copy that we normalize */

test_DN = "dc=Example, dc=COM";/* Prior to normalization...*/

/* When normalizing the DN with slapi_dn_normalize() and
* slapi_dn_normalize_case(), the DN is changed in place.
* Use slapi_ch_strdup() to work on a copy. */
copy_DN = slapi_ch_strdup(test_DN);
copy_DN = slapi_dn_normalize_case(copy_DN);

return 0;
}


The function slapi_dn_normalize_case() works directly on the char * DN passed as an argument to perform "case ignore matching" as specified, but not defined, for X.500. Use slapi_ch_strdup() to make a copy first if you do not want to modify the original.

Is This the Directory Manager?

Answer this question using slapi_dn_isroot(). The bind for the directory superuser, however, as for anonymous users, is handled as a special case. For such users, the server front end handles the bind before calling pre-operation bind plug-ins.


Previous      Contents      Index      Next     
Copyright 2003 Sun Microsystems, Inc. All rights reserved.