Index: src/eldap.c =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/eldap.c,v retrieving revision 1.13.2.10 diff -u -u -r1.13.2.10 eldap.c --- eldap.c 1999/10/24 03:48:33 1.13.2.10 +++ eldap.c 1999/12/12 12:10:40 @@ -54,6 +54,9 @@ static Lisp_Object Qkrbv41, Qkrbv42; /* Deref policy */ static Lisp_Object Qnever, Qalways, Qfind; +/* Modification types (Qdelete is defined in general.c) */ +static Lisp_Object Qadd, Qreplace; + /************************************************************************/ /* Utility Functions */ @@ -313,6 +316,9 @@ if ((err = ldap_set_option (ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON)) != LDAP_SUCCESS) signal_ldap_error (ld, NULL, err); + if ((err = ldap_set_option (ld, LDAP_OPT_RESTART, + LDAP_OPT_ON)) != LDAP_SUCCESS) + signal_ldap_error (ld, NULL, err); #else /* not HAVE_LDAP_SET_OPTION */ ld->ld_deref = ldap_deref; ld->ld_timelimit = ldap_timelimit; @@ -322,12 +328,11 @@ #else /* not LDAP_REFERRALS */ ld->ld_options = 0; #endif /* not LDAP_REFERRALS */ + /* XEmacs uses interrupts (SIGIO,SIGALRM), LDAP calls need to ignore them */ + ld->ld_options |= LDAP_OPT_RESTART; #endif /* not HAVE_LDAP_SET_OPTION */ - /* ldap_bind_s calls select and may be wedged by SIGIO. */ - slow_down_interrupts (); err = ldap_bind_s (ld, ldap_binddn, ldap_passwd, ldap_auth); - speed_up_interrupts (); if (err != LDAP_SUCCESS) signal_simple_error ("Failed binding to the server", build_string (ldap_err2string (err))); @@ -365,7 +370,6 @@ struct berval **vals; }; - static Lisp_Object ldap_search_unwind (Lisp_Object unwind_obj) { @@ -378,7 +382,12 @@ return Qnil; } -DEFUN ("ldap-search-internal", Fldap_search_internal, 2, 7, 0, /* +/* The following function is called `ldap-search-basic' instead of */ +/* plain `ldap-search' to maintain compatibility with the XEmacs 21.1 */ +/* API where `ldap-search' was the name of the high-level search */ +/* function */ + +DEFUN ("ldap-search-basic", Fldap_search_basic, 2, 8, 0, /* Perform a search on an open LDAP connection. LDAP is an LDAP connection object created with `ldap-open'. FILTER is a filter string for the search as described in RFC 1558. @@ -389,13 +398,14 @@ for each matching entry. If nil return all available attributes. If ATTRSONLY is non-nil then only the attributes are retrieved, not the associated values. -If WITHDN is non-nil each entry in the result will be prepennded with +If WITHDN is non-nil each entry in the result will be prepended with its distinguished name DN. +If VERBOSE is non-nil progress messages will be echoed. The function returns a list of matching entries. Each entry is itself an alist of attribute/value pairs optionally preceded by the DN of the entry according to the value of WITHDN. */ - (ldap, filter, base, scope, attrs, attrsonly, withdn)) + (ldap, filter, base, scope, attrs, attrsonly, withdn, verbose)) { /* This function can GC */ @@ -491,10 +501,8 @@ /* Build the results list */ matches = 0; - /* ldap_result calls select() and can get wedged by EINTR signals */ - slow_down_interrupts (); rc = ldap_result (ld, LDAP_RES_ANY, 0, NULL, &unwind.res); - speed_up_interrupts (); + while (rc == LDAP_RES_SEARCH_ENTRY) { QUIT; @@ -504,7 +512,8 @@ destroys the current echo area contents, even when invoked from Lisp. It should use echo_area_message() instead, and restore the old echo area contents later. */ - message ("Parsing ldap results... %d", matches); + if (! NILP (verbose)) + message ("Parsing ldap results... %d", matches); entry = Qnil; /* Get the DN if required */ if (! NILP (withdn)) @@ -540,39 +549,229 @@ ldap_msgfree (unwind.res); unwind.res = NULL; - slow_down_interrupts (); rc = ldap_result (ld, LDAP_RES_ANY, 0, NULL, &(unwind.res)); - speed_up_interrupts (); } - if (rc == -1) - signal_ldap_error (ld, unwind.res, 0); - - if (rc == 0) - signal_ldap_error (ld, NULL, LDAP_TIMELIMIT_EXCEEDED); - #if defined HAVE_LDAP_PARSE_RESULT rc2 = ldap_parse_result (ld, unwind.res, &rc, NULL, NULL, NULL, NULL, 0); if (rc2 != LDAP_SUCCESS) rc = rc2; -#elif defined HAVE_LDAP_RESULT2ERROR +#else + if (rc == 0) + signal_ldap_error (ld, NULL, LDAP_TIMELIMIT_EXCEEDED); + + if (rc == -1) + signal_ldap_error (ld, unwind.res, (unwind.res==NULL) ? ld->ld_errno : 0); + +#if defined HAVE_LDAP_RESULT2ERROR rc = ldap_result2error (ld, unwind.res, 0); +#endif #endif - if ((rc != LDAP_SUCCESS) && (rc != LDAP_SIZELIMIT_EXCEEDED)) + + if (rc != LDAP_SUCCESS) signal_ldap_error (ld, NULL, rc); ldap_msgfree (unwind.res); unwind.res = (LDAPMessage *)NULL; + /* #### See above for calling message(). */ - message ("Parsing ldap results... done"); + if (! NILP (verbose)) + message ("Parsing ldap results... done"); unbind_to (speccount, Qnil); UNGCPRO; return Fnreverse (result); } +DEFUN ("ldap-add", Fldap_add, 3, 3, 0, /* +Add an entry to an LDAP directory. +LDAP is an LDAP connection object created with `ldap-open'. +DN is the distinguished name of the entry to add. +ENTRY is an entry specification, i.e., a list of cons cells +containing attribute/value string pairs. +*/ + (ldap, dn, entry)) +{ + LDAP *ld; + LDAPMod *ldap_mods, **ldap_mods_ptrs; + struct berval *bervals; + int rc; + int i, j; + + Lisp_Object current, values; + struct gcpro gcpro1, gcpro2; + GCPRO2 (current, values); + + /* Do all the parameter checking */ + CHECK_LIVE_LDAP (ldap); + ld = XLDAP (ldap)->ld; + + /* Check the DN */ + CHECK_STRING (dn); + + /* Check the entry */ + CHECK_CONS (entry); + if (NILP (entry)) + signal_simple_error ("Cannot add void entry", entry); + + /* Build the ldap_mods array */ + ldap_mods = alloca_array (LDAPMod, XINT (Flength (entry))); + ldap_mods_ptrs = alloca_array (LDAPMod *, 1 + XINT (Flength (entry))); + i = 0; + EXTERNAL_LIST_LOOP (entry, entry) + { + current = XCAR (entry); + CHECK_CONS (current); + CHECK_STRING (XCAR (current)); + ldap_mods_ptrs[i] = &(ldap_mods[i]); + GET_C_STRING_OS_DATA_ALLOCA ( XCAR (current), ldap_mods[i].mod_type); + ldap_mods[i].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + values = XCDR (current); + if (CONSP (values)) + { + bervals = + alloca_array (struct berval, XINT (Flength (values))); + ldap_mods[i].mod_vals.modv_bvals = + alloca_array (struct berval *, 1 + XINT (Flength (values))); + j = 0; + EXTERNAL_LIST_LOOP (values, values) + { + current = XCAR (values); + CHECK_STRING (current); + ldap_mods[i].mod_vals.modv_bvals[j] = &(bervals[j]); + GET_STRING_OS_DATA_ALLOCA (current, + bervals[j].bv_val, + bervals[j].bv_len); + j++; + } + ldap_mods[i].mod_vals.modv_bvals[j] = NULL; + } + else + { + CHECK_STRING (values); + bervals = alloca_array (struct berval, 1); + ldap_mods[i].mod_vals.modv_bvals = alloca_array (struct berval *, 2); + ldap_mods[i].mod_vals.modv_bvals[0] = &(bervals[0]); + GET_STRING_OS_DATA_ALLOCA (values, + bervals[0].bv_val, + bervals[0].bv_len); + ldap_mods[i].mod_vals.modv_bvals[1] = NULL; + } + i++; + } + ldap_mods_ptrs[i] = NULL; + rc = ldap_add_s (ld, (char *) XSTRING_DATA (dn), ldap_mods_ptrs); + if (rc != LDAP_SUCCESS) + signal_ldap_error (ld, NULL, rc); + + UNGCPRO; +} + +DEFUN ("ldap-modify", Fldap_modify, 3, 3, 0, /* +Add an entry to an LDAP directory. +LDAP is an LDAP connection object created with `ldap-open'. +DN is the distinguished name of the entry to modify. +MODS is a list of modifications to apply. +A modification is a list of the form (MOD-OP ATTR VALUE1 VALUE2 ...) +MOD-OP and ATTR are mandatory, VALUEs are optional depending on MOD-OP. +MOD-OP is the type of modification, one of the symbols `add', `delete' +or `replace'. ATTR is the LDAP attribute type to modify +*/ + (ldap, dn, mods)) +{ + LDAP *ld; + LDAPMod *ldap_mods, **ldap_mods_ptrs; + struct berval *bervals; + int i, j, rc; + + Lisp_Object current, mod_op, values; + struct gcpro gcpro1, gcpro2; + + GCPRO2 (current, values); + + /* Do all the parameter checking */ + CHECK_LIVE_LDAP (ldap); + ld = XLDAP (ldap)->ld; + + /* Check the DN */ + CHECK_STRING (dn); + + /* Check the entry */ + CHECK_CONS (mods); + if (NILP (mods)) + return Qnil; + + /* Build the ldap_mods array */ + ldap_mods = alloca_array (LDAPMod, XINT (Flength (mods))); + ldap_mods_ptrs = alloca_array (LDAPMod *, 1 + XINT (Flength (mods))); + i = 0; + EXTERNAL_LIST_LOOP (mods, mods) + { + current = XCAR (mods); + CHECK_CONS (current); + CHECK_SYMBOL (XCAR (current)); + mod_op = XCAR (current); + ldap_mods_ptrs[i] = &(ldap_mods[i]); + ldap_mods[i].mod_op = LDAP_MOD_BVALUES; + if (EQ (mod_op, Qadd)) + ldap_mods[i].mod_op |= LDAP_MOD_ADD; + else if (EQ (mod_op, Qdelete)) + ldap_mods[i].mod_op |= LDAP_MOD_DELETE; + else if (EQ (mod_op, Qreplace)) + ldap_mods[i].mod_op |= LDAP_MOD_REPLACE; + else + signal_simple_error ("Invalid LDAP modification type", mod_op); + current = XCDR (current); + CHECK_STRING (XCAR (current)); + GET_C_STRING_OS_DATA_ALLOCA ( XCAR (current), ldap_mods[i].mod_type); + values = XCDR (current); + bervals = alloca_array (struct berval, XINT (Flength (values))); + ldap_mods[i].mod_vals.modv_bvals = + alloca_array (struct berval *, 1 + XINT (Flength (values))); + j = 0; + EXTERNAL_LIST_LOOP (values, values) + { + current = XCAR (values); + CHECK_STRING (current); + ldap_mods[i].mod_vals.modv_bvals[j] = &(bervals[j]); + GET_STRING_OS_DATA_ALLOCA (current, + bervals[j].bv_val, + bervals[j].bv_len); + j++; + } + ldap_mods[i].mod_vals.modv_bvals[j] = NULL; + i++; + } + ldap_mods_ptrs[i] = NULL; + rc = ldap_modify_s (ld, (char *) XSTRING_DATA (dn), ldap_mods_ptrs); + if (rc != LDAP_SUCCESS) + signal_ldap_error (ld, NULL, rc); + + UNGCPRO; +} + + +DEFUN ("ldap-delete", Fldap_delete, 2, 2, 0, /* +Delete an entry to an LDAP directory. +LDAP is an LDAP connection object created with `ldap-open'. +DN is the distinguished name of the entry to delete. +*/ + (ldap, dn)) +{ + LDAP *ld; + int rc; + + /* Check parameters */ + CHECK_LIVE_LDAP (ldap); + ld = XLDAP (ldap)->ld; + CHECK_STRING (dn); + + rc = ldap_delete_s (ld, (char *) XSTRING_DATA (dn)); + if (rc != LDAP_SUCCESS) + signal_ldap_error (ld, NULL, rc); +} void syms_of_eldap (void) @@ -593,13 +792,18 @@ defsymbol (&Qnever, "never"); defsymbol (&Qalways, "always"); defsymbol (&Qfind, "find"); + defsymbol (&Qadd, "add"); + defsymbol (&Qreplace, "replace"); DEFSUBR (Fldapp); DEFSUBR (Fldap_host); DEFSUBR (Fldap_status); DEFSUBR (Fldap_open); DEFSUBR (Fldap_close); - DEFSUBR (Fldap_search_internal); + DEFSUBR (Fldap_search_basic); + DEFSUBR (Fldap_add); + DEFSUBR (Fldap_modify); + DEFSUBR (Fldap_delete); } void Index: src/eldap.h =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/src/eldap.h,v retrieving revision 1.7.2.3 diff -u -u -r1.7.2.3 eldap.h --- eldap.h 1999/10/24 03:48:33 1.7.2.3 +++ eldap.h 1999/12/12 12:10:40 @@ -61,12 +61,21 @@ Lisp_Object Fldap_open (Lisp_Object host, Lisp_Object ldap_plist); Lisp_Object Fldap_close (Lisp_Object ldap); -Lisp_Object Fldap_search_internal (Lisp_Object ldap, - Lisp_Object filter, - Lisp_Object base, - Lisp_Object scope, - Lisp_Object attrs, - Lisp_Object attrsonly, - Lisp_Object withdn); +Lisp_Object Fldap_search_basic (Lisp_Object ldap, + Lisp_Object filter, + Lisp_Object base, + Lisp_Object scope, + Lisp_Object attrs, + Lisp_Object attrsonly, + Lisp_Object withdn, + Lisp_Object verbose); +Lisp_Object Fldap_add (Lisp_Object ldap, + Lisp_Object dn, + Lisp_Object entry); +Lisp_Object Fldap_modify (Lisp_Object ldap, + Lisp_Object dn, + Lisp_Object entry); +Lisp_Object Fldap_delete (Lisp_Object ldap, + Lisp_Object dn); #endif /* _XEMACS_ELDAP_H_ */ Index: lisp/ldap.el =================================================================== RCS file: /usr/CVSroot/XEmacs/xemacs/lisp/ldap.el,v retrieving revision 1.7.2.6 diff -u -u -r1.7.2.6 ldap.el --- ldap.el 1999/08/24 08:30:28 1.7.2.6 +++ ldap.el 1999/12/12 12:10:41 @@ -35,6 +35,10 @@ ;;; Code: +(eval-when '(load) + (if (not (fboundp 'ldap-open)) + (error "No LDAP support compiled in this XEmacs"))) + (defgroup ldap nil "Lightweight Directory Access Protocol" :group 'comm) @@ -145,6 +149,11 @@ (integer :tag "(number of records)"))))) :group 'ldap) +(defcustom ldap-verbose nil + "*If non-nil, LDAP operations echo progress messages." + :type 'boolean + :group 'ldap) + (defcustom ldap-ignore-attribute-codings nil "*If non-nil, do not perform any encoding/decoding on LDAP attribute values." :type 'boolean @@ -436,8 +445,17 @@ (cons name (mapcar decoder values)) attr))) +(defun ldap-search (arg1 &rest args) + "Perform an LDAP search." + (apply (if (ldapp arg1) + 'ldap-search-basic + 'ldap-search-entries) arg1 args)) + +(make-obsolete 'ldap-search + "Use `ldap-search-entries' instead or +`ldap-search-basic' for the low-level search API.") -(defun ldap-search (filter &optional host attributes attrsonly withdn) +(defun ldap-search-entries (filter &optional host attributes attrsonly withdn) "Perform an LDAP search. FILTER is the search filter in RFC1558 syntax, i.e., something that looks like \"(cn=John Smith)\". @@ -459,13 +477,16 @@ (let ((host-plist (cdr (assoc host ldap-host-parameters-alist))) ldap result) - (message "Opening LDAP connection to %s..." host) + (if ldap-verbose + (message "Opening LDAP connection to %s..." host)) (setq ldap (ldap-open host host-plist)) - (message "Searching with LDAP on %s..." host) - (setq result (ldap-search-internal ldap filter - (plist-get host-plist 'base) - (plist-get host-plist 'scope) - attributes attrsonly withdn)) + (if ldap-verbose + (message "Searching with LDAP on %s..." host)) + (setq result (ldap-search ldap filter + (plist-get host-plist 'base) + (plist-get host-plist 'scope) + attributes attrsonly withdn + ldap-verbose)) (ldap-close ldap) (if ldap-ignore-attribute-codings result @@ -473,6 +494,120 @@ (lambda (record) (mapcar 'ldap-decode-attribute record))) result)))) + +(defun ldap-add-entries (entries &optional host binddn passwd) + "Add entries to an LDAP directory. +ENTRIES is a list of entry specifications of +the form (DN (ATTR . VALUE) (ATTR . VALUE) ...) where +DN is the distinguished name of an entry to add, the following +are cons cells containing attribute/value string pairs. +HOST is the LDAP host, defaulting to `ldap-default-host' +BINDDN is the DN to bind as to the server +PASSWD is the corresponding password" + (or host + (setq host ldap-default-host) + (error "No LDAP host specified")) + (let ((host-plist (cdr (assoc host ldap-host-parameters-alist))) + ldap + (i 1)) + (if (or binddn passwd) + (setq host-plist (copy-seq host-plist))) + (if binddn + (setq host-plist (plist-put host-plist 'binddn binddn))) + (if passwd + (setq host-plist (plist-put host-plist 'passwd passwd))) + (if ldap-verbose + (message "Opening LDAP connection to %s..." host)) + (setq ldap (ldap-open host host-plist)) + (if ldap-verbose + (message "Adding LDAP entries...")) + (mapcar (function + (lambda (thisentry) + (ldap-add ldap (car thisentry) (cdr thisentry)) + (if ldap-verbose + (message "%d added" i)) + (setq i (1+ i)))) + entries) + (ldap-close ldap))) + + +(defun ldap-modify-entries (entry-mods &optional host binddn passwd) + "Modify entries of an LDAP directory. +ENTRY_MODS is a list of entry modifications of the form + (DN MOD-SPEC1 MOD-SPEC2 ...) where DN is the distinguished name of +the entry to modify, the following are modification specifications. +A modification specification is itself a list of the form +(MOD-OP ATTR VALUE1 VALUE2 ...) MOD-OP and ATTR are mandatory, +VALUEs are optional depending on MOD-OP. +MOD-OP is the type of modification, one of the symbols `add', `delete' +or `replace'. ATTR is the LDAP attribute type to modify. +HOST is the LDAP host, defaulting to `ldap-default-host' +BINDDN is the DN to bind as to the server +PASSWD is the corresponding password" + (or host + (setq host ldap-default-host) + (error "No LDAP host specified")) + (let ((host-plist (cdr (assoc host ldap-host-parameters-alist))) + ldap + (i 1)) + (if (or binddn passwd) + (setq host-plist (copy-seq host-plist))) + (if binddn + (setq host-plist (plist-put host-plist 'binddn binddn))) + (if passwd + (setq host-plist (plist-put host-plist 'passwd passwd))) + (if ldap-verbose + (message "Opening LDAP connection to %s..." host)) + (setq ldap (ldap-open host host-plist)) + (if ldap-verbose + (message "Modifying LDAP entries...")) + (mapcar (function + (lambda (thisentry) + (ldap-modify ldap (car thisentry) (cdr thisentry)) + (if ldap-verbose + (message "%d modified" i)) + (setq i (1+ i)))) + entry-mods) + (ldap-close ldap))) + + +(defun ldap-delete-entries (dn &optional host binddn passwd) + "Delete an entry from an LDAP directory. +DN is the distinguished name of an entry to delete or +a list of those. +HOST is the LDAP host, defaulting to `ldap-default-host' +BINDDN is the DN to bind as to the server +PASSWD is the corresponding password." + (or host + (setq host ldap-default-host) + (error "No LDAP host specified")) + (let ((host-plist (cdr (assoc host ldap-host-parameters-alist))) + ldap) + (if (or binddn passwd) + (setq host-plist (copy-seq host-plist))) + (if binddn + (setq host-plist (plist-put host-plist 'binddn binddn))) + (if passwd + (setq host-plist (plist-put host-plist 'passwd passwd))) + (if ldap-verbose + (message "Opening LDAP connection to %s..." host)) + (setq ldap (ldap-open host host-plist)) + (if (consp dn) + (let ((i 1)) + (if ldap-verbose + (message "Deleting LDAP entries...")) + (mapcar (function + (lambda (thisdn) + (ldap-delete ldap thisdn) + (if ldap-verbose + (message "%d deleted" i)) + (setq i (1+ i)))) + dn)) + (if ldap-verbose + (message "Deleting LDAP entry...")) + (ldap-delete ldap dn)) + (ldap-close ldap))) + (provide 'ldap)