Novell is now a part of Micro Focus

Extending the NDS Schema with DSAPIs

Articles and Tips: article

KEVIN BURNETT
Software Engineer
Strategic Partner Engineering Group

01 Jul 1999


Shows how to extend the NDS schema using Novell's DSAPIs. The number of DSAPIs has been expanded from about 50 with the first release of NDS to over 250 with the current X-plat or cross platform libraries. Contains source code that illustrates how to define attributes and classes, create a new object, populate a new attribute, and remove objects, class definitions and attributes.

Introduction

Extending the Novell Directory Services (NDS) schema using Novell's DSAPIs is easier than one may first imagine. Novell provides the full SDK, rich with many APIs to make the job straightforward.

When you buy NetWare 5, you get a base schema which Novell provides for basic administrative tasks such as creating user, printer and server objects in the directory tree. What do you do if you, as a developer, want to store special information in the directory to interact with your specific application? Do you have a need to add specialized objects to manage your technology? Do you have a need to store valuable information in a secure place? Do you have a need to customize NDS to suit your applications needs? Extending the NDS schema is your solution.

NDS Schema Overview

The NDS schema is built on three information types:

  • Object classes, which describe the types of objects that can exist in NDS.

  • Attribute types, which provide information about an object.

  • Attribute syntaxes, which define the type of data stored in an attribute.

The NDS schema determines what kind of objects can exist in the NDS database. Each object is based on an object class that defines the object's attributes. All attributes are based on the set of defined attribute types, and all attributes are based on the set of attribute syntaxes.

NDS has a set of built-in classes and attribute types that accommodate general categories of network objects such as organizations, users, and devices. This set is called the base schema. As a developer, you can build on the base schema to create new classes for specific kinds of objects.

The DSAPIs (directory services applications programming interfaces) in the Novell Developer Kit (www.developer.novell.com) provide your interface to the NDS schema. These APIs allow you to do the following operations and more:

  • Read, create and delete class definitions

  • Read, create and delete attribute definitions

  • Read a syntax definition

  • Read a syntax ID

  • List the classes an object can contain

  • Modify a class definition

Object Classes

Object classes are templates for creating objects in the NDS database. We will get into more detail about how to create class definitions in the source code section later in this article.

Attribute Types

Classes are formed from standard attribute types defined by the NDS schema. You can read existing attribute types and create new ones. You can also remove attribute types but only nonstandard ones and only if the attribute type isn't assigned to a class. You can't remove any of the standard attribute types.

Once an attribute type has been created, it can't be modified. Attribute type information includes the following:

  • Attribute Syntax ID

  • Attribute Flags

  • ASN.1 ID

When you create an attribute programmatically, you will need to fill in a structure containing these three elements. (See the source code example, following, or the Novell NDK for more information.)

Attribute Syntaxes

The NDS schema includes a standard group of attribute syntaxes that may be used to construct attribute types. The set of available syntax IDs is fixed and can't be modified. An attribute syntax consists of one or more data types with associated matching rules. Matching rules indicate the characteristics that are significant then comparing two values of the same syntax. The syntax definition includes the following information:

  • Syntax ID

  • Syntax name

  • Syntax flags

The syntax flags indicate the matching rules for the syntax such as equality, greater than, less than, etc. Case Ignore String is an example of an attribute syntax. Two substrings of this syntax can be matched for equality. When compared, only the characters themselves are tested and the case is ignored.

NDS DSAPIs

When NDS was first released to the public, a set of corresponding APIs was released to provide a programming interface to NDS. In the first release of this SDK, two libraries were provided. One was client specific and the other server specific. This release contained about 50 DSAPIs.

About three years ago, Novell introduced what is called the Cross-platform or X-plat libraries. These are libraries designed to interact with NetWare, of which a portion are DSAPI. This newer set of libraries provides a common code-base for both client and server. It also expands the number of DSAPIs to over 250. This X-plat library is what Novell recommends that developers use. Developers can download the X-Plat libraries from www.developer.novell.com.

Additionally, sample code for each of these DSAPIs is available from the same Web site.

Novell Schema Registry

When you choose to extend the NDS schema, Novell provides a service to guarantee that your schema extensions will not conflict with anyone else's or Novell's. A prefix name, that you choose, is registered with Novell. Novell recommends that a schema extension take the following form:

NOVELL:MYATTRIBUTE or NOVELL:MYCLASS

