Commit aff4f66f authored by Randy Kunkee's avatar Randy Kunkee
Browse files

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).
parent f31cc3e7
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=*.
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment