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.
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 #ifndef LDAP_API_FEATURE_X_OPENLDAP
80 #define ldap_memfree(p) free(p)
82 #ifdef LDAP_OPT_ERROR_NUMBER
83 #define ldap_get_lderrno(ld) (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &lderrno), lderrno)
85 #define ldap_get_lderrno(ld) (ld->ld_errno)
87 #define LDAP_ERR_STRING(ld) \
88 ldap_err2string(ldap_get_lderrno(ld))
89 #elif defined( LDAP_OPT_SIZELIMIT )
91 ** Netscape SDK w/ ldap_set_option, ldap_get_option
93 #define LDAP_ERR_STRING(ld) \
94 ldap_err2string(ldap_get_lderrno(ldap))
96 /* U-Mich/OpenLDAP 1.x API */
97 /* RFC-1823 w/ changes */
99 #define ldap_memfree(p) free(p)
100 #define ldap_ber_free(p, n) ber_free(p, n)
101 #define ldap_value_free_len(bvals) ber_bvecfree(bvals)
102 #define ldap_get_lderrno(ld) (ld->ld_errno)
103 #define LDAP_ERR_STRING(ld) \
104 ldap_err2string(ld->ld_errno)
107 typedef struct ldaptclobj {
109 int caching; /* flag 1/0 if caching is enabled */
110 long timeout; /* timeout from last cache enable */
111 long maxmem; /* maxmem from last cache enable */
112 Tcl_Obj *trapCmdObj; /* error handler */
113 int *traplist; /* list of errorCodes to trap */
118 #define LDAPTCL_INTERRCODES 0x001
120 #include "ldaptclerr.h"
123 LDAP_SetErrorCode(LDAPTCL *ldaptcl, int code, Tcl_Interp *interp)
130 code = ldap_get_lderrno(ldaptcl->ldap);
131 if ((ldaptcl->flags & LDAPTCL_INTERRCODES) || code > LDAPTCL_MAXERR ||
132 ldaptclerrorcode[code] == NULL) {
133 sprintf(shortbuf, "0x%03x", code);
136 errp = ldaptclerrorcode[code];
138 Tcl_SetErrorCode(interp, errp, NULL);
139 if (ldaptcl->trapCmdObj) {
142 if (ldaptcl->traplist != NULL) {
143 for (i = ldaptcl->traplist; *i && *i != code; i++)
147 (void) Tcl_EvalObj(interp, ldaptcl->trapCmdObj);
152 LDAP_ErrorStringToCode(Tcl_Interp *interp, char *s)
157 offset = (strncasecmp(s, "LDAP_", 5) == 0) ? 0 : 5;
158 for (code = 0; code < LDAPTCL_MAXERR; code++) {
159 if (!ldaptclerrorcode[code]) continue;
160 if (strcasecmp(s, ldaptclerrorcode[code]+offset) == 0)
163 Tcl_ResetResult(interp);
164 Tcl_AppendResult(interp, s, " is an invalid code", (char *) NULL);
168 /*-----------------------------------------------------------------------------
169 * LDAP_ProcessOneSearchResult --
171 * Process one result return from an LDAP search.
174 * o interp - Tcl interpreter; Errors are returned in result.
175 * o ldap - LDAP structure pointer.
176 * o entry - LDAP message pointer.
177 * o destArrayNameObj - Name of Tcl array in which to store attributes.
178 * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
180 * o TCL_OK if processing succeeded..
181 * o TCL_ERROR if an error occured, with error message in interp.
182 *-----------------------------------------------------------------------------
185 LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
189 Tcl_Obj *destArrayNameObj;
190 Tcl_Obj *evalCodeObj;
193 Tcl_Obj *attributeNameObj;
194 Tcl_Obj *attributeDataObj;
197 struct berval **bvals;
201 Tcl_UnsetVar (interp, Tcl_GetStringFromObj (destArrayNameObj, NULL), 0);
203 dn = ldap_get_dn(ldap, entry);
205 if (Tcl_SetVar2(interp, /* set dn */
206 Tcl_GetStringFromObj(destArrayNameObj, NULL),
209 TCL_LEAVE_ERR_MSG) == NULL)
213 attributeNameObj = Tcl_NewObj();
214 Tcl_IncrRefCount (attributeNameObj);
216 /* Note that attributeName below is allocated for OL2+ libldap, so it
217 must be freed with ldap_memfree(). Test below is admittedly a hack.
220 for (attributeName = ldap_first_attribute (ldap, entry, &ber);
221 attributeName != NULL;
222 attributeName = ldap_next_attribute(ldap, entry, ber)) {
224 bvals = ldap_get_values_len(ldap, entry, attributeName);
227 /* Note here that the U.of.M. ldap will return a null bvals
228 when the last attribute value has been deleted, but still
229 retains the attributeName. Even though this is documented
230 as an error, we ignore it to present a consistent interface
231 with Netscape's server
233 attributeDataObj = Tcl_NewObj();
234 Tcl_SetStringObj(attributeNameObj, attributeName, -1);
235 #if LDAP_API_VERSION >= 2004
236 ldap_memfree(attributeName); /* free if newer API */
238 for (i = 0; bvals[i] != NULL; i++) {
239 Tcl_Obj *singleAttributeValueObj;
241 singleAttributeValueObj = Tcl_NewStringObj(bvals[i]->bv_val, bvals[i]->bv_len);
242 if (Tcl_ListObjAppendElement (interp,
244 singleAttributeValueObj)
251 ldap_value_free_len(bvals);
253 if (Tcl_ObjSetVar2 (interp,
257 TCL_LEAVE_ERR_MSG) == NULL) {
262 Tcl_DecrRefCount (attributeNameObj);
263 return Tcl_EvalObj (interp, evalCodeObj);
266 /*-----------------------------------------------------------------------------
267 * LDAP_PerformSearch --
269 * Perform an LDAP search.
272 * o interp - Tcl interpreter; Errors are returned in result.
273 * o ldap - LDAP structure pointer.
274 * o base - Base DN from which to perform search.
275 * o scope - LDAP search scope, must be one of LDAP_SCOPE_BASE,
276 * LDAP_SCOPE_ONELEVEL, or LDAP_SCOPE_SUBTREE.
277 * o attrs - Pointer to array of char * pointers of desired
278 * attribute names, or NULL for all attributes.
279 * o filtpatt LDAP filter pattern.
280 * o value Value to get sprintf'ed into filter pattern.
281 * o destArrayNameObj - Name of Tcl array in which to store attributes.
282 * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
284 * o TCL_OK if processing succeeded..
285 * o TCL_ERROR if an error occured, with error message in interp.
286 *-----------------------------------------------------------------------------
289 LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
290 destArrayNameObj, evalCodeObj, timeout_p, all, sortattr)
298 Tcl_Obj *destArrayNameObj;
299 Tcl_Obj *evalCodeObj;
300 struct timeval *timeout_p;
304 LDAP *ldap = ldaptcl->ldap;
309 int tclResult = TCL_OK;
311 LDAPMessage *resultMessage = 0;
312 LDAPMessage *entryMessage = 0;
317 sprintf(filter, filtpatt, value);
320 if ((msgid = ldap_search (ldap, base, scope, filter, attrs, 0)) == -1) {
321 Tcl_AppendResult (interp,
322 "LDAP start search error: ",
323 LDAP_ERR_STRING(ldap),
325 LDAP_SetErrorCode(ldaptcl, -1, interp);
334 resultCode = ldap_result (ldap, msgid, all, timeout_p, &resultMessage);
335 if (resultCode != LDAP_RES_SEARCH_RESULT &&
336 resultCode != LDAP_RES_SEARCH_ENTRY)
340 sortKey = (strcasecmp(sortattr, "dn") == 0) ? NULL : sortattr;
341 ldap_sort_entries(ldap, &resultMessage, sortKey, strcasecmp);
343 entryMessage = ldap_first_entry(ldap, resultMessage);
345 while (entryMessage) {
346 tclResult = LDAP_ProcessOneSearchResult (interp,
351 if (tclResult != TCL_OK) {
352 if (tclResult == TCL_CONTINUE) {
354 } else if (tclResult == TCL_BREAK) {
358 } else if (tclResult == TCL_ERROR) {
360 sprintf(msg, "\n (\"search\" body line %d)",
362 Tcl_AddObjErrorInfo(interp, msg, -1);
370 entryMessage = ldap_next_entry(ldap, entryMessage);
372 if (resultCode == LDAP_RES_SEARCH_RESULT || all)
375 ldap_msgfree(resultMessage);
376 resultMessage = NULL;
380 ldap_msgfree(resultMessage);
381 if (resultCode == LDAP_RES_SEARCH_ENTRY)
382 ldap_abandon(ldap, msgid);
385 if (resultCode == -1) {
386 Tcl_ResetResult (interp);
387 Tcl_AppendResult (interp,
388 "LDAP result search error: ",
389 LDAP_ERR_STRING(ldap),
391 LDAP_SetErrorCode(ldaptcl, -1, interp);
395 if ((errorCode = ldap_result2error (ldap, resultMessage, 0))
397 Tcl_ResetResult (interp);
398 Tcl_AppendResult (interp,
399 "LDAP search error: ",
400 ldap_err2string(errorCode),
403 ldap_msgfree(resultMessage);
404 LDAP_SetErrorCode(ldaptcl, errorCode, interp);
408 ldap_msgfree(resultMessage);
412 /*-----------------------------------------------------------------------------
413 * NeoX_LdapTargetObjCmd --
415 * Implements the body of commands created by Neo_LdapObjCmd.
418 * A standard Tcl result.
421 * See the user documentation.
422 *-----------------------------------------------------------------------------
425 NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
426 ClientData clientData;
429 Tcl_Obj *CONST objv[];
433 LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
434 LDAP *ldap = ldaptcl->ldap;
437 int is_add_or_modify = 0;
439 char *m, *s, *errmsg;
442 int lderrno; /* might be used by LDAP_ERR_STRING macro */
444 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
447 Tcl_WrongNumArgs (interp, 1, objv, "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 Tcl_WrongNumArgs (interp, 2, objv, "authtype dn passwd");
467 ldap_authString = Tcl_GetStringFromObj (objv[2], NULL);
469 if (STREQU (ldap_authString, "simple")) {
470 ldap_authInt = LDAP_AUTH_SIMPLE;
473 else if (STREQU (ldap_authString, "kerberos_ldap")) {
474 ldap_authInt = LDAP_AUTH_KRBV41;
475 } else if (STREQU (ldap_authString, "kerberos_dsa")) {
476 ldap_authInt = LDAP_AUTH_KRBV42;
477 } else if (STREQU (ldap_authString, "kerberos_both")) {
478 ldap_authInt = LDAP_AUTH_KRBV4;
482 Tcl_AppendStringsToObj (resultObj,
488 "\" authtype must be one of \"simple\", ",
489 "\"kerberos_ldap\", \"kerberos_dsa\" ",
490 "or \"kerberos_both\"",
492 "\" authtype must be \"simple\", ",
498 binddn = Tcl_GetStringFromObj (objv[3], &stringLength);
499 if (stringLength == 0)
502 passwd = Tcl_GetStringFromObj (objv[4], &stringLength);
503 if (stringLength == 0)
506 /* ldap_bind_s(ldap, dn, pw, method) */
509 #define LDAP_BIND(ldap, dn, pw, method) \
510 ldap_bind_s(ldap, dn, pw, method)
512 #define LDAP_BIND(ldap, dn, pw, method) \
513 ldap_simple_bind_s(ldap, dn, pw)
515 if ((errcode = LDAP_BIND (ldap,
518 ldap_authInt)) != LDAP_SUCCESS) {
520 Tcl_AppendStringsToObj (resultObj,
522 ldap_err2string(errcode),
524 LDAP_SetErrorCode(ldaptcl, errcode, interp);
530 if (STREQU (subCommand, "unbind")) {
532 Tcl_WrongNumArgs (interp, 2, objv, "");
536 return Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], NULL));
539 /* object delete dn */
540 if (STREQU (subCommand, "delete")) {
542 Tcl_WrongNumArgs (interp, 2, objv, "dn");
546 dn = Tcl_GetStringFromObj (objv [2], NULL);
547 if ((errcode = ldap_delete_s(ldap, dn)) != LDAP_SUCCESS) {
548 Tcl_AppendStringsToObj (resultObj,
549 "LDAP delete error: ",
550 ldap_err2string(errcode),
552 LDAP_SetErrorCode(ldaptcl, errcode, interp);
558 /* object rename_rdn dn rdn */
559 /* object modify_rdn dn rdn */
560 if (STREQU (subCommand, "rename_rdn") || STREQU (subCommand, "modify_rdn")) {
565 Tcl_WrongNumArgs (interp, 2, objv, "dn rdn");
569 dn = Tcl_GetStringFromObj (objv [2], NULL);
570 rdn = Tcl_GetStringFromObj (objv [3], NULL);
572 deleteOldRdn = (*subCommand == 'r');
574 if ((errcode = ldap_modrdn2_s (ldap, dn, rdn, deleteOldRdn)) != LDAP_SUCCESS) {
575 Tcl_AppendStringsToObj (resultObj,
579 ldap_err2string(errcode),
581 LDAP_SetErrorCode(ldaptcl, errcode, interp);
587 /* object add dn attributePairList */
588 /* object add_attributes dn attributePairList */
589 /* object replace_attributes dn attributePairList */
590 /* object delete_attributes dn attributePairList */
592 if (STREQU (subCommand, "add")) {
594 is_add_or_modify = 1;
597 if (STREQU (subCommand, "add_attributes")) {
598 is_add_or_modify = 1;
599 mod_op = LDAP_MOD_ADD;
600 } else if (STREQU (subCommand, "replace_attributes")) {
601 is_add_or_modify = 1;
602 mod_op = LDAP_MOD_REPLACE;
603 } else if (STREQU (subCommand, "delete_attributes")) {
604 is_add_or_modify = 1;
605 mod_op = LDAP_MOD_DELETE;
609 if (is_add_or_modify) {
613 char **valPtrs = NULL;
615 Tcl_Obj **attribObjv;
617 Tcl_Obj **valuesObjv;
618 int nPairs, allPairs;
624 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
626 if (objc < 4 || objc > 4 && is_add || is_add == 0 && objc&1) {
627 Tcl_AppendStringsToObj (resultObj,
629 Tcl_GetStringFromObj (objv [0], NULL),
632 " dn attributePairList",
635 Tcl_AppendStringsToObj (resultObj,
636 " ?[add|delete|replace] attributePairList ...?", (char *)NULL);
640 dn = Tcl_GetStringFromObj (objv [2], NULL);
643 for (i = 3; i < objc; i += 2) {
644 if (Tcl_ListObjLength (interp, objv[i], &j) == TCL_ERROR)
647 Tcl_AppendStringsToObj (resultObj,
648 "attribute list does not contain an ",
649 "even number of key-value elements",
656 modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (allPairs + 1));
663 if (Tcl_ListObjGetElements (interp, objv [pairIndex], &attribObjc, &attribObjv)
669 nPairs = attribObjc / 2;
671 for (i = 0; i < nPairs; i++) {
672 mod = modArray[modIndex++] = (LDAPMod *) malloc (sizeof(LDAPMod));
673 mod->mod_op = mod_op;
674 mod->mod_type = Tcl_GetStringFromObj (attribObjv [i * 2], NULL);
676 if (Tcl_ListObjGetElements (interp, attribObjv [i * 2 + 1], &valuesObjc, &valuesObjv) == TCL_ERROR) {
677 /* FIX: cleanup memory here */
682 valPtrs = mod->mod_vals.modv_strvals = \
683 (char **)malloc (sizeof (char *) * (valuesObjc + 1));
684 valPtrs[valuesObjc] = (char *)NULL;
686 for (j = 0; j < valuesObjc; j++) {
687 valPtrs [j] = Tcl_GetStringFromObj (valuesObjv[j], NULL);
689 /* If it's "delete" and value is an empty string, make
690 * value be NULL to indicate entire attribute is to be
692 if ((*valPtrs [j] == '\0')
693 && (mod->mod_op == LDAP_MOD_DELETE || mod->mod_op == LDAP_MOD_REPLACE)) {
700 if (mod_op != -1 && pairIndex < objc) {
701 subCommand = Tcl_GetStringFromObj (objv[pairIndex - 1], NULL);
703 if (STREQU (subCommand, "add")) {
704 mod_op = LDAP_MOD_ADD;
705 } else if (STREQU (subCommand, "replace")) {
706 mod_op = LDAP_MOD_REPLACE;
707 } else if (STREQU (subCommand, "delete")) {
708 mod_op = LDAP_MOD_DELETE;
711 Tcl_SetStringObj (resultObj,
712 "Additional operators must be one of"
713 " add, replace, or delete", -1);
719 } while (mod_op != -1 && pairIndex < objc);
720 modArray[modIndex] = (LDAPMod *) NULL;
723 result = ldap_add_s (ldap, dn, modArray);
725 result = ldap_modify_s (ldap, dn, modArray);
726 if (ldaptcl->caching)
727 ldap_uncache_entry (ldap, dn);
730 /* free the modArray elements, then the modArray itself. */
732 for (i = 0; i < modIndex; i++) {
733 free ((char *) modArray[i]->mod_vals.modv_strvals);
734 free ((char *) modArray[i]);
736 free ((char *) modArray);
738 /* after modArray is allocated, mod_op = -1 upon error for cleanup */
742 /* FIX: memory cleanup required all over the place here */
743 if (result != LDAP_SUCCESS) {
744 Tcl_AppendStringsToObj (resultObj,
748 ldap_err2string(result),
750 LDAP_SetErrorCode(ldaptcl, result, interp);
756 /* object search controlArray dn pattern */
757 if (STREQU (subCommand, "search")) {
758 char *controlArrayName;
759 Tcl_Obj *controlArrayNameObj;
769 char **attributesArray;
770 char *attributesString;
773 char *filterPatternString;
777 struct timeval timeout, *timeout_p;
785 Tcl_Obj *destArrayNameObj;
786 Tcl_Obj *evalCodeObj;
789 Tcl_WrongNumArgs (interp, 2, objv,
790 "controlArray destArray code");
794 controlArrayNameObj = objv [2];
795 controlArrayName = Tcl_GetStringFromObj (controlArrayNameObj, NULL);
797 destArrayNameObj = objv [3];
799 evalCodeObj = objv [4];
801 baseString = Tcl_GetVar2 (interp,
806 if (baseString == (char *)NULL) {
807 Tcl_AppendStringsToObj (resultObj,
808 "required element \"base\" ",
809 "is missing from ldap control array \"",
816 filterPatternString = Tcl_GetVar2 (interp,
820 if (filterPatternString == (char *)NULL) {
821 filterPatternString = "(objectclass=*)";
824 /* Fetch scope setting from control array.
825 * If it doesn't exist, default to subtree scoping.
827 scopeString = Tcl_GetVar2 (interp, controlArrayName, "scope", 0);
828 if (scopeString == NULL) {
829 scope = LDAP_SCOPE_SUBTREE;
831 if (STREQU(scopeString, "base"))
832 scope = LDAP_SCOPE_BASE;
833 else if (STRNEQU(scopeString, "one", 3))
834 scope = LDAP_SCOPE_ONELEVEL;
835 else if (STRNEQU(scopeString, "sub", 3))
836 scope = LDAP_SCOPE_SUBTREE;
838 Tcl_AppendStringsToObj (resultObj,
839 "\"scope\" element of \"",
841 "\" array is not one of ",
842 "\"base\", \"onelevel\", ",
849 #ifdef LDAP_OPT_DEREF
850 /* Fetch dereference control setting from control array.
851 * If it doesn't exist, default to never dereference. */
852 derefString = Tcl_GetVar2 (interp,
856 if (derefString == (char *)NULL) {
857 deref = LDAP_DEREF_NEVER;
859 if (STREQU(derefString, "never"))
860 deref = LDAP_DEREF_NEVER;
861 else if (STREQU(derefString, "search"))
862 deref = LDAP_DEREF_SEARCHING;
863 else if (STREQU(derefString, "find"))
864 deref = LDAP_DEREF_FINDING;
865 else if (STREQU(derefString, "always"))
866 deref = LDAP_DEREF_ALWAYS;
868 Tcl_AppendStringsToObj (resultObj,
869 "\"deref\" element of \"",
871 "\" array is not one of ",
872 "\"never\", \"search\", \"find\", ",
880 /* Fetch list of attribute names from control array.
881 * If entry doesn't exist, default to NULL (all).
883 attributesString = Tcl_GetVar2 (interp,
887 if (attributesString == (char *)NULL) {
888 attributesArray = NULL;
890 if ((Tcl_SplitList (interp,
893 &attributesArray)) != TCL_OK) {
898 /* Fetch timeout value if there is one
900 timeoutString = Tcl_GetVar2 (interp,
905 if (timeoutString == (char *)NULL) {
909 if (Tcl_GetDouble(interp, timeoutString, &timeoutTime) != TCL_OK)
911 timeout.tv_sec = floor(timeoutTime);
912 timeout.tv_usec = (timeoutTime-timeout.tv_sec) * 1000000;
913 timeout_p = &timeout;
916 paramString = Tcl_GetVar2 (interp, controlArrayName, "cache", 0);
918 if (Tcl_GetInt(interp, paramString, &cacheThis) == TCL_ERROR)
922 paramString = Tcl_GetVar2 (interp, controlArrayName, "all", 0);
924 if (Tcl_GetInt(interp, paramString, &all) == TCL_ERROR)
928 sortattr = Tcl_GetVar2 (interp, controlArrayName, "sort", 0);
931 ldap->ld_deref = deref;
932 ldap->ld_timelimit = 0;
933 ldap->ld_sizelimit = 0;
934 ldap->ld_options = 0;
937 /* Caching control within the search: if the "cache" control array */
938 /* value is set, disable/enable caching accordingly */
941 if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
943 if (ldaptcl->timeout == 0) {
944 Tcl_SetStringObj(resultObj, "Caching never before enabled, I have no timeout value to use", -1);
947 ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
950 ldap_disable_cache(ldap);
954 #ifdef LDAP_OPT_DEREF
955 ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
958 tclResult = LDAP_PerformSearch (interp,
970 /* Following the search, if we changed the caching behavior, change */
973 if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
975 ldap_disable_cache(ldap);
977 ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
979 #ifdef LDAP_OPT_DEREF
980 deref = LDAP_DEREF_NEVER;
981 ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
987 /* object compare dn attr value */
988 if (STREQU (subCommand, "compare")) {
996 Tcl_WrongNumArgs (interp,
998 "dn attribute value");
1002 dn = Tcl_GetStringFromObj (objv[2], NULL);
1003 attr = Tcl_GetStringFromObj (objv[3], NULL);
1004 value = Tcl_GetStringFromObj (objv[4], NULL);
1006 result = ldap_compare_s (ldap, dn, attr, value);
1007 if (result == LDAP_COMPARE_TRUE || result == LDAP_COMPARE_FALSE) {
1008 Tcl_SetBooleanObj(resultObj, result == LDAP_COMPARE_TRUE);
1011 LDAP_SetErrorCode(ldaptcl, result, interp);
1012 Tcl_AppendStringsToObj (resultObj,
1013 "LDAP compare error: ",
1014 LDAP_ERR_STRING(ldap),
1019 if (STREQU (subCommand, "cache")) {
1020 #if defined(UMICH_LDAP) || (defined(OPEN_LDAP) && !defined(LDAP_API_VERSION))
1025 Tcl_WrongNumArgs (interp, 2, objv [0], "command [args...]");
1029 cacheCommand = Tcl_GetStringFromObj (objv [2], NULL);
1031 if (STREQU (cacheCommand, "uncache")) {
1035 Tcl_WrongNumArgs (interp,
1041 dn = Tcl_GetStringFromObj (objv [3], NULL);
1042 ldap_uncache_entry (ldap, dn);
1046 if (STREQU (cacheCommand, "enable")) {
1047 long timeout = ldaptcl->timeout;
1048 long maxmem = ldaptcl->maxmem;
1051 Tcl_WrongNumArgs (interp, 3, objv, "?timeout? ?maxmem?");
1056 if (Tcl_GetLongFromObj (interp, objv [3], &timeout) == TCL_ERROR)
1060 Tcl_SetStringObj(resultObj,
1061 objc > 3 ? "timeouts must be greater than 0" :
1062 "no previous timeout to reference", -1);
1067 if (Tcl_GetLongFromObj (interp, objv [4], &maxmem) == TCL_ERROR)
1070 if (ldap_enable_cache (ldap, timeout, maxmem) == -1) {
1071 Tcl_AppendStringsToObj (resultObj,
1072 "LDAP cache enable error: ",
1073 LDAP_ERR_STRING(ldap),
1075 LDAP_SetErrorCode(ldaptcl, -1, interp);
1078 ldaptcl->caching = 1;
1079 ldaptcl->timeout = timeout;
1080 ldaptcl->maxmem = maxmem;
1084 if (objc != 3) goto badargs;
1086 if (STREQU (cacheCommand, "disable")) {
1087 ldap_disable_cache (ldap);
1088 ldaptcl->caching = 0;
1092 if (STREQU (cacheCommand, "destroy")) {
1093 ldap_destroy_cache (ldap);
1094 ldaptcl->caching = 0;
1098 if (STREQU (cacheCommand, "flush")) {
1099 ldap_flush_cache (ldap);
1103 if (STREQU (cacheCommand, "no_errors")) {
1104 ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHENOERRS);
1108 if (STREQU (cacheCommand, "all_errors")) {
1109 ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHEALLERRS);
1113 if (STREQU (cacheCommand, "size_errors")) {
1114 ldap_set_cache_options (ldap, 0);
1117 Tcl_AppendStringsToObj (resultObj,
1123 " must be one of \"enable\", ",
1125 "\"destroy\", \"flush\", \"uncache\", ",
1126 "\"no_errors\", \"size_errors\",",
1127 " or \"all_errors\"",
1134 if (STREQU (subCommand, "trap")) {
1135 Tcl_Obj *listObj, *resultObj;
1139 Tcl_WrongNumArgs (interp, 2, objv,
1140 "command ?errorCode-list?");
1144 if (!ldaptcl->trapCmdObj)
1146 resultObj = Tcl_NewListObj(0, NULL);
1147 Tcl_ListObjAppendElement(interp, resultObj, ldaptcl->trapCmdObj);
1148 if (ldaptcl->traplist) {
1149 listObj = Tcl_NewObj();
1150 for (p = ldaptcl->traplist; *p; p++) {
1151 Tcl_ListObjAppendElement(interp, listObj,
1152 Tcl_NewStringObj(ldaptclerrorcode[*p], -1));
1154 Tcl_ListObjAppendElement(interp, resultObj, listObj);
1156 Tcl_SetObjResult(interp, resultObj);
1159 if (ldaptcl->trapCmdObj) {
1160 Tcl_DecrRefCount (ldaptcl->trapCmdObj);
1161 ldaptcl->trapCmdObj = NULL;
1163 if (ldaptcl->traplist) {
1164 free(ldaptcl->traplist);
1165 ldaptcl->traplist = NULL;
1167 Tcl_GetStringFromObj(objv[2], &l);
1169 return TCL_OK; /* just turn off trap */
1170 ldaptcl->trapCmdObj = objv[2];
1171 Tcl_IncrRefCount (ldaptcl->trapCmdObj);
1173 return TCL_OK; /* no code list */
1174 if (Tcl_ListObjLength(interp, objv[3], &l) != TCL_OK)
1177 return TCL_OK; /* empty code list */
1178 ldaptcl->traplist = (int*)malloc(sizeof(int) * (l + 1));
1179 ldaptcl->traplist[l] = 0;
1180 for (i = 0; i < l; i++) {
1181 Tcl_ListObjIndex(interp, objv[3], i, &resultObj);
1182 code = LDAP_ErrorStringToCode(interp, Tcl_GetStringFromObj(resultObj, NULL));
1184 free(ldaptcl->traplist);
1185 ldaptcl->traplist = NULL;
1188 ldaptcl->traplist[i] = code;
1192 if (STREQU (subCommand, "trapcodes")) {
1196 resultObj = Tcl_GetObjResult(interp);
1198 for (code = 0; code < LDAPTCL_MAXERR; code++) {
1199 if (!ldaptclerrorcode[code]) continue;
1200 Tcl_ListObjAppendElement(interp, resultObj,
1201 Tcl_NewStringObj(ldaptclerrorcode[code], -1));
1206 if (STREQU (subCommand, "debug")) {
1208 Tcl_AppendStringsToObj(resultObj, "Wrong # of arguments",
1212 return Tcl_GetIntFromObj(interp, objv[2], &ldap_debug);
1216 /* FIX: this needs to enumerate all the possibilities */
1217 Tcl_AppendStringsToObj (resultObj,
1220 "\" must be one of \"add\", ",
1221 "\"add_attributes\", ",
1222 "\"bind\", \"cache\", \"delete\", ",
1223 "\"delete_attributes\", \"modify\", ",
1224 "\"modify_rdn\", \"rename_rdn\", ",
1225 "\"replace_attributes\", ",
1226 "\"search\" or \"unbind\".",
1232 * Delete and LDAP command object
1236 NeoX_LdapObjDeleteCmd(clientData)
1237 ClientData clientData;
1239 LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
1240 LDAP *ldap = ldaptcl->ldap;
1242 if (ldaptcl->trapCmdObj)
1243 Tcl_DecrRefCount (ldaptcl->trapCmdObj);
1244 if (ldaptcl->traplist)
1245 free(ldaptcl->traplist);
1247 free((char*) ldaptcl);
1250 /*-----------------------------------------------------------------------------
1251 * NeoX_LdapObjCmd --
1253 * Implements the `ldap' command:
1254 * ldap open newObjName host [port]
1255 * ldap init newObjName host [port]
1258 * A standard Tcl result.
1261 * See the user documentation.
1262 *-----------------------------------------------------------------------------
1265 NeoX_LdapObjCmd (clientData, interp, objc, objv)
1266 ClientData clientData;
1269 Tcl_Obj *CONST objv[];
1275 int ldapPort = LDAP_PORT;
1279 Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
1282 Tcl_WrongNumArgs (interp, 1, objv,
1283 "(open|init) new_command host [port]|explode dn");
1287 subCommand = Tcl_GetStringFromObj (objv[1], NULL);
1289 if (STREQU(subCommand, "explode")) {
1293 char **exploded, **p;
1295 param = Tcl_GetStringFromObj (objv[2], NULL);
1296 if (param[0] == '-') {
1297 if (STREQU(param, "-nonames")) {
1299 } else if (STREQU(param, "-list")) {
1302 Tcl_WrongNumArgs (interp, 1, objv, "explode ?-nonames|-list? dn");
1306 if (nonames || list)
1307 param = Tcl_GetStringFromObj (objv[3], NULL);
1308 exploded = ldap_explode_dn(param, nonames);
1309 for (p = exploded; *p; p++) {
1311 char *q = strchr(*p, '=');
1313 Tcl_SetObjLength(resultObj, 0);
1314 Tcl_AppendStringsToObj(resultObj, "rdn ", *p,
1315 " missing '='", NULL);
1316 ldap_value_free(exploded);
1320 if (Tcl_ListObjAppendElement(interp, resultObj,
1321 Tcl_NewStringObj(*p, -1)) != TCL_OK ||
1322 Tcl_ListObjAppendElement(interp, resultObj,
1323 Tcl_NewStringObj(q+1, -1)) != TCL_OK) {
1324 ldap_value_free(exploded);
1328 if (Tcl_ListObjAppendElement(interp, resultObj,
1329 Tcl_NewStringObj(*p, -1))) {
1330 ldap_value_free(exploded);
1335 ldap_value_free(exploded);
1340 if (STREQU(subCommand, "friendly")) {
1341 char *friendly = ldap_dn2ufn(Tcl_GetStringFromObj(objv[2], NULL));
1342 Tcl_SetStringObj(resultObj, friendly, -1);
1348 newCommand = Tcl_GetStringFromObj (objv[2], NULL);
1349 ldapHost = Tcl_GetStringFromObj (objv[3], NULL);
1352 if (Tcl_GetIntFromObj (interp, objv [4], &ldapPort) == TCL_ERROR) {
1353 Tcl_AppendStringsToObj (resultObj,
1354 "LDAP port number is non-numeric",
1360 if (STREQU (subCommand, "open")) {
1361 ldap = ldap_open (ldapHost, ldapPort);
1362 } else if (STREQU (subCommand, "init")) {
1369 #if LDAPTCL_PROTOCOL_VERSION_DEFAULT
1370 version = LDAPTCL_PROTOCOL_VERSION_DEFAULT;
1373 for (i = 6; i < objc; i += 2) {
1374 subOption = Tcl_GetStringFromObj(objv[i-1], NULL);
1375 if (STREQU (subOption, "protocol_version")) {
1376 #ifdef LDAP_OPT_PROTOCOL_VERSION
1377 subValue = Tcl_GetStringFromObj(objv[i], NULL);
1378 if (STREQU (subValue, "2")) {
1379 version = LDAP_VERSION2;
1381 else if (STREQU (subValue, "3")) {
1382 #ifdef LDAP_VERSION3
1383 version = LDAP_VERSION3;
1385 Tcl_SetStringObj (resultObj, "protocol_version 3 not supported", -1);
1390 Tcl_SetStringObj (resultObj, "protocol_version must be '2' or '3'", -1);
1394 Tcl_SetStringObj (resultObj, "protocol_version not supported", -1);
1397 } else if (STREQU (subOption, "port")) {
1398 if (Tcl_GetIntFromObj (interp, objv [i], &ldapPort) == TCL_ERROR) {
1399 Tcl_AppendStringsToObj (resultObj,
1400 "LDAP port number is non-numeric",
1405 Tcl_SetStringObj (resultObj, "valid options: protocol_version, port", -1);
1409 ldap = ldap_init (ldapHost, ldapPort);
1411 #if LDAP_OPT_PROTOCOL_VERSION
1413 ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
1416 Tcl_AppendStringsToObj (resultObj,
1417 "option was not \"open\" or \"init\"");
1421 if (ldap == (LDAP *)NULL) {
1422 Tcl_SetErrno(errno);
1423 Tcl_AppendStringsToObj (resultObj,
1424 Tcl_PosixError (interp),
1430 ldap->ld_deref = LDAP_DEREF_NEVER; /* Turn off alias dereferencing */
1433 ldaptcl = (LDAPTCL *) malloc(sizeof(LDAPTCL));
1434 ldaptcl->ldap = ldap;
1435 ldaptcl->caching = 0;
1436 ldaptcl->timeout = 0;
1437 ldaptcl->maxmem = 0;
1438 ldaptcl->trapCmdObj = NULL;
1439 ldaptcl->traplist = NULL;
1442 Tcl_CreateObjCommand (interp,
1444 NeoX_LdapTargetObjCmd,
1445 (ClientData) ldaptcl,
1446 NeoX_LdapObjDeleteCmd);
1450 /*-----------------------------------------------------------------------------
1452 * Initialize the LDAP interface.
1453 *-----------------------------------------------------------------------------
1456 Ldaptcl_Init (interp)
1459 Tcl_CreateObjCommand (interp,
1463 (Tcl_CmdDeleteProc*) NULL);
1465 if (Neo_initLDAPX(interp) != TCL_OK)
1468 Tcl_PkgProvide(interp, "Ldaptcl", VERSION);