In this example, NOVELL is the prefix and MYATTRIBUTE and MYCLASS are the unique identifier for a specific attribute definition or class definition. Novell recommends that you separate the prefix from the identifier with a colon. The reason for this format is that Novell charges a fee for each prefix registered. This way a developer can register one prefix and use it for unlimited schema extensions. Additionally, you will need to register your schema extensions with Novell if you want to have your applications certified by Novell Labs. For more information see, http://developer.novell.com/support/schreg2c.htm.

Sample Code

The following sample code illustrates how to do the following:

  • Define a new attribute

  • Define a new class

  • Create a new object from the new class definition

  • Populate the new attribute with a value

  • Remove the new object

  • Remove the new class definition

  • Remove the new attribute

  • Clean up

The source code can be compiled into a NetWare Loadable Module as is or into a MS-DOS executable by changing the platform definition and the header files.

Note: NDS is a hierarchical database and as such enforces referential integrity. If your class definition refers to an attribute that does not exist in the NDS tree, then the class can not be created. Likewise, if you attempt to remove a class definition or attribute definition and there are still objects in the NDS tree that use these definitions, you will be unable to remove them.

/**************************************************************************
** Copyright (c) 1997, 1998, 1999 Novell, Inc.  All Rights Reserved.

**

** THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
** TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE
** LICENSE AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK)
** THAT CONTAINS THIS WORK.
** 
** Pursuant to the SDK License Agreement, Novell hereby grants to
** Developer a royalty-free, non-exclusive license to include the
** sample code SCHEMA.C and derivative binaries in its product.
** Novell grants to Developer worldwide distribution rights to market,
** distribute or sell the sample code SCHEMA.C and derivative
** binaries as a component of Developer's product(s). Novell shall
** have no obligations to Developer or Developer's customers with
** respect to this code.
** 
** DISCLAIMER:
** 
** Novell, Inc. makes no representations or warranties with respect
** to the contents or use of this code, and specifically disclaims any
** express or implied warranties of merchantability or fitness for any
** particular purpose. Further, Novell, Inc. reserves the right to revise
** this publication and to make changes to its content, at any time,
** without obligation to notify any person or entity of such revisions or
** changes.
**
** Further, Novell, Inc. makes no representations or warranties with
** respect to any software, and specifically disclaims any express or
** implied warranties of merchantability or fitness for any particular
** purpose. Further, Novell, Inc. reserves the right to make changes to
** any and all parts of the software, at any time, without obligation to
** notify any person or entity of such changes.
**
***************************************************************************
**
** File: SCHEMA.C
**
** This program will define a new attribute called Second Home Directory.
** Next a new class definition will be created in the NetWare Directory
** Services Schema called MyClass, utilizing the new attribute. Finally
** an object, myObject, will be created and the new object's Second Home
** Directory will be populated.
**
** The second portion of this program will remove the new object,
** myObject. Next it will delete the new class definition, MyClass.
** Lastly it will remove the new attribute definition for Second Home
** Directory. 
**
**
**
** Programmers:
**
** Ini   Who                  Firm
** ---------------------------------------------------------------------
** KDB   Kevin D. Burnett     Novell, Inc. 
**
**************************************************************************/

/**************************************************************************

** Explanation of the program.
**
** Expanding the Directory Schema.
** You can expand the Directory Services Schema by creating new class,
** attribute, and object definitions. These changes will be replicated
** on all name servers within the Directory tree.
**
** This program is broken into three parts:
** 1. Defining a new attribute.
** 2. Defining a new class.
** 3. Creating a new object to use the new class and attribute definitions. 
**
** NOTE: If you expand the Base Schema, it is your responsibility to
** provide utilities that your customers can use to access the new object
** classes and attribute types. ConsoleOne includes rudimentary editing
** capabilities.
**
**************************************************************************/

/**************************************************************************
** Headers and function prototypes.
*/

   /*------------------------------------------------
   ** Defines
   */
   #define N_PLAT_NLM /* X-plat libraries need to know the platform. */
   #define TRUE 1
   #define FALSE 0

   /*------------------------------------------------
   ** ANSI Include headers.
   */
   #include <stdio.h>

   /*------------------------------------------------
   ** Novell specific Include headers.
   */

   #include <nwdsapi.h>
   #include <nwdsdc.h>
   #include <nwdsdefs.h>
   #include <nwapidef.h>
   #include <nwdsnmtp.h>
//   #include <nwnet.h>
//   #include <nwcalls.h>

/**************************************************************************
** main
*/
void main (void)
{
NWDSCCODE            ccode; 
   NWDSContextHandle    context;
   NWATTR_INFO          newAttr;       // attribute structure
   NWCLASS_INFO         myClassInfo;   // class info structure 
   pBuf_T               myClassBuf;
   pBuf_T               myObjectBuf;
   nint32               myClassBufFlag = FALSE;
   nint32               myObjectBufFlag = FALSE;
   nstr8                userName[NW_MAX_USER_NAME_LEN];
   nstr8                userPassword[50];
   int32	 iterationHandle = NO_MORE_ITERATIONS;

*------------------------------------------------
   ** Initialize NWCalls.  This is only required for the client, but 
   ** won't hurt on the server.
   */
   ccode = NWCallsInit(NULL, NULL);
   if(ccode != 0)
   {
      printf("\nNWCallsInit failed.");
      goto errorExitGeneral;
   }

   /*------------------------------------------------
   ** Create a NDS context.
   */
   ccode = NWDSCreateContextHandle(&context);
   if(ccode != 0)
   {
      printf("\nNWDSCreateContextHandle failed.");
      goto errorExitGeneral;
   }

   /*------------------------------------------------
   ** As an NLM as well as on the client, we need to be authenticated to NDS.
   ** Get user name and password.
   */
      printf("\nMust authenticate to NDS");
      printf("\nEnter User name: ");
   gets(userName);
      printf("Enter User Password: ");
   gets(userPassword);

   /*------------------------------------------------
   ** Perform the Login/authentication.
	*/
   ccode = NWDSLogin(context, 0, userName, userPassword, 0);
   if(ccode)
   {
      printf("\nNWDSLogin returned %d", ccode);
      goto errorExitContext;
   }
   else
   {
      printf("\nAuthentication successful.");
      printf("\nStart process of defining a new class/attribute.");
   }

/**************************************************************************
** Create the new attribute.
**
** Following are steps for creating a New Attribute Definition.
**
** Before creating a new attribute, you should examine the attributes provided
** by the Base Schema to determine if any of them will meet your needs.
** If not, you can create a new attribute type.
**
** This example program demonstrates the creation of a new attribute definition
** named "Second Home Directory" that can be used to hold the name of an
** alternate home directory.
**
** An attribute named "Home Directory" is listed in the "Attribute Type
** Definitions" chapter, in the NDS schema reference in the NDK.
** Most of the definition for the "Home Directory" attribute works for
** defining the "Second Home Directory" attribute.

**
** The "Second Home Directory" attribute is defined as follows:
**
** Syntax is "case insensitive string" or "Path."
** The constraints are DS_SINGLE_VALUED_ATTR + DS_SIZED_ATTR.
** The size of the value pointed to can be from 1 to 255 characters.
**
**
** To create the "Second Home Directory" attribute, complete the following steps:
**
** 1.Declare a structure of type NWATTR_INFO and fill it in.
**
** NWDSCCODE ccode; 
** NWATTR_INFO newAttr; 
**  
**	This code assumes you have created your context and have 
**	logged in as an object that has permission to extend the 
**	Schema.You need to have write rights to the root partition 
**	and logging in as Admin will acomplish this. You must also 
**	have initialized the Unicode tables if you are on a 
**	workstation client. 
** 
** newAttr.attrFlags=DS_SINGLE_VALUED_ATTR | DS_SIZED_ATTR; 
** newAttr.attrSyntaxID=SYN_CI_STRING; 
** newAttr.attrLower=1; 
** newAttr.attrUpper=255;
**  
** This example does not use the asn1ID field.
**
** newAttr.asn1ID.length=0; 
** newAttr.asn1ID.data[0]=0;
**
** 2.Create the new attribute definition.
**
** ccode= NWDSDefineAttr(context,"Second Home Directory",
**                       &newAttr); 
** if(ccode < 0) 
**   printf("Attribute could not be added. 
**           NWDSDefineAttr returned %d\n",err);
**
**************************************************************************/

   /*------------------------------------------------
   ** Set up the NWATTR_INFO structure.
   */
   newAttr.attrFlags = DS_SINGLE_VALUED_ATTR | DS_SIZED_ATTR; 
   newAttr.attrSyntaxID = SYN_CI_STRING;
   newAttr.attrLower = 1; 
   newAttr.attrUpper = 255; 
   newAttr.asn1ID.length = 0; /* This example does not use the asn1ID field. */
   newAttr.asn1ID.data[0] = 0; /* This example does not use the asn1ID field. */ 

   /*------------------------------------------------

   ** Add the new attribute definition to the schema.
   */
   ccode= NWDSDefineAttr(context,"Second Home Directory", &newAttr);
   if(ccode == ERR_ATTRIBUTE_ALREADY_EXISTS)
   {
      printf("\nNWDSDefineAttr, Second Home Directory, already exists.");
      goto defineClass;
   }

	else if(ccode != 0)
   { 
      printf("\nNWDSDefineAttr, Second Home Directory, returned %d", ccode);
      goto errorExitLogin;
   }
   else
      printf("\nNWDSDefineAttr `Second Home Directory' successful.");

/**************************************************************************
** Define the new class.
**
** Creating a Class Definition
**
** To create a new object class, you must follow a five-step process:
**
** 1. Declare the needed variables.
** 2. Allocate and initialize a request buffer.
** 3. Fill out the class information structure.
** 4. Place the attribute information into the request buffer.
** 5. Create the new object definition.
**
** Suppose you wanted to create a new class called "MyClass" class.
** Your first step would be to declare the needed variables:
**
** NWDSCCODE      ccode;
** NWCLASS_INFO   MyClass;       // class info structure
** NWDS_BUFFER    *myClassBuf;   // request buffer
**
** A buffer of type NWDS_BUFFER is needed as a request buffer and a structure of
** type NWCLASS_INFO is needed to store the class information.
**
** The next step is to allocate and initialize the request buffer:
**
** ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myClassBuf);
** ccode = NWDSInitBuf(context, DSV_DEFINE_CLASS, myClassBuf);
**
** Once the buffer has been initialized, your next step is to fill out the
** class information structure:
**
** myClassInfo.classFlags = DS_EFFECTIVE_CLASS;   // Set flags to effective class
** myClassInfo.ans1ID.length = 0;                 // Not used in this example
** myClassInfo.ans1ID.data[0] = 0;                // Not used in this example
**
** The class information structure has only two fields:
**
** classFlags
** Defines the class type. If the effective class flag was cleared,
** the class would be noneffective. Other useful flags are DS_CONTAINER_CLASS and 
**	DS_AUXILIARY_CLASS.
**
** ans1ID
** When developers register their schema with Novell, they are given an asn1ID,
** which is the developer's identification. This field (structure) is provided
** for developers to pass in their asn1ID information.
**
** With the structure filled out, your next step is to start placing the attribute
** information into the request buffer. There are five categories of class items
** that you must place into the class buffer. You call NWDSBeginClassItem to start
** a new category. In fact, this function must be called for each category, even if
** you are not going to add items to it. To add items to a category, you call
** NWDSPutClassItem for each item added. Here are the five categories of class items
** in the order they are called:

**
** 1.Super class names
** 2.Containment class names
** 3.Naming attribute names
** 4.Mandatory attribute names
** 5.Optional attribute names
**
** Super class names:
** ccode = NWDSBeginClassItem(context, myClassBuf);
** ccode = NWDSPutClassItem(context, myClassBuf, "TOP");
**
** Containment class:
** ccode = NWDSBeginClassItem(context, myClassBuf);
** ccode = NWDSPutClassItem(context, myClassBuf, "TOP");
** ccode = NWDSPutClassItem(context, myClassBuf, "Organization");
** ccode = NWDSPutClassItem(context, myClassBuf, "Organizational Unit");
**
** Naming attributes:
** ccode = NWDSBeginClassItem(context, myClassBuf);
** ccode = NWDSPutClassItem(context, myClassBuf, "CN");
**
** Mandatory attributes:
** ccode = NWDSBeginClassItem(context, myClassBuf);
** ccode = NWDSPutClassItem(context, myClassBuf, "CN");
** ccode = NWDSPutClassItem(context, myClassBuf, "Second Home Directory");
**
** Optional attributes:
** ccode = NWDSBeginClassItem(context, myClassBuf);
**
** Mandatory attributes must be added during the creation of a class
** definition. You cannot go back later and use NWDSModifyClassDef
** to add a mandatory attribute. Nevertheless, you should be very
** conservative about making an attribute mandatory.
**
** The final step is to create the new object class definition:
**
** ccode = NWDSDefineClass(context, "MyClass", &myClassInfo, myClassBuf);
** if (ccode != 0)
**   printf("Error while creating class: %d\n", ccode);
**
** NWDSDefineClass requires four arguments: the Directory context handle,
** the name of the new class, the address of the class information structure,
** and a pointer to the request buffer.
**************************************************************************/
defineClass:

    /*------------------------------------------------
   ** Define the class that we are going to extend
   ** the schema to.
   ** First allocate a request buffer.
   */
   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myClassBuf);
	if(ccode != 0)
   {
      printf("\nNWDSAllocBuf, myClassBuf, returned %d", ccode);
      goto errorExitmyObjectBuf;
	}
   else
      printf("\nNWDSAllocBuf, myClassBuf, successful.");

   /*------------------------------------------------
   ** Next initialize the request buffer.

   */
   ccode = NWDSInitBuf(context, DSV_DEFINE_CLASS, myClassBuf);
   if(ccode != 0)
   {
      printf("\nNWDSInitBuf, class, returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSInitBuf, class, successful.");

   /*------------------------------------------------
   ** Fill out the class information structure.
   */
   myClassInfo.classFlags = DS_EFFECTIVE_CLASS;   // Need to be able to create object.  
   myClassInfo.asn1ID.length = 0;                 // Not used in this example
   myClassInfo.asn1ID.data[0] = 0;                // Not used in this example

   /*------------------------------------------------
   ** Prepare to put information into myClassBuffer.
   ** This needs to be called five times for each of
   ** the five possibilities:
   **
   ** 1. Super Class Definitions.
   */
   ccode = NWDSBeginClassItem(context, myClassBuf);
   if(ccode != 0)
   {
      printf("\nNWDSBeginClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSBeginClassItem, 1, successful.");

   ccode = NWDSPutClassItem(context, myClassBuf, "TOP");
   if(ccode)
   {
      printf("\nNWDSPutClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassitem: TOP, successful");

   /*------------------------------------------------
   ** 2. Containment class names.
   */
   ccode = NWDSBeginClassItem(context, myClassBuf);
   if(ccode != 0)
   {
      printf("\n NWDSBeginClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
	else
      printf("\nNWDSBeginClass, 2, successful");

    ccode = NWDSPutClassItem(context, myClassBuf, C_ORGANIZATIONAL_UNIT);
    if(ccode)
   {
      printf("\nNWDSPutClassItem returned %d", ccode);

		goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassItem: Organazational Unit, successful.");

   ccode = NWDSPutClassItem(context, myClassBuf, "Organization");
   if(ccode)
   {
      printf("\nNWDSPutClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassItem: Organization, successful.");

   /*------------------------------------------------
   ** 3. Naming attribute names.
   */
   ccode = NWDSBeginClassItem(context, myClassBuf);
   if(ccode != 0)
   {
      printf("\nNWDSBeginClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSBeginClass, 3, successful.");

   ccode = NWDSPutClassItem(context, myClassBuf, "CN");
   if(ccode)
   {
      printf("\nNWDSPutClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassItem: CN, successful.");

   /*------------------------------------------------
   ** 4. Mandatory attribute names.
   */
   ccode = NWDSBeginClassItem(context, myClassBuf);
   if(ccode != 0)
   {
      printf("\nNWDSBeginClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSBeginClassItem, 4, successful.");

   ccode = NWDSPutClassItem(context, myClassBuf, "CN"); /* create object with name */
   if(ccode != 0)
	{
      printf("\nNWDSPutClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassItem: CN, successful.");

   ccode = NWDSPutClassItem(context, myClassBuf, "Second Home Directory");
   if(ccode)
   {

      printf("\nNWDSPutClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSPutClassItem: Second Home Directory, successful.");

   /*------------------------------------------------
   ** 5. Optional attribute names.
   ** We won't use any in this example program.
   */
   ccode = NWDSBeginClassItem(context, myClassBuf);
   if(ccode != 0)
   {
      printf("\nNWDSBeginClassItem returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSBeginClassitem, 5, successful.");

   /*------------------------------------------------
   ** Define the new class.
   */
   ccode = NWDSDefineClass(context, "MyClass", &myClassInfo, myClassBuf);
   if(ccode == ERR_CLASS_ALREADY_EXISTS)
   {
      printf("\nNWDSDefineClass, MyClass, already exists.");
      goto createObject;
   }
   else if(ccode != 0)
   {
      printf("\nNWDSDefineClass returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSDefineClass: MyClass, successful.");

   /*------------------------------------------------
   ** Free the buffer used for the class creation
   ** process.
   */
   ccode = NWDSFreeBuf(myClassBuf);
   myClassBufFlag = TRUE;
   if(ccode)
   {
      printf("\nNWDSFreeBuf, myClassBuffer, returned %d", ccode);
      goto errorExitmyClassBuf;

   }
   else
      printf("\nNWDSFreeBuf, myClassBuffer, successful.");

/**************************************************************************
** Create an object using the new class definition.
**
** To create a new object, you must use the following process:
**
** 1. Allocate a buffer to create new object.
** 2. Initialize buffer to be a ADD_OBJECT buffer.
** 3. Let NDS know what kind of object is being created.
** 4. Name the new object.
** 5. If adding an attribute, specify the name.
** 6. You may want to populate the new attribute.
** 7. Create the new object.

**
** Allocate the buffer to create the new object.
** ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myObjectBuf);
**
** Initialize buffer to be a ADD_OBJECT buffer.
** ccode = NWDSInitBuf(context, DSV_ADD_ENTRY, myObjectBuf);
**
** Let NDS know what kind of object is being created.
** ccode = NWDSPutAttrName(context, myObjectBuf, A_OBJECT_CLASS);
**
** Name the new object.
** ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CLASS_NAME, "MyClass");
**
** If adding an attribute, specify the name.
** ccode = NWDSPutAttrName(context, myObjectBuf, "Second Home Directory");
**
** You may want to populate the new attribute.
** ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CI_STRING, "SERVER\\VOL:DIR\\DIR");
**
** Create the new object.
** ccode = NWDSAddObject(context, "myObject", NULL, 0, myObjectBuf);
**
** NWDSAddObject requires five arguments: the Directory context handle,
** the name of the object to be added, the iteration handle (this is
** reserved, pass NULL), more (a reserved parameter -- pass 0),
** and a pointer to the request buffer.
**************************************************************************/
createObject:

   /*------------------------------------------------
   ** Allocate buffer to create new object.
   */
   ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &myObjectBuf);
   if(ccode)
   {
      printf("\nNWDSAllocBuf returned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSAllocBuf, myObjectBuf, successful.");

   /*------------------------------------------------
   ** Initialize buffer to be a ADD OBJECT buffer.
   */
   ccode = NWDSInitBuf(context, DSV_ADD_ENTRY, myObjectBuf);
   if(ccode)
   {
      printf("\nNWDSInitBuf returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
      printf("\nNWDSInitBuf, myObjectBuf, successful.");

   /*------------------------------------------------
   ** Let NDS know what kind of object we are creating.
   */
   ccode = NWDSPutAttrName(context, myObjectBuf, A_OBJECT_CLASS);
   if(ccode)
   {
      printf("\nNWDSPutAttrName, A_OBJECT_CLASS, returned %d", ccode);
      goto errorExitmyObjectBuf;

   }
   else
      printf("\nNWDSPutAttrName, A_OBJECT_CLASS, successful.");

   /*------------------------------------------------
   ** Name the new object.
   */
   ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CLASS_NAME, "MyClass");
   if(ccode)
   {
      printf("\nNWDSPutAttrVal, MyClass, returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
      printf("\nNWDSPutAttrVal, MyClass, successful.");

   /*------------------------------------------------
   ** Specify the name of the attribute to add.
   */
   ccode = NWDSPutAttrName(context, myObjectBuf, "Second Home Directory");
   if(ccode)
   {
      printf("\nNWDSPutAttrName, Second Home Directory, returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
      printf("\nNWDSPutAttrName, Second Home Directory, successful.");

   /*------------------------------------------------
   ** Populate the new attribute with a value.
   */
   ccode = NWDSPutAttrVal(context, myObjectBuf, SYN_CI_STRING, "SERVER\\VOL:DIR\\DIR");
   if(ccode)
   {
      printf("\nNWDSPutAttrVal, SERVER\\VOL:DIR\\DIR, returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
		printf("\nNWDSPutAttrVal, SERVER\\VOL:DIR\\DIR, successful.");

   /*------------------------------------------------
   ** Create the new object using the template of
   ** "MyClass."
   */
   ccode = NWDSAddObject(context, "myObject", &iterationHandle, 0, myObjectBuf);
   if((ccode == DSERR_OBJECT_ALREADY_EXISTS) || (ccode == ERR_ENTRY_ALREADY_EXISTS))
   {
      printf("\nNWDSAddObject, myObject, already exists.");
      goto removeMessage;
   }
   else if(ccode)
   {
      printf("\nNWDSAddObject, myObject, returned %d", ccode);
      goto removeMessage;

   }
   else
      printf("\nNWDSAddObject, myObject, successful.");

   /*------------------------------------------------
   ** Free myObjectBuf buffer.
   */
   ccode = NWDSFreeBuf(myObjectBuf);
   myObjectBufFlag = TRUE;
   if(ccode)
   {
      printf("\nNWDSFreeBuf, myObjectBuf, reutrned %d", ccode);
      goto errorExitmyClassBuf;
   }
   else
      printf("\nNWDSFreeBuf, muObjectBuf, successful.");

/**************************************************************************
** Pause after creating the new attribute definition, class definition
** and object.  This is so the companion program, READSCHM can display
** the value of the Second Home Directory attribute.
**************************************************************************/
removeMessage:

   /*------------------------------------------------
   ** Wait for key press before we delete the
   ** new class and "Second Home Directory" attribute.
   */
   printf("\nPress any key to delete the `Second Home Directory' attribute.\n");
   getch();
   printf("\nStart process of deleting `Second Home Directory' attribute.");

   /*------------------------------------------------
   ** Remove the new object.
   */
   ccode = NWDSRemoveObject(context, "myObject");
   if(ccode == ERR_NO_SUCH_ENTRY)
   {
      printf("\nNWDSRemoveObject, myObject, does not exist.");
      goto removeClass;
   }   
   else if(ccode != 0)
   {
      printf("\nNWDSRemoveObject, myObject, returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
      printf("\nNWDSRemoveObject, myObject, successful.");

   /*------------------------------------------------
   ** Remove the definition for "MyClass."
   */
   removeClass:
   ccode = NWDSRemoveClassDef(context, "MyClass");
   if(ccode == ERR_NO_SUCH_CLASS)
   {
      printf("\nNWDSRemoveClassDef, MyClass, does not exist.");
      goto removeAttr;
   }
   else if(ccode != 0)
   {

      printf("\nNWDSRemoveClassDef, MyClass, returned %d", ccode);
      goto errorExitmyObjectBuf;

   }
   else
      printf("\nNWDSRemoveClassDef, MyClass, successful.");

   /*------------------------------------------------
   ** Remove the new attribute definition.
   */
   removeAttr:   
   ccode = NWDSRemoveAttrDef(context, "Second Home Directory");
   if(ccode == ERR_NO_SUCH_ATTRIBUTE)
   {
      printf("\nNWDSRemoveAttrDef, Second Home Directory, does not exist.");
      goto cleanup;
   }   
   else if(ccode != 0)
   {
      printf("\nNWDSRemoveAttrDef, Second Home Directory, returned %d", ccode);
      goto errorExitmyObjectBuf;
   }
   else
      printf("\nNWDSRemoveAttrDef, Second Home Directory, successful.");
   /*------------------------------------------------
   ** Cleanup.
   */
cleanup:   
errorExitmyObjectBuf:
   if(myObjectBufFlag == FALSE)
      NWDSFreeBuf(myObjectBuf); /* function always returns 0 */

errorExitmyClassBuf:
   if(myClassBufFlag == FALSE)
      NWDSFreeBuf(myClassBuf); /* function always returns 0 */

errorExitLogin:
   ccode = NWDSLogout(context);
   if(ccode)
      printf("\nNWDSLogout returned %d", ccode);
   else
      printf("\nNWDSLogout successful.");

errorExitContext:
   ccode = NWDSFreeContext(context);
   if(ccode)
      printf("\nNWDSFreeContext returned %d", ccode);
   else
      printf("\nNWDSFreeContext successful.");

errorExitGeneral:
   return;    
}

/**************************************************************************
**************************************************************************/

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates