From aff4f66fe6cfd84c298d74bfb95300f2ddeaac98 Mon Sep 17 00:00:00 2001 From: Randy Kunkee Date: Mon, 21 May 2001 22:01:57 +0000 Subject: [PATCH] From CHANGES notes for 2.0: - Detects OpenLDAP 2.0 and builds correctly with it. - Increment major version to 2, library file to libldaptcl2.0.so. - Can now perform add/delete/replace modifications in a single command. - Replaced calls to TclX_WrongArgs with core Tcl_WrongNumArgs to reduce dependency on Extended Tcl. - Wrap dereference search control with #ifdef LDAP_OPT_DEREF. - Deref during search should work. - Add protocol_version option to ldap init command. - Add LDAPTCL_PROTOCOL_VERSION_DEFAULT to allow specifying the default protocol version used. - Add controlArray(timeout) to control timeouts during searches. - Add controlArray(cache) to control caching current search results. (Experience has show this to be not very useful or not working correctly. Caching search results should probably be done in Ldaptcl rather than letting the LDAP API do it.) - Add "compare" subcommand - Add experimental trap subcommand (undocumented -- use at your own risk). --- contrib/ldaptcl/CHANGES | 19 +++ contrib/ldaptcl/configure | 6 +- contrib/ldaptcl/configure.in | 6 +- contrib/ldaptcl/ldap.n | 29 +++- contrib/ldaptcl/neoXldap.c | 252 ++++++++++++++++++++++++++--------- 5 files changed, 240 insertions(+), 72 deletions(-) diff --git a/contrib/ldaptcl/CHANGES b/contrib/ldaptcl/CHANGES index 2a7b8ce6ae..09e976c6be 100644 --- a/contrib/ldaptcl/CHANGES +++ b/contrib/ldaptcl/CHANGES @@ -1,3 +1,22 @@ +Package rersion 2.0: +- Detects OpenLDAP 2.0 and builds correctly with it. +- Increment major version to 2, library file to libldaptcl2.0.so. +- Can now perform add/delete/replace modifications in a single command. +- Replaced calls to TclX_WrongArgs with core Tcl_WrongNumArgs to reduce + dependency on Extended Tcl. +- Wrap dereference search control with #ifdef LDAP_OPT_DEREF. +- Deref during search should work. +- Add protocol_version option to ldap init command. +- Add LDAPTCL_PROTOCOL_VERSION_DEFAULT to allow specifying the default + protocol version used. +- Add controlArray(timeout) to control timeouts during searches. +- Add controlArray(cache) to control caching current search results. + (Experience has shown this to be not very useful or not working correctly. + Caching search results should probably be done in Ldaptcl rather than + letting the LDAP API do it.) +- Add "compare" subcommand +- Add experimental trap subcommand (undocumented -- use at your own risk). + Package version 1.2: - Filter no longer a required controlArray member, defaults to objectclass=*. diff --git a/contrib/ldaptcl/configure b/contrib/ldaptcl/configure index e32da66e6b..5779bbd245 100755 --- a/contrib/ldaptcl/configure +++ b/contrib/ldaptcl/configure @@ -539,9 +539,9 @@ fi # $OpenLDAP$ -NEO_VERSION=1.2 -NEO_MAJOR_VERSION=1 -NEO_MINOR_VERSION=2 +NEO_VERSION=2.0 +NEO_MAJOR_VERSION=2 +NEO_MINOR_VERSION=0 VERSION=${NEO_VERSION} if test "${prefix}" = "NONE"; then diff --git a/contrib/ldaptcl/configure.in b/contrib/ldaptcl/configure.in index 8e59ffd5f3..804c39087f 100644 --- a/contrib/ldaptcl/configure.in +++ b/contrib/ldaptcl/configure.in @@ -4,9 +4,9 @@ dnl to configure the system for the local environment. AC_INIT(neoXldap.c) # $OpenLDAP$ -NEO_VERSION=1.2 -NEO_MAJOR_VERSION=1 -NEO_MINOR_VERSION=2 +NEO_VERSION=2.0 +NEO_MAJOR_VERSION=2 +NEO_MINOR_VERSION=0 VERSION=${NEO_VERSION} if test "${prefix}" = "NONE"; then diff --git a/contrib/ldaptcl/ldap.n b/contrib/ldaptcl/ldap.n index 685540310f..267324f23c 100644 --- a/contrib/ldaptcl/ldap.n +++ b/contrib/ldaptcl/ldap.n @@ -13,7 +13,7 @@ ldap \- connect to and query an LDAP server .SH SYNOPSIS \fBldap \fBopen \fR \fIcommand\fR \fIhostlist\fR .br -\fBldap \fBinit \fR \fIcommand\fR \fIhostlist\fR +\fBldap \fBinit \fR \fIcommand\fR \fIhostlist\fR ?protocol_version [2|3]? .br \fBldap \fBexplode ?-nonames|-list?\fR \fIdn\fR .br @@ -61,6 +61,11 @@ and make queries to the remote LDAP server. Same as above, foo is created, but for "init", opening the connection is deferred until we actually try to do something. +The init command also allows some optional values to be set for the connection. +Currently, the only useful option is \fBprotocol_version\fR which take a +single argument to specify to use LDAP protocol 2 or 3. This may be required +when connecting to older LDAP server. + For the purposes of this example, we're going to assume that "foo" is the command created by opening a connection using "ldap open". @@ -189,6 +194,12 @@ value list {bar snap}, and you delete using the attributePairList "foo bar", If you provide an empty string ("") for the value list, the entire attribute will be deleted. +In Ldaptcl version 2.0, multiple operations may be combined into a single +transaction, ie. as in: + + foo add_attributes dn attributePairList replace attributePairList \ + delete attributePairList + .SH SEARCHING The Tcl interface to searching takes a control array, which contains @@ -200,6 +211,7 @@ of matching DNs if none are specified) and values are stored. The "code" part is executed repeatedly, once for each DN matching the search criteria. +.nf foo search controlArray destArray code Using data in the control array, a search is performed of the @@ -222,15 +234,26 @@ search criteria. controlArray(timeout) a timeout value in seconds (may contain fractional values -- extremely very small values are useful for forcing timeout conditions to test timeouts). +.fi - For each matching record, destArray is populated with none, - some or all attribute-value pairs. +For each matching record, destArray is populated with none, +some or all attribute-value pairs as determined by the request and +access control lists on the server. Note: There are some additional parameters that can be set, such as how long the synchronous version of the routines should wait before timing out, the interfaces for which are not available in the current version. +.SH COMPARE + + foo compare dn attribute value + +Interface to the ldap_compare_s() command. +Compares the value of \fIattribute\fR in the object at \fIdn\fR to the +\fIvalue\fR given in the command line. Returns an error if \fIdn\fR +does not exist. Otherwise, a + .SH CACHING (Note: Netscape clients do not have caching interfaces). The UMich and OpenLDAP client libraries offers the client application fairly diff --git a/contrib/ldaptcl/neoXldap.c b/contrib/ldaptcl/neoXldap.c index 305c290944..e347962a50 100644 --- a/contrib/ldaptcl/neoXldap.c +++ b/contrib/ldaptcl/neoXldap.c @@ -76,7 +76,9 @@ ** In OpenLDAP 2.x-devel, its 2000 + the draft number, ie 2002. ** This section is for OPENLDAP. */ +#ifndef LDAP_API_FEATURE_X_OPENLDAP #define ldap_memfree(p) free(p) +#endif #ifdef LDAP_OPT_ERROR_NUMBER #define ldap_get_lderrno(ld) (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &lderrno), lderrno) #else @@ -429,13 +431,14 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) char *m, *s, *errmsg; int errcode; int tclResult; + int lderrno; /* might be used by LDAP_ERR_STRING macro */ Tcl_Obj *resultObj = Tcl_GetObjResult (interp); - if (objc < 2) - return TclX_WrongArgs (interp, - objv [0], - "subcommand [args...]"); + if (objc < 2) { + Tcl_WrongNumArgs (interp, 1, objv, "subcommand [args...]"); + return TCL_ERROR; + } command = Tcl_GetStringFromObj (objv[0], NULL); subCommand = Tcl_GetStringFromObj (objv[1], NULL); @@ -448,8 +451,10 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) char *ldap_authString; int ldap_authInt; - if (objc != 5) - return TclX_WrongArgs (interp, objv [0], "bind authtype dn passwd"); + if (objc != 5) { + Tcl_WrongNumArgs (interp, 2, objv, "authtype dn passwd"); + return TCL_ERROR; + } ldap_authString = Tcl_GetStringFromObj (objv[2], NULL); @@ -515,16 +520,20 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) } if (STREQU (subCommand, "unbind")) { - if (objc != 2) - return TclX_WrongArgs (interp, objv [0], "unbind"); + if (objc != 2) { + Tcl_WrongNumArgs (interp, 2, objv, ""); + return TCL_ERROR; + } return Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], NULL)); } /* object delete dn */ if (STREQU (subCommand, "delete")) { - if (objc != 3) - return TclX_WrongArgs (interp, objv [0], "delete dn"); + if (objc != 3) { + Tcl_WrongNumArgs (interp, 2, objv, "dn"); + return TCL_ERROR; + } dn = Tcl_GetStringFromObj (objv [2], NULL); if ((errcode = ldap_delete_s(ldap, dn)) != LDAP_SUCCESS) { @@ -544,10 +553,10 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) char *rdn; int deleteOldRdn; - if (objc != 4) - return TclX_WrongArgs (interp, - objv [0], - "delete_rdn|modify_rdn dn rdn"); + if (objc != 4) { + Tcl_WrongNumArgs (interp, 2, objv, "dn rdn"); + return TCL_ERROR; + } dn = Tcl_GetStringFromObj (objv [2], NULL); rdn = Tcl_GetStringFromObj (objv [3], NULL); @@ -598,13 +607,15 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) Tcl_Obj **attribObjv; int valuesObjc; Tcl_Obj **valuesObjv; - int nPairs; + int nPairs, allPairs; int i; int j; + int pairIndex; + int modIndex; Tcl_Obj *resultObj = Tcl_GetObjResult (interp); - if (objc != 4) { + if (objc < 4 || objc > 4 && is_add || is_add == 0 && objc&1) { Tcl_AppendStringsToObj (resultObj, "wrong # args: ", Tcl_GetStringFromObj (objv [0], NULL), @@ -612,37 +623,52 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) subCommand, " dn attributePairList", (char *)NULL); + if (!is_add) + Tcl_AppendStringsToObj (resultObj, + " ?[add|delete|replace] attributePairList ...?", (char *)NULL); return TCL_ERROR; } dn = Tcl_GetStringFromObj (objv [2], NULL); - if (Tcl_ListObjGetElements (interp, objv [3], &attribObjc, &attribObjv) - == TCL_ERROR) { - return TCL_ERROR; + allPairs = 0; + for (i = 3; i < objc; i += 2) { + if (Tcl_ListObjLength (interp, objv[i], &j) == TCL_ERROR) + return TCL_ERROR; + if (j & 1) { + Tcl_AppendStringsToObj (resultObj, + "attribute list does not contain an ", + "even number of key-value elements", + (char *)NULL); + return TCL_ERROR; + } + allPairs += j / 2; } - if (attribObjc & 1) { - Tcl_AppendStringsToObj (resultObj, - "attribute list does not contain an ", - "even number of key-value elements", - (char *)NULL); - return TCL_ERROR; + modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (allPairs + 1)); + + pairIndex = 3; + modIndex = 0; + + do { + + if (Tcl_ListObjGetElements (interp, objv [pairIndex], &attribObjc, &attribObjv) + == TCL_ERROR) { + mod_op = -1; + goto badop; } nPairs = attribObjc / 2; - modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (nPairs + 1)); - modArray[nPairs] = (LDAPMod *) NULL; - for (i = 0; i < nPairs; i++) { - mod = modArray[i] = (LDAPMod *) malloc (sizeof(LDAPMod)); + mod = modArray[modIndex++] = (LDAPMod *) malloc (sizeof(LDAPMod)); mod->mod_op = mod_op; mod->mod_type = Tcl_GetStringFromObj (attribObjv [i * 2], NULL); if (Tcl_ListObjGetElements (interp, attribObjv [i * 2 + 1], &valuesObjc, &valuesObjv) == TCL_ERROR) { /* FIX: cleanup memory here */ - return TCL_ERROR; + mod_op = -1; + goto badop; } valPtrs = mod->mod_vals.modv_strvals = \ @@ -662,7 +688,30 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) } } - if (is_add) { + pairIndex += 2; + if (mod_op != -1 && pairIndex < objc) { + subCommand = Tcl_GetStringFromObj (objv[pairIndex - 1], NULL); + mod_op = -1; + if (STREQU (subCommand, "add")) { + mod_op = LDAP_MOD_ADD; + } else if (STREQU (subCommand, "replace")) { + mod_op = LDAP_MOD_REPLACE; + } else if (STREQU (subCommand, "delete")) { + mod_op = LDAP_MOD_DELETE; + } + if (mod_op == -1) { + Tcl_SetStringObj (resultObj, + "Additional operators must be one of" + " add, replace, or delete", -1); + mod_op = -1; + goto badop; + } + } + + } while (mod_op != -1 && pairIndex < objc); + modArray[modIndex] = (LDAPMod *) NULL; + + if (is_add) { result = ldap_add_s (ldap, dn, modArray); } else { result = ldap_modify_s (ldap, dn, modArray); @@ -671,12 +720,17 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) } /* free the modArray elements, then the modArray itself. */ - for (i = 0; i < nPairs; i++) { +badop: + for (i = 0; i < modIndex; i++) { free ((char *) modArray[i]->mod_vals.modv_strvals); free ((char *) modArray[i]); } free ((char *) modArray); + /* after modArray is allocated, mod_op = -1 upon error for cleanup */ + if (mod_op == -1) + return TCL_ERROR; + /* FIX: memory cleanup required all over the place here */ if (result != LDAP_SUCCESS) { Tcl_AppendStringsToObj (resultObj, @@ -723,10 +777,11 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) Tcl_Obj *destArrayNameObj; Tcl_Obj *evalCodeObj; - if (objc != 5) - return TclX_WrongArgs (interp, - objv [0], - "search controlArray destArray code"); + if (objc != 5) { + Tcl_WrongNumArgs (interp, 2, objv, + "controlArray destArray code"); + return TCL_ERROR; + } controlArrayNameObj = objv [2]; controlArrayName = Tcl_GetStringFromObj (controlArrayNameObj, NULL); @@ -783,13 +838,13 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) } } +#ifdef LDAP_OPT_DEREF /* Fetch dereference control setting from control array. * If it doesn't exist, default to never dereference. */ derefString = Tcl_GetVar2 (interp, controlArrayName, "deref", 0); - if (derefString == (char *)NULL) { deref = LDAP_DEREF_NEVER; } else { @@ -812,6 +867,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) return TCL_ERROR; } } +#endif /* Fetch list of attribute names from control array. * If entry doesn't exist, default to NULL (all). @@ -887,7 +943,9 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) } #endif +#ifdef LDAP_OPT_DEREF ldap_set_option(ldap, LDAP_OPT_DEREF, &deref); +#endif tclResult = LDAP_PerformSearch (interp, ldaptcl, @@ -910,8 +968,10 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) else ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem); } +#ifdef LDAP_OPT_DEREF deref = LDAP_DEREF_NEVER; ldap_set_option(ldap, LDAP_OPT_DEREF, &deref); +#endif #endif return tclResult; } @@ -924,10 +984,12 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) int result; int lderrno; - if (objc != 5) - return TclX_WrongArgs (interp, - objv [0], - "compare dn attribute value"); + if (objc != 5) { + Tcl_WrongNumArgs (interp, + 2, objv, + "dn attribute value"); + return TCL_ERROR; + } dn = Tcl_GetStringFromObj (objv[2], NULL); attr = Tcl_GetStringFromObj (objv[3], NULL); @@ -935,7 +997,7 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) result = ldap_compare_s (ldap, dn, attr, value); if (result == LDAP_COMPARE_TRUE || result == LDAP_COMPARE_FALSE) { - Tcl_SetIntObj(resultObj, result == LDAP_COMPARE_TRUE); + Tcl_SetBooleanObj(resultObj, result == LDAP_COMPARE_TRUE); return TCL_OK; } LDAP_SetErrorCode(ldaptcl, result, interp); @@ -946,25 +1008,27 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) return TCL_ERROR; } -#if defined(UMICH_LDAP) || (defined(OPEN_LDAP) && !defined(LDAP_API_VERSION)) if (STREQU (subCommand, "cache")) { +#if defined(UMICH_LDAP) || (defined(OPEN_LDAP) && !defined(LDAP_API_VERSION)) char *cacheCommand; - if (objc < 3) + if (objc < 3) { badargs: - return TclX_WrongArgs (interp, - objv [0], - "cache command [args...]"); + Tcl_WrongNumArgs (interp, 2, objv [0], "command [args...]"); + return TCL_ERROR; + } cacheCommand = Tcl_GetStringFromObj (objv [2], NULL); if (STREQU (cacheCommand, "uncache")) { char *dn; - if (objc != 4) - return TclX_WrongArgs (interp, - objv [0], - "cache uncache dn"); + if (objc != 4) { + Tcl_WrongNumArgs (interp, + 3, objv, + "dn"); + return TCL_ERROR; + } dn = Tcl_GetStringFromObj (objv [3], NULL); ldap_uncache_entry (ldap, dn); @@ -975,10 +1039,10 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) long timeout = ldaptcl->timeout; long maxmem = ldaptcl->maxmem; - if (objc > 5) - return TclX_WrongArgs (interp, - objv [0], - "cache enable ?timeout? ?maxmem?"); + if (objc > 5) { + Tcl_WrongNumArgs (interp, 3, objv, "?timeout? ?maxmem?"); + return TCL_ERROR; + } if (objc > 3) { if (Tcl_GetLongFromObj (interp, objv [3], &timeout) == TCL_ERROR) @@ -1055,15 +1119,19 @@ NeoX_LdapTargetObjCmd (clientData, interp, objc, objv) " or \"all_errors\"", (char *)NULL); return TCL_ERROR; - } +#else + return TCL_OK; #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 > 4) { + Tcl_WrongNumArgs (interp, 2, objv, + "command ?errorCode-list?"); + return TCL_ERROR; + } if (objc == 2) { if (!ldaptcl->trapCmdObj) return TCL_OK; @@ -1196,15 +1264,17 @@ NeoX_LdapObjCmd (clientData, interp, objc, objv) char *subCommand; char *newCommand; char *ldapHost; - int ldapPort = 389; + int ldapPort = LDAP_PORT; LDAP *ldap; LDAPTCL *ldaptcl; Tcl_Obj *resultObj = Tcl_GetObjResult (interp); - if (objc < 3 || objc > 5) - return TclX_WrongArgs (interp, objv [0], + if (objc < 3) { + Tcl_WrongNumArgs (interp, 1, objv, "(open|init) new_command host [port]|explode dn"); + return TCL_ERROR; + } subCommand = Tcl_GetStringFromObj (objv[1], NULL); @@ -1221,7 +1291,8 @@ NeoX_LdapObjCmd (clientData, interp, objc, objv) } else if (STREQU(param, "-list")) { list = 1; } else { - return TclX_WrongArgs (interp, objv [0], "explode ?-nonames|-list? dn"); + Tcl_WrongNumArgs (interp, 1, objv, "explode ?-nonames|-list? dn"); + return TCL_ERROR; } } if (nonames || list) @@ -1281,7 +1352,58 @@ NeoX_LdapObjCmd (clientData, interp, objc, objv) if (STREQU (subCommand, "open")) { ldap = ldap_open (ldapHost, ldapPort); } else if (STREQU (subCommand, "init")) { + int version = -1; + int i; + int value; + char *subOption; + char *subValue; + +#if LDAPTCL_PROTOCOL_VERSION_DEFAULT + version = LDAPTCL_PROTOCOL_VERSION_DEFAULT; +#endif + + for (i = 6; i < objc; i += 2) { + subOption = Tcl_GetStringFromObj(objv[i-1], NULL); + if (STREQU (subOption, "protocol_version")) { +#ifdef LDAP_OPT_PROTOCOL_VERSION + subValue = Tcl_GetStringFromObj(objv[i], NULL); + if (STREQU (subValue, "2")) { + version = LDAP_VERSION2; + } + else if (STREQU (subValue, "3")) { +#ifdef LDAP_VERSION3 + version = LDAP_VERSION3; +#else + Tcl_SetStringObj (resultObj, "protocol_version 3 not supported", -1); + return TCL_ERROR; +#endif + } + else { + Tcl_SetStringObj (resultObj, "protocol_version must be '2' or '3'", -1); + return TCL_ERROR; + } +#else + Tcl_SetStringObj (resultObj, "protocol_version not supported", -1); + return TCL_ERROR; +#endif + } else if (STREQU (subOption, "port")) { + if (Tcl_GetIntFromObj (interp, objv [i], &ldapPort) == TCL_ERROR) { + Tcl_AppendStringsToObj (resultObj, + "LDAP port number is non-numeric", + (char *)NULL); + return TCL_ERROR; + } + } else { + Tcl_SetStringObj (resultObj, "valid options: protocol_version, port", -1); + return TCL_ERROR; + } + } ldap = ldap_init (ldapHost, ldapPort); + +#if LDAP_OPT_PROTOCOL_VERSION + if (version != -1) + ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version); +#endif } else { Tcl_AppendStringsToObj (resultObj, "option was not \"open\" or \"init\""); @@ -1331,6 +1453,10 @@ Tcl_Interp *interp; NeoX_LdapObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL); + /* + if (Neo_initLDAPX(interp) != TCL_OK) + return TCL_ERROR; + */ Tcl_PkgProvide(interp, "Ldaptcl", VERSION); return TCL_OK; } -- 2.39.5