From 222962d2493c85ff4c893bc46bfca34b5630725e Mon Sep 17 00:00:00 2001 From: Randy Kunkee Date: Wed, 22 Dec 1999 18:19:11 +0000 Subject: [PATCH] Update documentation. Add support for caching, sorting, enabling "all" in searches, and experimental "trap" subcommand. --- contrib/ldaptcl/ldap.n | 58 +++++++------- contrib/ldaptcl/neoXldap.c | 151 ++++++++++++++++++++++++++++++++----- 2 files changed, 164 insertions(+), 45 deletions(-) diff --git a/contrib/ldaptcl/ldap.n b/contrib/ldaptcl/ldap.n index c7b65f1701..685540310f 100644 --- a/contrib/ldaptcl/ldap.n +++ b/contrib/ldaptcl/ldap.n @@ -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 diff --git a/contrib/ldaptcl/neoXldap.c b/contrib/ldaptcl/neoXldap.c index c172992627..5d36668895 100644 --- a/contrib/ldaptcl/neoXldap.c +++ b/contrib/ldaptcl/neoXldap.c @@ -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, -- 2.39.5