2 * NeoSoft Tcl client extensions to Lightweight Directory Access Protocol.
4 * Copyright (c) 1998-1999 NeoSoft, Inc.
7 * This software may be used, modified, copied, distributed, and sold,
8 * in both source and binary form provided that these copyrights are
9 * retained and their terms are followed.
11 * Under no circumstances are the authors or NeoSoft Inc. responsible
12 * for the proper functioning of this software, nor do the authors
13 * assume any liability for damages incurred with its use.
15 * Redistribution and use in source and binary forms are permitted
16 * provided that this notice is preserved and that due credit is given
19 * NeoSoft, Inc. may not be used to endorse or promote products derived
20 * from this software without specific prior written permission. This
21 * software is provided ``as is'' without express or implied warranty.
23 * Requests for permission may be sent to NeoSoft Inc, 1770 St. James Place,
24 * Suite 500, Houston, TX, 77056.
26 * $Id: neoXldap.c,v 1.5 1999/08/03 05:23:03 kunkee Exp $
31 * This code was originally developed by Karl Lehenbauer to work with
32 * Umich-3.3 LDAP. It was debugged against the Netscape LDAP server
33 * and their much more reliable SDK, and again backported to the
34 * Umich-3.3 client code. The UMICH_LDAP define is used to include
35 * code that will work with the Umich-3.3 LDAP, but not with Netscape's
36 * SDK. OpenLDAP may support some of these, but they have not been tested.
37 * Currently supported by Randy Kunkee (kunkee@OpenLDAP.org).
41 * Add timeout to controlArray to set timeout for ldap_result.
45 #include "tclExtend.h"
54 * Macros to do string compares. They pre-check the first character before
55 * checking of the strings are equal.
58 #define STREQU(str1, str2) \
59 (((str1) [0] == (str2) [0]) && (strcmp (str1, str2) == 0))
60 #define STRNEQU(str1, str2, n) \
61 (((str1) [0] == (str2) [0]) && (strncmp (str1, str2, n) == 0))
64 * The following section defines some common macros used by the rest
65 * of the code. It's ugly, and can use some work. This code was
66 * originally developed to work with Umich-3.3 LDAP. It was debugged
67 * against the Netscape LDAP server and the much more reliable SDK,
68 * and then again backported to the Umich-3.3 client code.
71 #if defined(OPEN_LDAP)
72 /* LDAP_API_VERSION must be defined per the current draft spec
73 ** it's value will be assigned RFC number. However, as
74 ** no RFC is defined, it's value is currently implementation
75 ** specific (though I would hope it's value is greater than 1823).
76 ** In OpenLDAP 2.x-devel, its 2000 + the draft number, ie 2002.
77 ** This section is for OPENLDAP.
79 #define ldap_memfree(p) free(p)
80 #ifdef LDAP_OPT_ERROR_NUMBER
81 #define ldap_get_lderrno(ld) (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &lderrno), lderrno)
83 #define ldap_get_lderrno(ld) (ld->ld_errno)
85 #define LDAP_ERR_STRING(ld) \
86 ldap_err2string(ldap_get_lderrno(ld))
87 #elif defined( LDAP_OPT_SIZELIMIT )
89 ** Netscape SDK w/ ldap_set_option, ldap_get_option
91 #define LDAP_ERR_STRING(ld) \
92 ldap_err2string(ldap_get_lderrno(ldap))
94 /* U-Mich/OpenLDAP 1.x API */
95 /* RFC-1823 w/ changes */
97 #define ldap_memfree(p) free(p)
98 #define ldap_ber_free(p, n) ber_free(p, n)
99 #define ldap_value_free_len(bvals) ber_bvecfree(bvals)
100 #define ldap_get_lderrno(ld) (ld->ld_errno)
101 #define LDAP_ERR_STRING(ld) \
102 ldap_err2string(ld->ld_errno)
105 typedef struct ldaptclobj {
107 int caching; /* flag 1/0 if caching is enabled */
108 long timeout; /* timeout from last cache enable */
109 long maxmem; /* maxmem from last cache enable */
110 Tcl_Obj *trapCmdObj; /* error handler */
111 int *traplist; /* list of errorCodes to trap */
116 #define LDAPTCL_INTERRCODES 0x001
118 #include "ldaptclerr.h"
121 LDAP_SetErrorCode(LDAPTCL *ldaptcl, int code, Tcl_Interp *interp)
128 code = ldap_get_lderrno(ldaptcl->ldap);
129 if ((ldaptcl->flags & LDAPTCL_INTERRCODES) || code > LDAPTCL_MAXERR ||
130 ldaptclerrorcode[code] == NULL) {
131 sprintf(shortbuf, "0x%03x", code);
134 errp = ldaptclerrorcode[code];
136 Tcl_SetErrorCode(interp, errp, NULL);
137 if (ldaptcl->trapCmdObj) {
140 if (ldaptcl->traplist != NULL) {
141 for (i = ldaptcl->traplist; *i && *i != code; i++)
145 (void) Tcl_EvalObj(interp, ldaptcl->trapCmdObj);
150 LDAP_ErrorStringToCode(Tcl_Interp *interp, char *s)
155 offset = (strncasecmp(s, "LDAP_", 5) == 0) ? 0 : 5;
156 for (code = 0; code < LDAPTCL_MAXERR; code++) {
157 if (!ldaptclerrorcode[code]) continue;
158 if (strcasecmp(s, ldaptclerrorcode[code]+offset) == 0)
161 Tcl_ResetResult(interp);
162 Tcl_AppendResult(interp, s, " is an invalid code", (char *) NULL);
166 /*-----------------------------------------------------------------------------
167 * LDAP_ProcessOneSearchResult --
169 * Process one result return from an LDAP search.
172 * o interp - Tcl interpreter; Errors are returned in result.
173 * o ldap - LDAP structure pointer.
174 * o entry - LDAP message pointer.
175 * o destArrayNameObj - Name of Tcl array in which to store attributes.
176 * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
178 * o TCL_OK if processing succeeded..
179 * o TCL_ERROR if an error occured, with error message in interp.
180 *-----------------------------------------------------------------------------
183 LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
187 Tcl_Obj *destArrayNameObj;
188 Tcl_Obj *evalCodeObj;
191 Tcl_Obj *attributeNameObj;
192 Tcl_Obj *attributeDataObj;
195 struct berval **bvals;
199 Tcl_UnsetVar (interp, Tcl_GetStringFromObj (destArrayNameObj, NULL), 0);
201 dn = ldap_get_dn(ldap, entry);
203 if (Tcl_SetVar2(interp, /* set dn */
204 Tcl_GetStringFromObj(destArrayNameObj, NULL),
207 TCL_LEAVE_ERR_MSG) == NULL)
211 attributeNameObj = Tcl_NewObj();
212 Tcl_IncrRefCount (attributeNameObj);
213 for (attributeName = ldap_first_attribute (ldap, entry, &ber);
214 attributeName != NULL;
215 attributeName = ldap_next_attribute(ldap, entry, ber)) {
217 bvals = ldap_get_values_len(ldap, entry, attributeName);
220 /* Note here that the U.of.M. ldap will return a null bvals
221 when the last attribute value has been deleted, but still
222 retains the attributeName. Even though this is documented
223 as an error, we ignore it to present a consistent interface
224 with Netscape's server
226 attributeDataObj = Tcl_NewObj();
227 Tcl_SetStringObj(attributeNameObj, attributeName, -1);
228 for (i = 0; bvals[i] != NULL; i++) {
229 Tcl_Obj *singleAttributeValueObj;
231 singleAttributeValueObj = Tcl_NewStringObj(bvals[i]->bv_val, bvals[i]->bv_len);
232 if (Tcl_ListObjAppendElement (interp,
234 singleAttributeValueObj)
241 ldap_value_free_len(bvals);
243 if (Tcl_ObjSetVar2 (interp,
247 TCL_LEAVE_ERR_MSG) == NULL) {
252 Tcl_DecrRefCount (attributeNameObj);
253 return Tcl_EvalObj (interp, evalCodeObj);
256 /*-----------------------------------------------------------------------------
257 * LDAP_PerformSearch --
259 * Perform an LDAP search.
262 * o interp - Tcl interpreter; Errors are returned in result.
263 * o ldap - LDAP structure pointer.
264 * o base - Base DN from which to perform search.
265 * o scope - LDAP search scope, must be one of LDAP_SCOPE_BASE,
266 * LDAP_SCOPE_ONELEVEL, or LDAP_SCOPE_SUBTREE.
267 * o attrs - Pointer to array of char * pointers of desired
268 * attribute names, or NULL for all attributes.
269 * o filtpatt LDAP filter pattern.
270 * o value Value to get sprintf'ed into filter pattern.
271 * o destArrayNameObj - Name of Tcl array in which to store attributes.
272 * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
274 * o TCL_OK if processing succeeded..
275 * o TCL_ERROR if an error occured, with error message in interp.
276 *-----------------------------------------------------------------------------
279 LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
280 destArrayNameObj, evalCodeObj, timeout_p, all, sortattr)
288 Tcl_Obj *destArrayNameObj;
289 Tcl_Obj *evalCodeObj;
290 struct timeval *timeout_p;
294 LDAP *ldap = ldaptcl->ldap;
299 int tclResult = TCL_OK;
301 LDAPMessage *resultMessage = 0;
302 LDAPMessage *entryMessage = 0;
308 resultObj = Tcl_GetObjResult (interp);
310 sprintf(filter, filtpatt, value);
313 if ((msgid = ldap_search (ldap, base, scope, filter, attrs, 0)) == -1) {
314 Tcl_AppendStringsToObj (resultObj,
315 "LDAP start search error: ",
316 LDAP_ERR_STRING(ldap),
318 LDAP_SetErrorCode(ldaptcl, -1, interp);
327 resultCode = ldap_result (ldap, msgid, all, timeout_p, &resultMessage);
328 if (resultCode != LDAP_RES_SEARCH_RESULT &&
329 resultCode != LDAP_RES_SEARCH_ENTRY)
333 sortKey = (strcasecmp(sortattr, "dn") == 0) ? NULL : sortattr;
334 ldap_sort_entries(ldap, &resultMessage, sortKey, strcasecmp);
336 entryMessage = ldap_first_entry(ldap, resultMessage);
338 while (entryMessage) {
339 tclResult = LDAP_ProcessOneSearchResult (interp,
344 if (tclResult != TCL_OK) {
345 if (tclResult == TCL_CONTINUE) {
347 } else if (tclResult == TCL_BREAK) {
351 } else if (tclResult == TCL_ERROR) {
353 sprintf(msg, "\n (\"search\" body line %d)",
355 Tcl_AddObjErrorInfo(interp, msg, -1);
363 entryMessage = ldap_next_entry(ldap, entryMessage);
365 if (resultCode == LDAP_RES_SEARCH_RESULT || all)
368 ldap_msgfree(resultMessage);
369 resultMessage = NULL;
373 ldap_msgfree(resultMessage);
374 if (resultCode == LDAP_RES_SEARCH_ENTRY)
375 ldap_abandon(ldap, msgid);
378 if (resultCode == -1) {
379 Tcl_AppendStringsToObj (resultObj,
380 "LDAP result search error: ",
381 LDAP_ERR_STRING(ldap),
383 LDAP_SetErrorCode(ldaptcl, -1, interp);
386 if (resultCode == 0) {
387 Tcl_SetErrorCode (interp, "TIMEOUT", (char*) NULL);
388 Tcl_SetStringObj (resultObj, "LDAP timeout retrieving results", -1);
392 if (resultCode == LDAP_RES_SEARCH_RESULT ||
393 (all && resultCode == LDAP_RES_SEARCH_ENTRY))
397 if ((errorCode = ldap_result2error (ldap, resultMessage, 0))
399 Tcl_AppendStringsToObj (resultObj,
400 "LDAP search error: ",
401 ldap_err2string(errorCode),
404 ldap_msgfree(resultMessage);
405 LDAP_SetErrorCode(ldaptcl, errorCode, interp);
409 ldap_msgfree(resultMessage);
413 /*-----------------------------------------------------------------------------
414 * NeoX_LdapTargetObjCmd --
416 * Implements the body of commands created by Neo_LdapObjCmd.
419 * A standard Tcl result.
422 * See the user documentation.
423 *-----------------------------------------------------------------------------
426 NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
427 ClientData clientData;
430 Tcl_Obj *CONST objv[];
434 LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
435 LDAP *ldap = ldaptcl->ldap;
438 int is_add_or_modify = 0;
440 char *m, *s, *errmsg;
444 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
447 return TclX_WrongArgs (interp,
449 "subcommand [args...]");
451 command = Tcl_GetStringFromObj (objv[0], NULL);
452 subCommand = Tcl_GetStringFromObj (objv[1], NULL);
454 /* object bind authtype name password */
455 if (STREQU (subCommand, "bind")) {
459 char *ldap_authString;
463 return TclX_WrongArgs (interp, objv [0], "bind authtype dn passwd");
465 ldap_authString = Tcl_GetStringFromObj (objv[2], NULL);
467 if (STREQU (ldap_authString, "simple")) {
468 ldap_authInt = LDAP_AUTH_SIMPLE;
471 else if (STREQU (ldap_authString, "kerberos_ldap")) {
472 ldap_authInt = LDAP_AUTH_KRBV41;
473 } else if (STREQU (ldap_authString, "kerberos_dsa")) {
474 ldap_authInt = LDAP_AUTH_KRBV42;
475 } else if (STREQU (ldap_authString, "kerberos_both")) {
476 ldap_authInt = LDAP_AUTH_KRBV4;
480 Tcl_AppendStringsToObj (resultObj,
486 "\" authtype must be one of \"simple\", ",
487 "\"kerberos_ldap\", \"kerberos_dsa\" ",
488 "or \"kerberos_both\"",
490 "\" authtype must be \"simple\", ",
496 binddn = Tcl_GetStringFromObj (objv[3], &stringLength);
497 if (stringLength == 0)
500 passwd = Tcl_GetStringFromObj (objv[4], &stringLength);
501 if (stringLength == 0)
504 /* ldap_bind_s(ldap, dn, pw, method) */
507 #define LDAP_BIND(ldap, dn, pw, method) \
508 ldap_bind_s(ldap, dn, pw, method)
510 #define LDAP_BIND(ldap, dn, pw, method) \
511 ldap_simple_bind_s(ldap, dn, pw)
513 if ((errcode = LDAP_BIND (ldap,
516 ldap_authInt)) != LDAP_SUCCESS) {
518 Tcl_AppendStringsToObj (resultObj,
520 ldap_err2string(errcode),
522 LDAP_SetErrorCode(ldaptcl, errcode, interp);
528 if (STREQU (subCommand, "unbind")) {
530 return TclX_WrongArgs (interp, objv [0], "unbind");
532 return Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], NULL));
535 /* object delete dn */
536 if (STREQU (subCommand, "delete")) {
538 return TclX_WrongArgs (interp, objv [0], "delete dn");
540 dn = Tcl_GetStringFromObj (objv [2], NULL);
541 if ((errcode = ldap_delete_s(ldap, dn)) != LDAP_SUCCESS) {
542 Tcl_AppendStringsToObj (resultObj,
543 "LDAP delete error: ",
544 ldap_err2string(errcode),
546 LDAP_SetErrorCode(ldaptcl, errcode, interp);
552 /* object rename_rdn dn rdn */
553 /* object modify_rdn dn rdn */
554 if (STREQU (subCommand, "rename_rdn") || STREQU (subCommand, "modify_rdn")) {
559 return TclX_WrongArgs (interp,
561 "delete_rdn|modify_rdn dn rdn");
563 dn = Tcl_GetStringFromObj (objv [2], NULL);
564 rdn = Tcl_GetStringFromObj (objv [3], NULL);
566 deleteOldRdn = (*subCommand == 'r');
568 if ((errcode = ldap_modrdn2_s (ldap, dn, rdn, deleteOldRdn)) != LDAP_SUCCESS) {
569 Tcl_AppendStringsToObj (resultObj,
573 ldap_err2string(errcode),
575 LDAP_SetErrorCode(ldaptcl, errcode, interp);
581 /* object add dn attributePairList */
582 /* object add_attributes dn attributePairList */
583 /* object replace_attributes dn attributePairList */
584 /* object delete_attributes dn attributePairList */
586 if (STREQU (subCommand, "add")) {
588 is_add_or_modify = 1;
591 if (STREQU (subCommand, "add_attributes")) {
592 is_add_or_modify = 1;
593 mod_op = LDAP_MOD_ADD;
594 } else if (STREQU (subCommand, "replace_attributes")) {
595 is_add_or_modify = 1;
596 mod_op = LDAP_MOD_REPLACE;
597 } else if (STREQU (subCommand, "delete_attributes")) {
598 is_add_or_modify = 1;
599 mod_op = LDAP_MOD_DELETE;
603 if (is_add_or_modify) {
607 char **valPtrs = NULL;
609 Tcl_Obj **attribObjv;
611 Tcl_Obj **valuesObjv;
616 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
619 Tcl_AppendStringsToObj (resultObj,
621 Tcl_GetStringFromObj (objv [0], NULL),
624 " dn attributePairList",
629 dn = Tcl_GetStringFromObj (objv [2], NULL);
631 if (Tcl_ListObjGetElements (interp, objv [3], &attribObjc, &attribObjv)
636 if (attribObjc & 1) {
637 Tcl_AppendStringsToObj (resultObj,
638 "attribute list does not contain an ",
639 "even number of key-value elements",
644 nPairs = attribObjc / 2;
646 modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (nPairs + 1));
647 modArray[nPairs] = (LDAPMod *) NULL;
649 for (i = 0; i < nPairs; i++) {
650 mod = modArray[i] = (LDAPMod *) malloc (sizeof(LDAPMod));
651 mod->mod_op = mod_op;
652 mod->mod_type = Tcl_GetStringFromObj (attribObjv [i * 2], NULL);
654 if (Tcl_ListObjGetElements (interp, attribObjv [i * 2 + 1], &valuesObjc, &valuesObjv) == TCL_ERROR) {
655 /* FIX: cleanup memory here */
659 valPtrs = mod->mod_vals.modv_strvals = \
660 (char **)malloc (sizeof (char *) * (valuesObjc + 1));
661 valPtrs[valuesObjc] = (char *)NULL;
663 for (j = 0; j < valuesObjc; j++) {
664 valPtrs [j] = Tcl_GetStringFromObj (valuesObjv[j], NULL);
666 /* If it's "delete" and value is an empty string, make
667 * value be NULL to indicate entire attribute is to be
669 if ((*valPtrs [j] == '\0')
670 && (mod->mod_op == LDAP_MOD_DELETE || mod->mod_op == LDAP_MOD_REPLACE)) {
677 result = ldap_add_s (ldap, dn, modArray);
679 result = ldap_modify_s (ldap, dn, modArray);
680 if (ldaptcl->caching)
681 ldap_uncache_entry (ldap, dn);
684 /* free the modArray elements, then the modArray itself. */
685 for (i = 0; i < nPairs; i++) {
686 free ((char *) modArray[i]->mod_vals.modv_strvals);
687 free ((char *) modArray[i]);
689 free ((char *) modArray);
691 /* FIX: memory cleanup required all over the place here */
692 if (result != LDAP_SUCCESS) {
693 Tcl_AppendStringsToObj (resultObj,
697 ldap_err2string(result),
699 LDAP_SetErrorCode(ldaptcl, result, interp);
705 /* object search controlArray dn pattern */
706 if (STREQU (subCommand, "search")) {
707 char *controlArrayName;
708 Tcl_Obj *controlArrayNameObj;
718 char **attributesArray;
719 char *attributesString;
722 char *filterPatternString;
726 struct timeval timeout, *timeout_p;
734 Tcl_Obj *destArrayNameObj;
735 Tcl_Obj *evalCodeObj;
738 return TclX_WrongArgs (interp,
740 "search controlArray destArray code");
742 controlArrayNameObj = objv [2];
743 controlArrayName = Tcl_GetStringFromObj (controlArrayNameObj, NULL);
745 destArrayNameObj = objv [3];
747 evalCodeObj = objv [4];
749 baseString = Tcl_GetVar2 (interp,
754 if (baseString == (char *)NULL) {
755 Tcl_AppendStringsToObj (resultObj,
756 "required element \"base\" ",
757 "is missing from ldap control array \"",
764 filterPatternString = Tcl_GetVar2 (interp,
768 if (filterPatternString == (char *)NULL) {
769 filterPatternString = "objectclass=*";
772 /* Fetch scope setting from control array.
773 * If it doesn't exist, default to subtree scoping.
775 scopeString = Tcl_GetVar2 (interp, controlArrayName, "scope", 0);
776 if (scopeString == NULL) {
777 scope = LDAP_SCOPE_SUBTREE;
779 if (STREQU(scopeString, "base"))
780 scope = LDAP_SCOPE_BASE;
781 else if (STRNEQU(scopeString, "one", 3))
782 scope = LDAP_SCOPE_ONELEVEL;
783 else if (STRNEQU(scopeString, "sub", 3))
784 scope = LDAP_SCOPE_SUBTREE;
786 Tcl_AppendStringsToObj (resultObj,
787 "\"scope\" element of \"",
789 "\" array is not one of ",
790 "\"base\", \"onelevel\", ",
797 /* Fetch dereference control setting from control array.
798 * If it doesn't exist, default to never dereference. */
799 derefString = Tcl_GetVar2 (interp,
804 if (derefString == (char *)NULL) {
805 deref = LDAP_DEREF_NEVER;
807 if (STREQU(derefString, "never"))
808 deref = LDAP_DEREF_NEVER;
809 else if (STREQU(derefString, "search"))
810 deref = LDAP_DEREF_SEARCHING;
811 else if (STREQU(derefString, "find") == 0)
812 deref = LDAP_DEREF_FINDING;
813 else if (STREQU(derefString, "always"))
814 deref = LDAP_DEREF_ALWAYS;
816 Tcl_AppendStringsToObj (resultObj,
817 "\"deref\" element of \"",
819 "\" array is not one of ",
820 "\"never\", \"search\", \"find\", ",
827 /* Fetch list of attribute names from control array.
828 * If entry doesn't exist, default to NULL (all).
830 attributesString = Tcl_GetVar2 (interp,
834 if (attributesString == (char *)NULL) {
835 attributesArray = NULL;
837 if ((Tcl_SplitList (interp,
840 &attributesArray)) != TCL_OK) {
845 /* Fetch timeout value if there is one
847 timeoutString = Tcl_GetVar2 (interp,
852 if (timeoutString == (char *)NULL) {
856 if (Tcl_GetDouble(interp, timeoutString, &timeoutTime) != TCL_OK)
858 timeout.tv_sec = floor(timeoutTime);
859 timeout.tv_usec = (timeoutTime-timeout.tv_sec) * 1000000;
860 timeout_p = &timeout;
863 paramString = Tcl_GetVar2 (interp, controlArrayName, "cache", 0);
865 if (Tcl_GetInt(interp, paramString, &cacheThis) == TCL_ERROR)
869 paramString = Tcl_GetVar2 (interp, controlArrayName, "all", 0);
871 if (Tcl_GetInt(interp, paramString, &all) == TCL_ERROR)
875 sortattr = Tcl_GetVar2 (interp, controlArrayName, "sort", 0);
878 ldap->ld_deref = deref;
879 ldap->ld_timelimit = 0;
880 ldap->ld_sizelimit = 0;
881 ldap->ld_options = 0;
884 /* Caching control within the search: if the "cache" control array */
885 /* value is set, disable/enable caching accordingly */
888 if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
890 if (ldaptcl->timeout == 0) {
891 Tcl_SetStringObj(resultObj, "Caching never before enabled, I have no timeout value to use", -1);
894 ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
897 ldap_disable_cache(ldap);
900 tclResult = LDAP_PerformSearch (interp,
912 /* Following the search, if we changed the caching behavior, change */
915 if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
917 ldap_disable_cache(ldap);
919 ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
925 #if defined(UMICH_LDAP) || (defined(OPEN_LDAP) && !defined(LDAP_API_VERSION))
926 if (STREQU (subCommand, "cache")) {
931 return TclX_WrongArgs (interp,
933 "cache command [args...]");
935 cacheCommand = Tcl_GetStringFromObj (objv [2], NULL);
937 if (STREQU (cacheCommand, "uncache")) {
941 return TclX_WrongArgs (interp,
945 dn = Tcl_GetStringFromObj (objv [3], NULL);
946 ldap_uncache_entry (ldap, dn);
950 if (STREQU (cacheCommand, "enable")) {
951 long timeout = ldaptcl->timeout;
952 long maxmem = ldaptcl->maxmem;
955 return TclX_WrongArgs (interp,
957 "cache enable ?timeout? ?maxmem?");
960 if (Tcl_GetLongFromObj (interp, objv [3], &timeout) == TCL_ERROR)
964 Tcl_SetStringObj(resultObj,
965 objc > 3 ? "timeouts must be greater than 0" :
966 "no previous timeout to reference", -1);
971 if (Tcl_GetLongFromObj (interp, objv [4], &maxmem) == TCL_ERROR)
974 if (ldap_enable_cache (ldap, timeout, maxmem) == -1) {
975 Tcl_AppendStringsToObj (resultObj,
976 "LDAP cache enable error: ",
977 LDAP_ERR_STRING(ldap),
979 LDAP_SetErrorCode(ldaptcl, -1, interp);
982 ldaptcl->caching = 1;
983 ldaptcl->timeout = timeout;
984 ldaptcl->maxmem = maxmem;
988 if (objc != 3) goto badargs;
990 if (STREQU (cacheCommand, "disable")) {
991 ldap_disable_cache (ldap);
992 ldaptcl->caching = 0;
996 if (STREQU (cacheCommand, "destroy")) {
997 ldap_destroy_cache (ldap);
998 ldaptcl->caching = 0;
1002 if (STREQU (cacheCommand, "flush")) {
1003 ldap_flush_cache (ldap);
1007 if (STREQU (cacheCommand, "no_errors")) {
1008 ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHENOERRS);
1012 if (STREQU (cacheCommand, "all_errors")) {
1013 ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHEALLERRS);
1017 if (STREQU (cacheCommand, "size_errors")) {
1018 ldap_set_cache_options (ldap, 0);
1021 Tcl_AppendStringsToObj (resultObj,
1027 " must be one of \"enable\", ",
1029 "\"destroy\", \"flush\", \"uncache\", ",
1030 "\"no_errors\", \"size_errors\",",
1031 " or \"all_errors\"",
1036 if (STREQU (subCommand, "trap")) {
1037 Tcl_Obj *listObj, *resultObj;
1041 return TclX_WrongArgs (interp, objv [0],
1042 "trap command ?errorCode-list?");
1044 if (!ldaptcl->trapCmdObj)
1046 resultObj = Tcl_NewListObj(0, NULL);
1047 Tcl_ListObjAppendElement(interp, resultObj, ldaptcl->trapCmdObj);
1048 if (ldaptcl->traplist) {
1049 listObj = Tcl_NewObj();
1050 for (p = ldaptcl->traplist; *p; p++) {
1051 Tcl_ListObjAppendElement(interp, listObj,
1052 Tcl_NewStringObj(ldaptclerrorcode[*p], -1));
1054 Tcl_ListObjAppendElement(interp, resultObj, listObj);
1056 Tcl_SetObjResult(interp, resultObj);
1059 if (ldaptcl->trapCmdObj) {
1060 Tcl_DecrRefCount (ldaptcl->trapCmdObj);
1061 ldaptcl->trapCmdObj = NULL;
1063 if (ldaptcl->traplist) {
1064 free(ldaptcl->traplist);
1065 ldaptcl->traplist = NULL;
1067 Tcl_GetStringFromObj(objv[2], &l);
1069 return TCL_OK; /* just turn off trap */
1070 ldaptcl->trapCmdObj = objv[2];
1071 Tcl_IncrRefCount (ldaptcl->trapCmdObj);
1073 return TCL_OK; /* no code list */
1074 if (Tcl_ListObjLength(interp, objv[3], &l) != TCL_OK)
1077 return TCL_OK; /* empty code list */
1078 ldaptcl->traplist = (int*)malloc(sizeof(int) * (l + 1));
1079 ldaptcl->traplist[l] = 0;
1080 for (i = 0; i < l; i++) {
1081 Tcl_ListObjIndex(interp, objv[3], i, &resultObj);
1082 code = LDAP_ErrorStringToCode(interp, Tcl_GetStringFromObj(resultObj, NULL));
1084 free(ldaptcl->traplist);
1085 ldaptcl->traplist = NULL;
1088 ldaptcl->traplist[i] = code;
1092 if (STREQU (subCommand, "trapcodes")) {
1096 resultObj = Tcl_GetObjResult(interp);
1098 for (code = 0; code < LDAPTCL_MAXERR; code++) {
1099 if (!ldaptclerrorcode[code]) continue;
1100 Tcl_ListObjAppendElement(interp, resultObj,
1101 Tcl_NewStringObj(ldaptclerrorcode[code], -1));
1106 if (STREQU (subCommand, "debug")) {
1108 Tcl_AppendStringsToObj(resultObj, "Wrong # of arguments",
1112 return Tcl_GetIntFromObj(interp, objv[2], &ldap_debug);
1116 /* FIX: this needs to enumerate all the possibilities */
1117 Tcl_AppendStringsToObj (resultObj,
1120 "\" must be one of \"add\", ",
1121 "\"add_attributes\", ",
1122 "\"bind\", \"cache\", \"delete\", ",
1123 "\"delete_attributes\", \"modify\", ",
1124 "\"modify_rdn\", \"rename_rdn\", ",
1125 "\"replace_attributes\", ",
1126 "\"search\" or \"unbind\".",
1132 * Delete and LDAP command object
1136 NeoX_LdapObjDeleteCmd(clientData)
1137 ClientData clientData;
1139 LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
1140 LDAP *ldap = ldaptcl->ldap;
1142 if (ldaptcl->trapCmdObj)
1143 Tcl_DecrRefCount (ldaptcl->trapCmdObj);
1144 if (ldaptcl->traplist)
1145 free(ldaptcl->traplist);
1147 free((char*) ldaptcl);
1150 /*-----------------------------------------------------------------------------
1151 * NeoX_LdapObjCmd --
1153 * Implements the `ldap' command:
1154 * ldap open newObjName host [port]
1155 * ldap init newObjName host [port]
1158 * A standard Tcl result.
1161 * See the user documentation.
1162 *-----------------------------------------------------------------------------
1165 NeoX_LdapObjCmd (clientData, interp, objc, objv)
1166 ClientData clientData;
1169 Tcl_Obj *CONST objv[];
1179 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
1181 if (objc < 3 || objc > 5)
1182 return TclX_WrongArgs (interp, objv [0],
1183 "(open|init) new_command host [port]|explode dn");
1185 subCommand = Tcl_GetStringFromObj (objv[1], NULL);
1187 if (STREQU(subCommand, "explode")) {
1191 char **exploded, **p;
1193 param = Tcl_GetStringFromObj (objv[2], NULL);
1194 if (param[0] == '-') {
1195 if (STREQU(param, "-nonames")) {
1197 } else if (STREQU(param, "-list")) {
1200 return TclX_WrongArgs (interp, objv [0], "explode ?-nonames|-list? dn");
1203 if (nonames || list)
1204 param = Tcl_GetStringFromObj (objv[3], NULL);
1205 exploded = ldap_explode_dn(param, nonames);
1206 for (p = exploded; *p; p++) {
1208 char *q = strchr(*p, '=');
1210 Tcl_SetObjLength(resultObj, 0);
1211 Tcl_AppendStringsToObj(resultObj, "rdn ", *p,
1212 " missing '='", NULL);
1213 ldap_value_free(exploded);
1217 if (Tcl_ListObjAppendElement(interp, resultObj,
1218 Tcl_NewStringObj(*p, -1)) != TCL_OK ||
1219 Tcl_ListObjAppendElement(interp, resultObj,
1220 Tcl_NewStringObj(q+1, -1)) != TCL_OK) {
1221 ldap_value_free(exploded);
1225 if (Tcl_ListObjAppendElement(interp, resultObj,
1226 Tcl_NewStringObj(*p, -1))) {
1227 ldap_value_free(exploded);
1232 ldap_value_free(exploded);
1237 if (STREQU(subCommand, "friendly")) {
1238 char *friendly = ldap_dn2ufn(Tcl_GetStringFromObj(objv[2], NULL));
1239 Tcl_SetStringObj(resultObj, friendly, -1);
1245 newCommand = Tcl_GetStringFromObj (objv[2], NULL);
1246 ldapHost = Tcl_GetStringFromObj (objv[3], NULL);
1249 if (Tcl_GetIntFromObj (interp, objv [4], &ldapPort) == TCL_ERROR) {
1250 Tcl_AppendStringsToObj (resultObj,
1251 "LDAP port number is non-numeric",
1257 if (STREQU (subCommand, "open")) {
1258 ldap = ldap_open (ldapHost, ldapPort);
1259 } else if (STREQU (subCommand, "init")) {
1260 ldap = ldap_init (ldapHost, ldapPort);
1262 Tcl_AppendStringsToObj (resultObj,
1263 "option was not \"open\" or \"init\"");
1267 if (ldap == (LDAP *)NULL) {
1268 Tcl_SetErrno(errno);
1269 Tcl_AppendStringsToObj (resultObj,
1270 Tcl_PosixError (interp),
1276 ldap->ld_deref = LDAP_DEREF_NEVER; /* Turn off alias dereferencing */
1279 ldaptcl = (LDAPTCL *) malloc(sizeof(LDAPTCL));
1280 ldaptcl->ldap = ldap;
1281 ldaptcl->caching = 0;
1282 ldaptcl->timeout = 0;
1283 ldaptcl->maxmem = 0;
1284 ldaptcl->trapCmdObj = NULL;
1285 ldaptcl->traplist = NULL;
1288 Tcl_CreateObjCommand (interp,
1290 NeoX_LdapTargetObjCmd,
1291 (ClientData) ldaptcl,
1292 NeoX_LdapObjDeleteCmd);
1296 /*-----------------------------------------------------------------------------
1298 * Initialize the LDAP interface.
1299 *-----------------------------------------------------------------------------
1302 Ldaptcl_Init (interp)
1305 Tcl_CreateObjCommand (interp,
1309 (Tcl_CmdDeleteProc*) NULL);
1310 Tcl_PkgProvide(interp, "Ldaptcl", VERSION);