]> git.sur5r.net Git - openldap/commitdiff
Update documentation. Add support for caching, sorting, enabling "all"
authorRandy Kunkee <kunkee@openldap.org>
Wed, 22 Dec 1999 18:19:11 +0000 (18:19 +0000)
committerRandy Kunkee <kunkee@openldap.org>
Wed, 22 Dec 1999 18:19:11 +0000 (18:19 +0000)
in searches, and experimental "trap" subcommand.

contrib/ldaptcl/ldap.n
contrib/ldaptcl/neoXldap.c

index c7b65f17019e3792c2c4930572b6ffdd27e6b204..685540310f095242d7ecfc64f769548da10c7616 100644 (file)
@@ -26,7 +26,7 @@ A new command by the name of \fIcommand\fR will be created to access
 the LDAP database at \fIhostlist\fR.  \fIhostlist\fR may contain elements
 of the format \fBhost:port\fR if a port other than the default LDAP port
 of 389 is required.  The LDAP library will attempt to connect to each
-host in turn until it succeeds.
+host in turn until it succeeds or exhausts the list.
 .PP
 The \fBexplode\fR form provides a means (via ldap_explode(3)) to explode a DN
 into its component parts.  \fB-nonames\fR strips off the attribute names,
@@ -64,9 +64,6 @@ deferred until we actually try to do something.
 For the purposes of this example, we're going to assume that "foo" is the
 command created by opening a connection using "ldap open".
 
-Note:  Karl is particularly dissatisfied with the syntax of this option,
-so it is one of the most likely things to change in a subsequent release.
-
 .SH BINDING
 
 After a connection is made to an LDAP server, an LDAP bind operation must
@@ -74,11 +71,10 @@ be performed before other operations can be attempted over the connection.
 
 Both simple authentication and kerberos authentication are available.
 LDAP version 3 supports many new "SSL"-style authentication and encryption
-systems, which are not currently supported by the UMich server, and hence
-by this interface package.
+systems, which are not currently supported by the OpenLDAP v1.2 server, and
+hence by this interface package.
 
-Currently simple authentication, and kerberos-based authentication, are
-supported.
+Currently simple and kerberos-based authentication, are supported.
 
 To use LDAP and still have reasonable security in a networked, 
 Internet/Intranet environment, secure shell can be used to setup
@@ -140,17 +136,19 @@ This creates a new distinguished name and defines zero or more attributes.
 
 "attributePairList" is a list of key-value pairs, the same as would
 be returned by "array get" if an array had been set up containing the
-key-value pairs.  Note that, right now, the sort of lowest-level pair
-of the DN must also appear in the attributePairList, as in:
+key-value pairs.
 
     foo add "cn=karl, ou=People, o=NeoSoft Inc, c=US" {cn karl ...}
 
+Some directory servers and/or their client SDKs will automatically
+add the leaf attribute value for you.
+
 Here is a more precise description of how an attributePairList looks:
 
     {cn {karl {Karl Lehenbauer}} telephone 713-968-5800}
 
 Note here that two cn values, "karl" and "Karl Lehenbauer", are added.
-A command error is to write
+Is it an error to write:
 
     {cn {Karl Lehenbauer}}
 
@@ -161,11 +159,11 @@ typing hard-coded lists.
 
 We have noticed that the Netscape server will automatically add the
 left-most rdn portion of the DN (ie. cn=karl), whereas the University
-of Michigan version does not.
+of Michigan and OpenLDAP 1.2 versions do not.
 
 .SH ADDING, DELETING, AND REPLACING OBJECT ATTRIBUTES
 
-You can have multiple occurrences of the same attribute in a record.
+You can have multiple values for a given attribute in an LDAP object.
 These are represented in search results, through the Tcl interface,
 as a list.
 
@@ -173,21 +171,23 @@ as a list.
 
 This adds key-value pairs to an existing DN.  If an attribute being
 added already exists, the new value will be appended to the list.
