]> git.sur5r.net Git - openldap/commitdiff
From CHANGES notes for 2.0:
authorRandy Kunkee <kunkee@openldap.org>
Mon, 21 May 2001 22:01:57 +0000 (22:01 +0000)
committerRandy Kunkee <kunkee@openldap.org>
Mon, 21 May 2001 22:01:57 +0000 (22:01 +0000)
- 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
contrib/ldaptcl/configure
contrib/ldaptcl/configure.in
contrib/ldaptcl/ldap.n
contrib/ldaptcl/neoXldap.c

index 2a7b8ce6ae847cbadc2c8b44b08a3008bfd948bb..09e976c6be715c1f2d4ad890cfec40242722fe9e 100644 (file)
@@ -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=*.
index e32da66e6bff9fe1b7a859a490b2a6c943442a3f..5779bbd2450349978e7c5bcb3f54fe9905a1390e 100755 (executable)
@@ -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
index 8e59ffd5f3f4feae73e09c555fd40ace3bc72eaf..804c39087f399db11b8e01f09d46d5ef100d8e48 100644 (file)
@@ -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
index 685540310f095242d7ecfc64f769548da10c7616..267324f23c7670dfd2088679ac17e188c0bbd466 100644 (file)
@@ -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
index 305c2909442800aa9098f1be86d8982bf8765f2a..e347962a503fef021b604d58a286356f7676f466 100644 (file)
@@ -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;
 }