Using C or C

     

Using C or C++

The C programming language has been, and still is, the programming language of choice for systems programming. It is, therefore, not a surprise that Novell's initial Software Developer Kit (SDK; now known as the NDK) efforts were placed in C libraries. Even today, the NetWare Loadable Module (NLM) libraries are still C based, and workstation application developers now have more options in their choice of programming language (see the "Other Programming and Scripting Languages" section, later in this chapter).

NOTE

From a performance viewpoint, NLMs work better than workstation-based applications because in many cases, NLMs do not generate network traffic as they run on the servers. However, because of the error exception handling routines used by C++ compilers, C++ generally is not suitable for NLM development. If you want to use C++ for NLMs, consider using the Metrowerks CodeWarrior compiler. Visit http://developer.novell.com/ndk/cwpdk.htm for more information.


When you're programming for NDS, there are a number of operations that your application must perform locally when accessing DS information. The operations include the following:

  • Working with naming conventions

  • Maintaining directory context data

  • Initializing Unicode tables

  • Managing local buffers

As mentioned previously, a DS object may be referenced in multiple ways: using its relative distinguished name (RDN) or its distinguished name (DN) in typeful or typeless naming syntax. DS operates on canonical names only ”that is, names that include full naming paths, with a type specification for each naming component (typeful DNs). Because it's not always convenient or practical to store or use canonical names, APIs enable you to use partial names and enable the underlying library routines to handle the conversion.

Most DS API calls that involve character data or interact with the NDS/eDirectory tree require additional information to be supplied. This is to indicate the default context and whether the character data should be translated to Unicode. This information is collectively held in a structure called the directory context and is used to pass the following details:

  • The default context

  • Whether alias objects should be dereferenced

  • Whether character data should be translated to and from Unicode

  • Whether object names should be given in canonical form

  • Whether object names should be given in typeless form

When a directory context (which is essentially a memory pointer) is created, the default context is the same as the workstation's current context, and the four operations listed previously are performed. The directory context should be freed when it is no longer needed (normally when the application terminates).

You can read and modify the information in a directory context. You need to be careful when changing the default context. You can set it to an arbitrary string, unlike with the CX command-line utility, which does not set the default context to an illegal value. If the default context is set to an illegal value, any API calls that rely on the directory context will fail.

The same object-naming conventions apply to the NDS API calls as to the NetWare command-line utilities. That is, leading and trailing periods can be used to modify the default context, and the type qualifiers, such as C= , can be inserted into the name to override the default typing.

The Unicode conversion tables have to be loaded before a workstation application can access NDS (this is not necessary for NLMs), and they should be released prior to an application's terminating. These tables are used to convert character data between the general Unicode format used by the NDS and the local format used by the application. You load the Unicode tables by executing the Unicode API NWInitUnicodeTables() and specifying the local code page. You can obtain the local code page by executing the Internationalization Services APIs NWLsetlocale() and NWLlocaleconv() .

DS API functions use buffers for sending and receiving data between the application and NDS/eDirectory. For example, a typical sequence in reading an object's attribute values would be to initialize an input buffer, load the buffer with the list of desired attributes, execute the read object command, and unload the attribute values from an output buffer. Given that the output buffer is finite, it is sometimes necessary to call the read function repeatedly to get subsequent values. This protocol of using input and output buffers provides a general interface for transferring data.

Memory for directory buffers is allocated using an DS API call. The suggested amount of memory to allocate is given by the constant DEFAULT_MESSAGE_LEN that is set to 4096 bytes. This value is normally more than sufficient for a typical DS application. The only exception under normal circumstances would be when there is a single attribute value (such as a stream file) that will not fit in the buffer (thus resulting in a -119 error [buffer too small]). If you are unsure, you can consider using MAX_MESSAGE_LEN , which has a 63KB value, or using your own setting. Directory buffers should be freed when they are no longer needed (normally when the application terminates); otherwise , memory leaks result.

The directory buffer management tasks make DS-aware applications more complex than bindery-based programs; don't let that discourage you from developing DS utilities, however, because it only looks harder than it is.

The following is an example of C source code that enables you to change a user 's telephone attribute from a command line. Error checking has been removed to simplify the example:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #define N_PLAT_DOS #include <nwcalls.h> #include <nwnet.h> #include <nwlocale.h> void main (int argc, char *argv[]) {      NWDSContextHandle     Cx;      NWDS_BUFFER           *inBuf;      LCONV                 lconvInfo;      if ( argc < 4 ) {           printf ("Usage: ");           printf ("Newnumber objectname oldTnum newTnum\n");           exit (1);      } // Needed for workstation-based APIs only.      NWCallsInit (NULL, NULL); // Unicode table must be loaded in order to use DS calls.      NWLlocaleconv (&lconvInfo);      NWInitUnicodeTables (lconvInfo.country_id,                             lconvInfo.code_page); // Create directory context      Cx = NWDSCreateContext (); // Allocate local buffers      NWDSAllocBuf (DEFAULT_MESSAGE_LEN, &inBuf);      NWDSInitBuf (Cx, DSV_MODIFY_ENTRY, inBuf); // First delete the old value and then add the new one // (Can not use DS_OVERWRITE_VALUE because Telephone Number //  is multi-valued and the API would not know which value //  to overwrite, if there is more than one) // The two actions can be combined into the same "buffer"      NWDSPutChange (Cx, inBuf, DS_REMOVE_VALUE,                      "Telephone Number");      NWDSPutAttrVal (Cx, inBuf, SYN_CI_STRING, argv[2]);      NWDSPutChange (Cx, inBuf, DS_ADD_VALUE,                      "Telephone Number");      NWDSPutAttrVal (Cx, inBuf, SYN_CI_STRING, argv[3]);      if ( NWDSModifyObject (Cx, argv[1], NULL, (NWFLAGS)0,                               inBuf) == 0 ) {           printf ("%s's old telephone number [%s] has been\n",                     argv[1], argv[2]);           printf ("replaced by a new number [%s]\n", argv[3]);      }      else            printf ("Unable to change telephone number.\n"); // Free allocated resources.      NWDSFreeBuf (inBuf);      NWDSFreeContext (Cx);      NWFreeUnicodeTables ();      exit(0); } 