+If a particular value being added to an attribute already exists in
+the object a Tcl error is raised.
 
     foo replace_attributes dn attributePairList
 
-This replaces specified key-value pairs in an existing DN, leaving
-unnamed ones untouched.
+This replaces the specified attributes in an existing DN, leaving
+unnamed ones untouched.  Any previous values for the supplied attributes
+(if any) are discarded.
 
     foo delete_attributes dn attributePairList
 
-This deletes attributes in the list.  If a pair is "foo {bar snap}" and
-you delete "foo bar", "foo" will still have "snap".
+This deletes attributes in the list.  If an attribute "foo" has the
+value list {bar snap}, and you delete using the attributePairList "foo bar",
+"foo" will still have "snap".
 
-If you provide an empty string ("") for the value part of the key-value
-pair, the entire attribute will be deleted.  To reiterate, if you provide
-a non-empty string for the value part, only that value will be removed
-from the value list.
+If you provide an empty string ("") for the value list,
+the entire attribute will be deleted.
 
 .SH SEARCHING
 
@@ -233,8 +233,8 @@ version.
 
 .SH CACHING (Note: Netscape clients do not have caching interfaces).
 
-The UMich LDAP library offers the client application fairly fine-
-grained control of caching of results retrieved from searches, 
+The UMich and OpenLDAP client libraries offers the client application fairly
+fine-grained control of caching of results retrieved from searches, 
 offering significant performance improvement and reduced
 network traffic.
 
@@ -272,7 +272,10 @@ To enable caching of data received from an LDAP connection,
 
        This should be used, for example, after doing an add_attributes,
        delete_attributes, or replace_attributes (ldap_modify(3)) 
-       involving the requested DN.
+       involving the requested DN.  Generally this should not be needed,
+       as the Tcl interface automatically performs this operation on
+       any dn that is modified (add,replace,delete) while caching is
+       enabled.
 
     foo cache no_errors
 
@@ -295,7 +298,8 @@ Because we used the new "Tcl object" C interfaces, this package only works
 with Tcl 8.0 or above.
 
 This package interfaces with the University of Michigan LDAP protocol
-package, version 3.3, an implementation of version 2 of the LDAP protocol.
+package, version 3.3, and OpenLDAP version 1.2, both of which are
+implementations of version 2 of the LDAP protocol.
 
 Although an LDAP client (or server) could be written in native Tcl 8.0,
 as Tcl 8.0 and above can do binary I/O, and Tcl 8 and above have strings 
@@ -334,8 +338,8 @@ related to LDAP services.
 
 .SH AUTHORS
 It was written by Karl Lehenbauer, of NeoSoft, Inc., in August and
-September of 1997.  Ldap explode, and numerous bug fixes by Randy
-Kunkee, also of NeoSoft, Inc., in 1998.
+September of 1997.  Ldap explode, and numerous bug fixes and extensions
+by Randy Kunkee, also of NeoSoft, Inc., in 1998-1999.
 
 .SH KEYWORDS
 element, join, list, separator
index c172992627ec67ecc7a5ceb4f6b70cb44e35ac41..5d36668895824f6fae6aeefa50c06ab73ef835a3 100644 (file)
@@ -107,6 +107,8 @@ typedef struct ldaptclobj {
     int                caching;        /* flag 1/0 if caching is enabled */
     long       timeout;        /* timeout from last cache enable */
     long       maxmem;         /* maxmem from last cache enable */
+    Tcl_Obj    *trapCmdObj;    /* error handler */
+    int                *traplist;      /* list of errorCodes to trap */
     int                flags;
 } LDAPTCL;
 
@@ -118,7 +120,7 @@ typedef struct ldaptclobj {
 static
 LDAP_SetErrorCode(LDAPTCL *ldaptcl, int code, Tcl_Interp *interp)
 {
-    char shortbuf[6];
+    char shortbuf[16];
     char *errp;
     int   lderrno;
 
@@ -132,6 +134,33 @@ LDAP_SetErrorCode(LDAPTCL *ldaptcl, int code, Tcl_Interp *interp)
        errp = ldaptclerrorcode[code];
 
     Tcl_SetErrorCode(interp, errp, NULL);
+    if (ldaptcl->trapCmdObj) {
+       int *i;
+       Tcl_Obj *cmdObj;
+       if (ldaptcl->traplist != NULL) {
+           for (i = ldaptcl->traplist; *i && *i != code; i++)
+               ;
+           if (*i == 0) return;
+       }
+       (void) Tcl_EvalObj(interp, ldaptcl->trapCmdObj);
+    }
+}
+
+static
+LDAP_ErrorStringToCode(Tcl_Interp *interp, char *s)
+{
+    int offset;
+    int code;
+
+    offset = (strncasecmp(s, "LDAP_", 5) == 0) ? 0 : 5;
+    for (code = 0; code < LDAPTCL_MAXERR; code++) {
+       if (!ldaptclerrorcode[code]) continue;
+       if (strcasecmp(s, ldaptclerrorcode[code]+offset) == 0)
+           return code;
+    }
+    Tcl_ResetResult(interp);
+    Tcl_AppendResult(interp, s, " is an invalid code", (char *) NULL);
+    return -1;
 }
 
 /*-----------------------------------------------------------------------------
@@ -179,6 +208,8 @@ LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
            return TCL_ERROR;
        ldap_memfree(dn);
     }
+    attributeNameObj = Tcl_NewObj();
+    Tcl_IncrRefCount (attributeNameObj);
     for (attributeName = ldap_first_attribute (ldap, entry, &ber); 
       attributeName != NULL;
       attributeName = ldap_next_attribute(ldap, entry, ber)) {
@@ -192,13 +223,12 @@ LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
               as an error, we ignore it to present a consistent interface
               with Netscape's server
            */
-           attributeNameObj = Tcl_NewStringObj (attributeName, -1);
-           Tcl_IncrRefCount (attributeNameObj);
            attributeDataObj = Tcl_NewObj();
+           Tcl_SetStringObj(attributeNameObj, attributeName, -1);
            for (i = 0; bvals[i] != NULL; i++) {
                Tcl_Obj *singleAttributeValueObj;
 
-               singleAttributeValueObj = Tcl_NewStringObj (bvals[i]->bv_val, -1);
+               singleAttributeValueObj = Tcl_NewStringObj(bvals[i]->bv_val, bvals[i]->bv_len);
                if (Tcl_ListObjAppendElement (interp, 
                                              attributeDataObj, 
                                              singleAttributeValueObj) 
@@ -217,9 +247,9 @@ LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
                                TCL_LEAVE_ERR_MSG) == NULL) {
                return TCL_ERROR;
            }
-           Tcl_DecrRefCount (attributeNameObj);
        }
     }
+    Tcl_DecrRefCount (attributeNameObj);
     return Tcl_EvalObj (interp, evalCodeObj);
 }
 
@@ -268,8 +298,8 @@ LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
     int                  abandon;
     int                  tclResult = TCL_OK;
     int                  msgid;
-    LDAPMessage  *resultMessage;
-    LDAPMessage  *entryMessage;
+    LDAPMessage  *resultMessage = 0;
+    LDAPMessage  *entryMessage = 0;
     char         *sortKey;
 
     Tcl_Obj      *resultObj;
@@ -334,10 +364,13 @@ LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
        }
        if (resultCode == LDAP_RES_SEARCH_RESULT || all)
            break;
+       if (resultMessage)
        ldap_msgfree(resultMessage);
+       resultMessage = NULL;
     }
     if (abandon) {
-       ldap_msgfree(resultMessage);
+       if (resultMessage)
+           ldap_msgfree(resultMessage);
        if (resultCode == LDAP_RES_SEARCH_ENTRY)
            ldap_abandon(ldap, msgid);
        return tclResult;
@@ -367,10 +400,13 @@ LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
                              "LDAP search error: ",
                              ldap_err2string(errorCode),
                              (char *)NULL);
-      ldap_msgfree(resultMessage);
+      if (resultMessage)
+         ldap_msgfree(resultMessage);
       LDAP_SetErrorCode(ldaptcl, errorCode, interp);
       return TCL_ERROR;
     }
+    if (resultMessage)
+       ldap_msgfree(resultMessage);
     return tclResult;
 }
 
@@ -607,11 +643,11 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
 
        nPairs = attribObjc / 2;
 
-       modArray = (LDAPMod **)ckalloc (sizeof(LDAPMod *) * (nPairs + 1));
+       modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (nPairs + 1));
        modArray[nPairs] = (LDAPMod *) NULL;
 
        for (i = 0; i < nPairs; i++) {
-           mod = modArray[i] = (LDAPMod *) ckalloc (sizeof(LDAPMod));
+           mod = modArray[i] = (LDAPMod *) malloc (sizeof(LDAPMod));
            mod->mod_op = mod_op;
            mod->mod_type = Tcl_GetStringFromObj (attribObjv [i * 2], NULL);
 
@@ -621,7 +657,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
            }
 
            valPtrs = mod->mod_vals.modv_strvals = \
-               (char **)ckalloc (sizeof (char *) * (valuesObjc + 1));
+               (char **)malloc (sizeof (char *) * (valuesObjc + 1));
            valPtrs[valuesObjc] = (char *)NULL;
 
            for (j = 0; j < valuesObjc; j++) {
@@ -631,7 +667,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
                 * value be NULL to indicate entire attribute is to be 
                 * deleted */
                if ((*valPtrs [j] == '\0') 
-                   && (mod->mod_op == LDAP_MOD_DELETE)) {
+                   && (mod->mod_op == LDAP_MOD_DELETE || mod->mod_op == LDAP_MOD_REPLACE)) {
                        valPtrs [j] = NULL;
                }
            }
@@ -647,10 +683,10 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
 
         /* free the modArray elements, then the modArray itself. */
        for (i = 0; i < nPairs; i++) {
-           ckfree ((char *) modArray[i]->mod_vals.modv_strvals);
-           ckfree ((char *) modArray[i]);
+           free ((char *) modArray[i]->mod_vals.modv_strvals);
+           free ((char *) modArray[i]);
        }
-       ckfree ((char *) modArray);
+       free ((char *) modArray);
 
        /* FIX: memory cleanup required all over the place here */
         if (result != LDAP_SUCCESS) {
@@ -848,6 +884,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
        /* Caching control within the search: if the "cache" control array */
        /* value is set, disable/enable caching accordingly */
 
+#if 0
        if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
            if (cacheThis) {
                if (ldaptcl->timeout == 0) {
@@ -859,6 +896,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
            else
                ldap_disable_cache(ldap);
        }
+#endif
        tclResult = LDAP_PerformSearch (interp, 
                                    ldaptcl, 
                                    baseString, 
@@ -873,12 +911,14 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
                                    sortattr);
        /* Following the search, if we changed the caching behavior, change */
        /* it back. */
+#if 0
        if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
            if (cacheThis)
                ldap_disable_cache(ldap);
            else
                ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
        }
+#endif
        return tclResult;
     }
 
@@ -993,6 +1033,75 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
        return TCL_ERROR;
     }
 #endif
