XEmacs 21.2-b1
[chise/xemacs-chise.git-] / modules / ldap / eldap.c
diff --git a/modules/ldap/eldap.c b/modules/ldap/eldap.c
new file mode 100644 (file)
index 0000000..d902d57
--- /dev/null
@@ -0,0 +1,462 @@
+/* LDAP client interface for XEmacs.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* Author: Oscar Figueiredo */
+
+/* This file provides lisp primitives for access to an LDAP library
+   conforming to the API defined in RFC 1823.
+   It has been tested with:
+   - UMich LDAP 3.3 (http://www.umich.edu/~dirsvcs/ldap/)
+   - Netscape's LDAP SDK 1.0 (http://developer.netscape.com) */
+
+
+
+#include <config.h>
+
+#if defined (HAVE_LDAP)
+
+/* The entire file is within this conditional */
+
+#include "lisp.h"
+
+#include "eldap.h"
+#include <lber.h>
+#include <ldap.h>
+
+#ifdef HAVE_NS_LDAP
+#define HAVE_LDAP_SET_OPTION 1
+#define HAVE_LDAP_GET_ERRNO 1
+#else
+#undef HAVE_LDAP_SET_OPTION
+#undef HAVE_LDAP_GET_ERRNO
+#endif
+
+static Lisp_Object Vldap_default_base;
+static Lisp_Object Vldap_default_host;
+
+/* ldap-search-internal plist keywords */
+static Lisp_Object Qhost, Qfilter, Qattributes, Qattrsonly, Qbase, Qscope,
+  Qauth, Qbinddn, Qpasswd, Qderef, Qtimelimit, Qsizelimit;
+/* Search scope limits */
+static Lisp_Object Qbase, Qonelevel, Qsubtree;
+/* Authentication methods */
+#ifdef LDAP_AUTH_KRBV41
+static Lisp_Object Qkrbv41;
+#endif
+#ifdef LDAP_AUTH_KRBV42
+static Lisp_Object Qkrbv42;
+#endif
+/* Deref policy */
+static Lisp_Object Qnever, Qalways, Qfind;
+
+DEFUN ("ldap-search-internal", Fldap_search_internal, 1, 1, 0, /*
+Perform a search on a LDAP server.
+SEARCH-PLIST is a property list describing the search request.
+Valid keys in that list are:
+  `host' is a string naming one or more (blank separated) LDAP servers to
+to try to connect to. Each host name may optionally be of the form host:port.
+  `filter' is a filter string for the search as described in RFC 1558
+  `attributes' is a list of strings indicating which attributes to retrieve
+for each matching entry. If nil return all available attributes.
+  `attrsonly' if non-nil indicates that only the attributes are retrieved, not
+the associated values.
+  `base' is the base for the search as described in RFC 1779.
+  `scope' is one of the three symbols `subtree', `base' or `onelevel'.
+  `auth' is the authentication method to use, possible values depend on
+the LDAP library XEmacs was compiled with: `simple', `krbv41' and `krbv42'.
+  `binddn' is the distinguished name of the user to bind as (in RFC 1779 syntax).
+  `passwd' is the password to use for simple authentication.
+  `deref' is one of the symbols `never', `always', `search' or `find'.
+  `timelimit' is the timeout limit for the connection in seconds.
+  `sizelimit' is the maximum number of matches to return.
+The function returns a list of matching entries.  Each entry is itself
+an alist of attribute/values.
+*/
+       (search_plist))
+{
+ /* This function calls lisp */
+
+  /* Vars for query */
+  LDAP *ld;
+  LDAPMessage *res, *e;
+  BerElement *ptr;
+  char *a;
+  int i, rc, err;
+
+  char *ldap_host = NULL;
+  char *ldap_filter = NULL;
+  char **ldap_attributes = NULL;
+  int  ldap_attrsonly = 0;
+  char *ldap_base = NULL;
+  int  ldap_scope = LDAP_SCOPE_SUBTREE;
+  int  ldap_auth = LDAP_AUTH_SIMPLE;
+  char *ldap_binddn = NULL;
+  char *ldap_passwd = NULL;
+  int  ldap_deref = LDAP_DEREF_NEVER;
+  int  ldap_timelimit = 0;
+  int  ldap_sizelimit = 0;
+
+  char **vals = NULL;
+  int  matches;
+
+  Lisp_Object list, entry, result, keyword, value;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
+
+  list = entry = result = keyword = value = Qnil;
+  GCPRO5 (list, entry, result, keyword, value);
+
+
+  EXTERNAL_PROPERTY_LIST_LOOP(list, keyword, value, search_plist)
+    {
+      /* Host */
+      if (EQ (keyword, Qhost))
+        {
+          CHECK_STRING (value);
+          ldap_host = alloca (XSTRING_LENGTH (value) + 1);
+          strcpy (ldap_host, (char *)XSTRING_DATA (value));
+        }
+      /* Filter */
+      else if (EQ (keyword, Qfilter))
+        {
+          CHECK_STRING (value);
+          ldap_filter = alloca (XSTRING_LENGTH (value) + 1);
+          strcpy (ldap_filter, (char *)XSTRING_DATA (value));
+        }
+      /* Attributes */
+      else if (EQ (keyword, Qattributes))
+        {
+          if (! NILP (value))
+            {
+              Lisp_Object attr_left = value;
+              struct gcpro ngcpro1;
+
+              NGCPRO1 (attr_left);
+              CHECK_CONS (value);
+
+              ldap_attributes = alloca ((XINT (Flength (value)) + 1)*sizeof (char *));
+
+              for (i=0; !NILP (attr_left); i++) {
+                CHECK_STRING (XCAR (attr_left));
+                ldap_attributes[i] = alloca (XSTRING_LENGTH (XCAR (attr_left)) + 1);
+                strcpy(ldap_attributes[i],
+                       (char *)(XSTRING_DATA( XCAR (attr_left))));
+                attr_left = XCDR (attr_left);
+              }
+              ldap_attributes[i] = NULL;
+              NUNGCPRO;
+            }
+        }
+      /* Attributes Only */
+      else if (EQ (keyword, Qattrsonly))
+        {
+          CHECK_SYMBOL (value);
+          ldap_attrsonly = NILP (value) ? 0 : 1;
+        }
+      /* Base */
+      else if (EQ (keyword, Qbase))
+        {
+          if (!NILP (value))
+            {
+              CHECK_STRING (value);
+              ldap_base = alloca (XSTRING_LENGTH (value) + 1);
+              strcpy (ldap_base, (char *)XSTRING_DATA (value));
+            }
+        }
+      /* Scope */
+      else if (EQ (keyword, Qscope))
+        {
+          CHECK_SYMBOL (value);
+
+          if (EQ (value, Qbase))
+            ldap_scope = LDAP_SCOPE_BASE;
+          else if (EQ (value, Qonelevel))
+            ldap_scope = LDAP_SCOPE_ONELEVEL;
+          else if (EQ (value, Qsubtree))
+            ldap_scope = LDAP_SCOPE_SUBTREE;
+          else
+            signal_simple_error ("Invalid scope", value);
+        }
+      /* Authentication method */
+      else if (EQ (keyword, Qauth))
+        {
+          CHECK_SYMBOL (value);
+
+          if (EQ (value, Qsimple))
+            ldap_auth = LDAP_AUTH_SIMPLE;
+#ifdef LDAP_AUTH_KRBV41
+          else if (EQ (value, Qkrbv41))
+            ldap_auth = LDAP_AUTH_KRBV41;
+#endif
+#ifdef LDAP_AUTH_KRBV42
+          else if (EQ (value, Qkrbv42))
+            ldap_auth = LDAP_AUTH_KRBV42;
+#endif
+          else
+            signal_simple_error ("Invalid authentication method", value);
+        }
+      /* Bind DN */
+      else if (EQ (keyword, Qbinddn))
+        {
+          if (!NILP (value))
+            {
+              CHECK_STRING (value);
+              ldap_binddn = alloca (XSTRING_LENGTH (value) + 1);
+              strcpy (ldap_binddn, (char *)XSTRING_DATA (value));
+            }
+        }
+      /* Password */
+      else if (EQ (keyword, Qpasswd))
+        {
+          if (!NILP (value))
+            {
+              CHECK_STRING (value);
+              ldap_passwd = alloca (XSTRING_LENGTH (value) + 1);
+              strcpy (ldap_passwd, (char *)XSTRING_DATA (value));
+            }
+        }
+      /* Deref */
+      else if (EQ (keyword, Qderef))
+        {
+          CHECK_SYMBOL (value);
+          if (EQ (value, Qnever))
+            ldap_deref = LDAP_DEREF_NEVER;
+          else if (EQ (value, Qsearch))
+            ldap_deref = LDAP_DEREF_SEARCHING;
+          else if (EQ (value, Qfind))
+            ldap_deref = LDAP_DEREF_FINDING;
+          else if (EQ (value, Qalways))
+            ldap_deref = LDAP_DEREF_ALWAYS;
+          else
+            signal_simple_error ("Invalid deref value", value);
+        }
+      /* Timelimit */
+      else if (EQ (keyword, Qtimelimit))
+        {
+          if (!NILP (value))
+            {
+              CHECK_INT (value);
+              ldap_timelimit = XINT (value);
+            }
+        }
+      /* Sizelimit */
+      else if (EQ (keyword, Qsizelimit))
+        {
+          if (!NILP (value))
+            {
+              CHECK_INT (value);
+              ldap_sizelimit = XINT (value);
+            }
+        }
+    }
+
+  /* Use ldap-default-base if no default base was given */
+  if (ldap_base == NULL && !NILP (Vldap_default_base))
+    {
+      CHECK_STRING (Vldap_default_base);
+      ldap_base = alloca (XSTRING_LENGTH (Vldap_default_base) + 1);
+      strcpy (ldap_base, (char *)XSTRING_DATA (Vldap_default_base));
+    }
+
+  /* Use ldap-default-host if no host was given */
+  if (ldap_host == NULL && !NILP (Vldap_default_host))
+    {
+      CHECK_STRING (Vldap_default_host);
+      ldap_host = alloca (XSTRING_LENGTH (Vldap_default_host) + 1);
+      strcpy (ldap_host, (char *)XSTRING_DATA (Vldap_default_host));
+    }
+
+  if (ldap_filter == NULL)
+    error ("Empty search filter");
+
+  /* Garbage collect before connecting (if using UMich lib).
+     This is ugly, I know, but without this, the UMich LDAP library 3.3
+     frequently reports "Can't contact LDAP server".  I really need to
+     check what happens inside that lib. Anyway this should be harmless to
+     XEmacs and makes things work. */
+#if defined (HAVE_UMICH_LDAP)
+  garbage_collect_1 ();
+#endif
+
+  /* Connect to the server and bind */
+  message ("Connecting to %s...", ldap_host);
+  if ( (ld = ldap_open (ldap_host, LDAP_PORT)) == NULL )
+     signal_simple_error ("Failed connecting to host",
+                          build_string (ldap_host));
+
+#if HAVE_LDAP_SET_OPTION
+  if (ldap_set_option (ld, LDAP_OPT_DEREF, (void *)&ldap_deref) != LDAP_SUCCESS)
+    error ("Failed to set deref option");
+  if (ldap_set_option (ld, LDAP_OPT_TIMELIMIT, (void *)&ldap_timelimit) != LDAP_SUCCESS)
+    error ("Failed to set timelimit option");
+  if (ldap_set_option (ld, LDAP_OPT_SIZELIMIT, (void *)&ldap_sizelimit) != LDAP_SUCCESS)
+    error ("Failed to set sizelimit option");
+  if (ldap_set_option (ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON) != LDAP_SUCCESS)
+    error ("Failed to set referral option");
+#else /* HAVE_LDAP_SET_OPTION */
+  ld->ld_deref = ldap_deref;
+  ld->ld_timelimit = ldap_timelimit;
+  ld->ld_sizelimit = ldap_sizelimit;
+#ifdef LDAP_REFERRALS
+  ld->ld_options = LDAP_OPT_REFERRALS;
+#else /* LDAP_REFERRALS */
+  ld->ld_options = 0;
+#endif /* LDAP_REFERRALS */
+#endif /* HAVE_LDAP_SET_OPTION */
+
+  message ("Binding to %s...", ldap_host);
+  if ( (err = (ldap_bind_s (ld, ldap_binddn, ldap_passwd, ldap_auth ))) != LDAP_SUCCESS )
+    signal_simple_error ("Failed binding to the server",
+                         build_string (ldap_err2string (err)));
+
+  /* Perform the search */
+  message ("Searching with LDAP on %s...", ldap_host);
+  if ( ldap_search (ld, ldap_base, ldap_scope, ldap_filter,
+                      ldap_attributes, ldap_attrsonly) == -1)
+    {
+      ldap_unbind (ld);
+#if HAVE_LDAP_GET_ERRNO
+      signal_simple_error ("Error during LDAP search",
+                           build_string (ldap_err2string (ldap_get_lderrno (ld, NULL, NULL))));
+#else
+      signal_simple_error ("Error during LDAP search",
+                           build_string (ldap_err2string (ld->ld_errno)));
+#endif
+    }
+
+  /* Build the results list */
+  matches = 0;
+
+  while ( (rc = ldap_result (ld, LDAP_RES_ANY, 0, NULL, &res))
+          == LDAP_RES_SEARCH_ENTRY )
+    {
+      matches ++;
+      e = ldap_first_entry (ld, res);
+      message ("Parsing results... %d", matches);
+      entry = Qnil;
+      for (a= ldap_first_attribute (ld, e, &ptr);
+           a != NULL;
+           a= ldap_next_attribute (ld, e, ptr) )
+        {
+          list = Fcons (build_string (a), Qnil);
+          vals = ldap_get_values (ld, e, a);
+          if (vals != NULL)
+            {
+              for (i=0; vals[i]!=NULL; i++)
+                {
+                  list = Fcons (build_string (vals[i]),
+                                list);
+                }
+            }
+          entry = Fcons (Fnreverse (list),
+                         entry);
+          ldap_value_free (vals);
+        }
+      result = Fcons (Fnreverse (entry),
+                      result);
+      ldap_msgfree (res);
+    }
+
+  if (rc == -1)
+    {
+#if HAVE_LDAP_GET_ERRNO
+      signal_simple_error ("Error retrieving result",
+                           build_string (ldap_err2string (ldap_get_lderrno (ld, NULL, NULL))));
+#else
+      signal_simple_error ("Error retrieving result",
+                           build_string (ldap_err2string (ld->ld_errno)));
+#endif
+    }
+
+  if ((rc = ldap_result2error (ld, res, 0)) != LDAP_SUCCESS)
+    {
+#if HAVE_LDAP_GET_ERRNO
+      signal_simple_error ("Error on result",
+                           build_string (ldap_err2string (ldap_get_lderrno (ld, NULL, NULL))));
+#else
+      signal_simple_error ("Error on result",
+                           build_string (ldap_err2string (ld->ld_errno)));
+#endif
+    }
+
+  ldap_msgfree (res);
+  ldap_unbind (ld);
+  message ("Done.");
+
+  result = Fnreverse (result);
+  clear_message ();
+
+  UNGCPRO;
+  return result;
+}
+
+void
+syms_of (void)
+{
+  DEFSUBR(Fldap_search_internal);
+
+  defsymbol (&Qhost, "host");
+  defsymbol (&Qfilter, "filter");
+  defsymbol (&Qattributes, "attributes");
+  defsymbol (&Qattrsonly, "attrsonly");
+  defsymbol (&Qbase, "base");
+  defsymbol (&Qscope, "scope");
+  defsymbol (&Qauth, "auth");
+  defsymbol (&Qbinddn, "binddn");
+  defsymbol (&Qpasswd, "passwd");
+  defsymbol (&Qderef, "deref");
+  defsymbol (&Qtimelimit, "timelimit");
+  defsymbol (&Qsizelimit, "sizelimit");
+  defsymbol (&Qbase, "base");
+  defsymbol (&Qonelevel, "onelevel");
+  defsymbol (&Qsubtree, "subtree");
+#ifdef LDAP_AUTH_KRBV41
+  defsymbol (&Qkrbv41, "krbv41");
+#endif
+#ifdef LDAP_AUTH_KRBV42
+  defsymbol (&Qkrbv42, "krbv42");
+#endif
+  defsymbol (&Qnever, "never");
+  defsymbol (&Qalways, "always");
+  defsymbol (&Qfind, "find");
+}
+
+void
+vars_of (void)
+{
+  Fprovide (intern ("ldap-internal"));
+
+  DEFVAR_LISP ("ldap-default-host", &Vldap_default_host /*
+Default LDAP host.
+*/ );
+
+  DEFVAR_LISP ("ldap-default-base", &Vldap_default_base /*
+Default base for LDAP searches.
+This is a string using the syntax of RFC 1779.
+For instance, "o=ACME, c=US" limits the search to the
+Acme organization in the United States.
+*/ );
+
+  Vldap_default_host =  Qnil;
+  Vldap_default_base =  Qnil;
+}
+
+#endif /* HAVE_LDAP */