If you have an external database that contains up-to-date user information, you can use a modified version of this example to read the telephone number (or other attribute values) from this database and repopulate the user objects with the information when needed.

You might be interested in knowing that when you modify eDirectory data via LDAP, you do not need to deal with allocating buffers, directory context, and so on (as is the case when using DS calls). You do, however, have to set up the necessary code to connect and bind to the LDAP server (where a workstation-based DS utility would not require but an NLM would require additional code to authenticate to NDS prior to changes being made). The following is a sample C code snippet (based on Novell's modattrs.c example) that changes the telephone number:

 #include <stdio.h> #include <stdlib.h> #include <ldap.h> int main( int argc, char **argv) {       int            version, ldapPort, rc;       char           *ldapHost, *loginDN, *password, *modifyDN;       char           *phoneValues[2];       LDAP           *ld;       LDAPMod        modPhone, *modify[2];       // 10-second connection timeout       struct timeval timeOut = {10,0};       if (argc != 6) {            printf( "\n Usage:   modattrs <host name> ");            printf ("<port number> <login dn> <password>\n");            printf ("\n\t <modify dn>\n");            printf ("\n Example: modattrs acme.com 389 ");            printf ("cn=admin,o=acme secret\n");            printf ("\n\t cn=admin,ou=demo,o=testing\n");            return(1);      } // Set up connection and binding info      ldapHost    =    argv[1];      ldapPort    =    atoi(argv[2]);      loginDN     =    argv[3];      password    =    argv[4];      modifyDN    =    argv[5]; // Set LDAP version to 3 and set connection timeout      version = LDAP_VERSION3;      ldap_set_option ( NULL, LDAP_OPT_PROTOCOL_VERSION,                         &version);      ldap_set_option ( NULL, LDAP_OPT_NETWORK_TIMEOUT,                         &timeOut); // Initialize the LDAP session      if (( ld = ldap_init ( ldapHost, ldapPort )) == NULL) {           printf ( "\n\tLDAP session initialization failed\n");           return( 1 );      }      printf ("\n\tLDAP session initialized\n"); // Bind to the server      rc = ldap_simple_bind_s( ld, loginDN, password );      if (rc != LDAP_SUCCESS ) {           printf ("ldap_simple_bind_s: %s\n",                     ldap_err2string( rc ));           ldap_unbind_s ( ld );           return( 1 );      }      printf ("\n\tBind successful\n"); /*      * To modify the attributes of an entry      *    1  Specify the modify actions      *    2. Specify the attribute name to be modified      *    3. Specify the value of the attribute      *    4. Add to an array of LDAPMod structures      *    5. Call ldap_modify_ext_s */ //LDAP_MOD_REPLACE succeeds whether the value //already exists or not     modPhone.mod_op = LDAP_MOD_REPLACE     modPhone.mod_type = "telephoneNumber";     phoneValues[0] = "1 234 567 8910";     phoneValues[1] = NULL;     modPhone.mod_values = phoneValues; //If you have more than one modifications to make, increase //the array size of modify[] and add more "&mod*" entries     modify[0] = &modPhone;     modify[1] = NULL; //Modify the attributes      rc= ldap_modify_ext_s (ld,         // LDAP session handle                               modifyDN,   // the object to //modify                               modify,     // array of LDAPMod                                           // structures                               NULL,       // server controls                               NULL);      // client controls      if ( rc != LDAP_SUCCESS ) {           printf ("ldap_modify_ext_s: %s\n",                    ldap_err2string( rc ));           ldap_unbind_s( ld );           return(1);      }      printf ("\n\t%s modified successfully.\n", modifyDN);      ldap_unbind_s( ld );      return( 0 ); } 

As you can see, although the LDAP code is longer than the DS-based code presented earlier in this chapter, about half of it actually has to deal with connecting and binding to the LDAP server. The actual code necessary for updating the attribute value is less than 10 lines, and most of it is just setup code.

Although the amount of work involved in using DS calls instead of LDAP calls seem about the same, the big advantage of using LDAP is that your application will also work with directories other than NDS. If you have to update a number of different directories with the same data and they all support LDAP, using LDAP calls would be the better way to go.



Novell's Guide to Troubleshooting eDirectory
Novells Guide to Troubleshooting eDirectory
ISBN: 0789731460
EAN: 2147483647
Year: 2003
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net