+    if (STREQU (subCommand, "trap")) {
+       Tcl_Obj *listObj, *resultObj;
+       int *p, l, i, code;
+
+       if (objc > 4) 
+           return TclX_WrongArgs (interp, objv [0],
+                                  "trap command ?errorCode-list?");
+       if (objc == 2) {
+           if (!ldaptcl->trapCmdObj)
+               return TCL_OK;
+           resultObj = Tcl_NewListObj(0, NULL);
+           Tcl_ListObjAppendElement(interp, resultObj, ldaptcl->trapCmdObj);
+           if (ldaptcl->traplist) {
+               listObj = Tcl_NewObj();
+               for (p = ldaptcl->traplist; *p; p++) {
+                   Tcl_ListObjAppendElement(interp, listObj, 
+                       Tcl_NewStringObj(ldaptclerrorcode[*p], -1));
+               }
+               Tcl_ListObjAppendElement(interp, resultObj, listObj);
+           }
+           Tcl_SetObjResult(interp, resultObj);
+           return TCL_OK;
+       }
+       if (ldaptcl->trapCmdObj) {
+           Tcl_DecrRefCount (ldaptcl->trapCmdObj);
+           ldaptcl->trapCmdObj = NULL;
+       }
+       if (ldaptcl->traplist) {
+           free(ldaptcl->traplist);
+           ldaptcl->traplist = NULL;
+       }
+       Tcl_GetStringFromObj(objv[2], &l);
+       if (l == 0)
+           return TCL_OK;              /* just turn off trap */
+       ldaptcl->trapCmdObj = objv[2];
+       Tcl_IncrRefCount (ldaptcl->trapCmdObj);
+       if (objc < 4)
+           return TCL_OK;              /* no code list */
+       if (Tcl_ListObjLength(interp, objv[3], &l) != TCL_OK)
+           return TCL_ERROR;
+       if (l == 0)
+           return TCL_OK;              /* empty code list */
+       ldaptcl->traplist = malloc(sizeof(int) * (l + 1));
+       ldaptcl->traplist[l] = 0;
+       for (i = 0; i < l; i++) {
+           Tcl_ListObjIndex(interp, objv[3], i, &resultObj);
+           code = LDAP_ErrorStringToCode(interp, Tcl_GetStringFromObj(resultObj));
+           if (code == -1) {
+               free(ldaptcl->traplist);
+               ldaptcl->traplist = NULL;
+               return TCL_ERROR;
+           }
+           ldaptcl->traplist[i] = code;
+       }
+       return TCL_OK;
+    }
+    if (STREQU (subCommand, "trapcodes")) {
+       int code;
+       Tcl_Obj *resultObj;
+       Tcl_Obj *stringObj;
+       resultObj = Tcl_GetObjResult(interp);
+
+       for (code = 0; code < LDAPTCL_MAXERR; code++) {
+           if (!ldaptclerrorcode[code]) continue;
+           Tcl_ListObjAppendElement(interp, resultObj,
+                       Tcl_NewStringObj(ldaptclerrorcode[code], -1));
+       }
+       return TCL_OK;
+    }
 #ifdef LDAP_DEBUG
     if (STREQU (subCommand, "debug")) {
        if (objc != 3) {
@@ -1030,8 +1139,12 @@ NeoX_LdapObjDeleteCmd(clientData)
     LDAPTCL      *ldaptcl = (LDAPTCL *)clientData;
     LDAP         *ldap = ldaptcl->ldap;
 
+    if (ldaptcl->trapCmdObj)
+       Tcl_DecrRefCount (ldaptcl->trapCmdObj);
+    if (ldaptcl->traplist)
+       free(ldaptcl->traplist);
     ldap_unbind(ldap);
-    ckfree((char*) ldaptcl);
+    free((char*) ldaptcl);
 }
 
 /*-----------------------------------------------------------------------------
@@ -1163,11 +1276,13 @@ NeoX_LdapObjCmd (clientData, interp, objc, objv)
     ldap->ld_deref = LDAP_DEREF_NEVER;  /* Turn off alias dereferencing */
 #endif
 
-    ldaptcl = (LDAPTCL *) ckalloc(sizeof(LDAPTCL));
+    ldaptcl = (LDAPTCL *) malloc(sizeof(LDAPTCL));
     ldaptcl->ldap = ldap;
     ldaptcl->caching = 0;
     ldaptcl->timeout = 0;
     ldaptcl->maxmem = 0;
+    ldaptcl->trapCmdObj = NULL;
+    ldaptcl->traplist = NULL;
     ldaptcl->flags = 0;
 
     Tcl_CreateObjCommand (interp,