]> git.sur5r.net Git - openldap/commitdiff
This is the commit of:
authorPierangelo Masarati <ando@openldap.org>
Sat, 12 May 2001 00:51:28 +0000 (00:51 +0000)
committerPierangelo Masarati <ando@openldap.org>
Sat, 12 May 2001 00:51:28 +0000 (00:51 +0000)
  - librewrite, for string rewriting; it may be used in back-ldap
    by configuring with '--enable-rewrite'. It must be used in
    back-meta. There's a text file, 'libraries/librewrite/RATIONALE',
    that explains the usage and the features. More comprehensive
    documentation will follow.
  - enhancements of back-ldap (ITS#989,ITS#998,ITS#1002,ITS#1054 and ITS#1137)
    including dn rewriting, a fix to group acl matching and so
  - back-meta: a new backend that proxies a set of remote servers
    by spawning queries. It uses portions of back-ldap and the rewrite
    capabilities of librewrite. It can be compiled by configuring
    with `--enable-ldap --enable-rewrite --enable-meta'.
    There's a text file, 'servers/slapd/back-meta/Documentation', that
    describes the main features and config statements.

Note: someone (Kurt?) should run 'autoconf' and commit 'configure' as
my autoconf version must be different: my configures contain a number
of differences and I didn't feel comfortable in adding them :)

66 files changed:
configure.in
include/portable.h.in
include/rewrite.h [new file with mode: 0644]
libraries/Makefile.in
libraries/librewrite/Copyright [new file with mode: 0644]
libraries/librewrite/Makefile.in [new file with mode: 0644]
libraries/librewrite/RATIONALE [new file with mode: 0644]
libraries/librewrite/config.c [new file with mode: 0644]
libraries/librewrite/context.c [new file with mode: 0644]
libraries/librewrite/info.c [new file with mode: 0644]
libraries/librewrite/ldapmap.c [new file with mode: 0644]
libraries/librewrite/map.c [new file with mode: 0644]
libraries/librewrite/params.c [new file with mode: 0644]
libraries/librewrite/parse.c [new file with mode: 0644]
libraries/librewrite/rewrite-int.h [new file with mode: 0644]
libraries/librewrite/rewrite-map.h [new file with mode: 0644]
libraries/librewrite/rewrite.c [new file with mode: 0644]
libraries/librewrite/rule.c [new file with mode: 0644]
libraries/librewrite/session.c [new file with mode: 0644]
libraries/librewrite/subst.c [new file with mode: 0644]
libraries/librewrite/var.c [new file with mode: 0644]
servers/slapd/back-ldap/add.c
servers/slapd/back-ldap/back-ldap.h
servers/slapd/back-ldap/bind.c
servers/slapd/back-ldap/compare.c
servers/slapd/back-ldap/config.c
servers/slapd/back-ldap/delete.c
servers/slapd/back-ldap/group.c
servers/slapd/back-ldap/init.c
servers/slapd/back-ldap/modify.c
servers/slapd/back-ldap/modrdn.c
servers/slapd/back-ldap/search.c
servers/slapd/back-ldap/suffixmassage.c
servers/slapd/back-ldap/unbind.c
servers/slapd/back-meta/Changes [new file with mode: 0644]
servers/slapd/back-meta/Copyright [new file with mode: 0644]
servers/slapd/back-meta/Documentation [new file with mode: 0644]
servers/slapd/back-meta/Makefile.in [new file with mode: 0644]
servers/slapd/back-meta/TODO [new file with mode: 0644]
servers/slapd/back-meta/add.c [new file with mode: 0644]
servers/slapd/back-meta/attribute.c [new file with mode: 0644]
servers/slapd/back-meta/back-meta.h [new file with mode: 0644]
servers/slapd/back-meta/bind.c [new file with mode: 0644]
servers/slapd/back-meta/candidates.c [new file with mode: 0644]
servers/slapd/back-meta/compare.c [new file with mode: 0644]
servers/slapd/back-meta/config.c [new file with mode: 0644]
servers/slapd/back-meta/conn.c [new file with mode: 0644]
servers/slapd/back-meta/data/meta-1.ldif [new file with mode: 0644]
servers/slapd/back-meta/data/meta-2.ldif [new file with mode: 0644]
servers/slapd/back-meta/data/meta-3.ldif [new file with mode: 0644]
servers/slapd/back-meta/data/setup.sh [new file with mode: 0644]
servers/slapd/back-meta/data/slapd-ldap-raw.conf [new file with mode: 0644]
servers/slapd/back-meta/data/slapd-ldap-rewrite.conf [new file with mode: 0644]
servers/slapd/back-meta/data/slapd-ldbm.conf [new file with mode: 0644]
servers/slapd/back-meta/data/slapd-meta-plain.conf [new file with mode: 0644]
servers/slapd/back-meta/data/slapd-meta-rewrite.conf [new file with mode: 0644]
servers/slapd/back-meta/delete.c [new file with mode: 0644]
servers/slapd/back-meta/dncache.c [new file with mode: 0644]
servers/slapd/back-meta/external.h [new file with mode: 0644]
servers/slapd/back-meta/group.c [new file with mode: 0644]
servers/slapd/back-meta/init.c [new file with mode: 0644]
servers/slapd/back-meta/modify.c [new file with mode: 0644]
servers/slapd/back-meta/modrdn.c [new file with mode: 0644]
servers/slapd/back-meta/search.c [new file with mode: 0644]
servers/slapd/back-meta/unbind.c [new file with mode: 0644]
servers/slapd/backend.c

index fb30f871d70dabeeb339e397b51b4ac4a335bafd..1156691d295ce8e6b254a7f6f1dd407a619cd590 100644 (file)
@@ -116,6 +116,7 @@ OL_ARG_ENABLE(referrals,[  --enable-referrals         enable V2 Referrals (experimenta
 OL_ARG_ENABLE(kbind,[  --enable-kbind    enable V2 Kerberos IV bind (deprecated)], no)dnl
 OL_ARG_ENABLE(ipv6,[  --enable-ipv6      enable IPv6 support], auto)dnl
 OL_ARG_ENABLE(local,[  --enable-local    enable AF_LOCAL (AF_UNIX) socket support], auto)dnl
+OL_ARG_ENABLE(rewrite,[    --enable-rewrite      enable rewrite], no)dnl
 OL_ARG_ENABLE(x_compile,[  --enable-x-compile    enable cross compiling],
        no, [yes no])dnl
 
@@ -177,6 +178,9 @@ OL_ARG_WITH(ldbm_module,[    --with-ldbm-module       module type], static,
        [static dynamic])
 OL_ARG_WITH(ldbm_type,[    --with-ldbm-type      use LDBM type], auto,
        [auto btree hash])
+OL_ARG_ENABLE(meta,[    --enable-meta    enable metadirectory backend], no)dnl
+OL_ARG_WITH(meta_module,[    --with-meta-module          module type], static,
+       [static dynamic])
 OL_ARG_ENABLE(passwd,[    --enable-passwd        enable passwd backend], no)dnl
 OL_ARG_WITH(passwd_module,[    --with-passwd-module  module type], static,
        [static dynamic])
@@ -222,6 +226,9 @@ if test $ol_enable_slapd = no ; then
        if test $ol_enable_ldbm = yes ; then
                AC_MSG_WARN([slapd disabled, ignoring --enable-ldbm argument])
        fi
+       if test $ol_enable_meta = yes ; then
+               AC_MSG_WARN([slapd disabled, ignoring --enable-meta argument])
+       fi
        if test $ol_enable_passwd = yes ; then
                AC_MSG_WARN([slapd disabled, ignoring --enable-passwd argument])
        fi
@@ -273,6 +280,9 @@ dnl fi
        if test $ol_with_ldbm_module != static ; then
                AC_MSG_WARN([slapd disabled, ignoring --with-ldbm-module argument])
        fi
+       if test $ol_with_meta_module != static ; then
+               AC_MSG_WARN([slapd disabled, ignoring --with-meta-module argument])
+       fi
        if test $ol_with_passwd_module != static ; then
                AC_MSG_WARN([slapd disabled, ignoring --with-passwd-module argument])
        fi
@@ -291,12 +301,16 @@ dnl       fi
        if test $ol_enable_slurpd = yes ; then
                AC_MSG_ERROR([slurpd requires slapd])
        fi
+       if test $ol_enable_rewrite = yes ; then
+               AC_MSG_WARN([slapd disabled, ignoring --enable-rewrite argument])
+       fi
 
        # force settings to no
        ol_enable_bdb=no
        ol_enable_dnssrv=no
        ol_enable_ldap=no
        ol_enable_ldbm=no
+       ol_enable_meta=no
        ol_enable_passwd=no
        ol_enable_perl=no
        ol_enable_shell=no
@@ -318,6 +332,7 @@ dnl ol_enable_multimaster=no
        ol_with_dnssrv_module=static
        ol_with_ldap_module=static
        ol_with_ldbm_module=static
+       ol_with_meta_module=static
        ol_with_passwd_module=static
        ol_with_perl_module=static
        ol_with_shell_module=static
@@ -326,6 +341,8 @@ dnl ol_enable_multimaster=no
 
        ol_enable_slurpd=no
 
+       ol_enable_rewrite=no
+
 elif test $ol_enable_ldbm = no ; then
        dnl SLAPD without LDBM
 
@@ -345,6 +362,7 @@ elif test $ol_enable_ldbm = no ; then
                $ol_enable_bdb = no -a \
                $ol_enable_dnssrv = no -a \
                $ol_enable_ldap = no -a \
+               $ol_enable_meta = no -a \
                $ol_enable_passwd = no -a \
                $ol_enable_perl = no -a \
                $ol_enable_shell = no -a \
@@ -427,6 +445,7 @@ BUILD_BDB=no
 BUILD_DNSSRV=no
 BUILD_LDAP=no
 BUILD_LDBM=no
+BUILD_META=no
 BUILD_PASSWD=no
 BUILD_PERL=no
 BUILD_SHELL=no
@@ -437,6 +456,7 @@ BUILD_BDB_DYNAMIC=static
 BUILD_DNSSRV_DYNAMIC=static
 BUILD_LDAP_DYNAMIC=static
 BUILD_LDBM_DYNAMIC=static
+BUILD_META_DYNAMIC=static
 BUILD_PASSWD_DYNAMIC=static
 BUILD_PERL_DYNAMIC=static
 BUILD_SHELL_DYNAMIC=static
@@ -650,6 +670,7 @@ else
        ol_with_dnssrv_module=static
        ol_with_ldap_module=static
        ol_with_ldbm_module=static
+       ol_with_meta_module=static
        ol_with_passwd_module=static
        ol_with_perl_module=static
        ol_with_shell_module=static
@@ -2342,6 +2363,25 @@ if test "$ol_link_ldbm" != no ; then
        fi
 fi
 
+if test "$ol_enable_meta" != no ; then
+       if test "$ol_enable_ldap" = no ; then
+               AC_MSG_ERROR([need --enable-ldap to use --enable-meta])
+       fi
+       if test "$ol_enable_rewrite" = no ; then
+               AC_MSG_ERROR([need --enable-rewrite to use --enable-meta])
+       fi
+       AC_DEFINE(SLAPD_META,1,[define to support LDAP Metadirectory backend])
+       BUILD_SLAPD=yes
+       BUILD_META=yes
+       if test "$ol_with_meta_module" != static ; then
+               AC_DEFINE(SLAPD_META_DYNAMIC,1,
+                       [define to support dynamic LDAP Metadirectory backend])
+               BUILD_META=mod
+               BUILD_META_DYNAMIC=shared
+               SLAPD_MODULES_LIST="$SLAPD_MODULES_LIST -dlopen \$(SLAP_DIR)back-meta/back_meta.la"
+       fi
+fi
+
 if test "$ol_enable_passwd" != no ; then
        AC_DEFINE(SLAPD_PASSWD,1,[define to support PASSWD backend])
        BUILD_SLAPD=yes
@@ -2412,6 +2452,12 @@ if test "$ol_enable_slurpd" != no -a "$ol_link_threads" != no -a \
        BUILD_SLURPD=yes
 fi
 
+if test "$ol_enable_rewrite" != no ; then
+       AC_DEFINE(ENABLE_REWRITE,1,[define to enable rewriting in back-ldap and back-meta])
+       BUILD_REWRITE=yes
+       SLAPD_LIBS="$SLAPD_LIBS -lrewrite"
+fi
+
 dnl ----------------------------------------------------------------
 
 if test "$LINK_BINS_DYNAMIC" = yes; then
@@ -2442,6 +2488,7 @@ AC_SUBST(BUILD_SLAPD)
   AC_SUBST(BUILD_DNSSRV)
   AC_SUBST(BUILD_LDAP)
   AC_SUBST(BUILD_LDBM)
+  AC_SUBST(BUILD_META)
   AC_SUBST(BUILD_PASSWD)
   AC_SUBST(BUILD_PERL)
   AC_SUBST(BUILD_SHELL)
@@ -2451,6 +2498,7 @@ AC_SUBST(BUILD_SLAPD)
   AC_SUBST(BUILD_DNSSRV_DYNAMIC)
   AC_SUBST(BUILD_LDAP_DYNAMIC)
   AC_SUBST(BUILD_LDBM_DYNAMIC)
+  AC_SUBST(BUILD_META_DYNAMIC)
   AC_SUBST(BUILD_PASSWD_DYNAMIC)
   AC_SUBST(BUILD_PERL_DYNAMIC)
   AC_SUBST(BUILD_SHELL_DYNAMIC)
@@ -2523,12 +2571,14 @@ libraries/libldbm/Makefile:build/top.mk:libraries/libldbm/Makefile.in:build/lib.
 libraries/libldif/Makefile:build/top.mk:libraries/libldif/Makefile.in:build/lib.mk:build/lib-static.mk \
 libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk \
 libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk       \
+libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk    \
 servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk \
 servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk \
 servers/slapd/back-bdb/Makefile:build/top.mk:servers/slapd/back-bdb/Makefile.in:build/mod.mk \
 servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk \
 servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk \
 servers/slapd/back-ldbm/Makefile:build/top.mk:servers/slapd/back-ldbm/Makefile.in:build/mod.mk \
+servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk \
 servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk \
 servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk \
 servers/slapd/back-shell/Makefile:build/top.mk:servers/slapd/back-shell/Makefile.in:build/mod.mk \
index 0b1952aa2b03b506985f3055d2948236b22d1ee2..70f29290fcbdc99aaef5b622411dfcfa6c35e9a0 100644 (file)
 /* define to support dynamic LDBM backend */
 #undef SLAPD_LDBM_DYNAMIC
 
+/* define to support LDAP Metadirectory backend */
+#undef SLAPD_META
+
+/* define to support dynamic LDAP Metadirectory backend */
+#undef SLAPD_META_DYNAMIC
+
 /* define to support PASSWD backend */
 #undef SLAPD_PASSWD
 
 /* define to support dynamic SQL backend */
 #undef SLAPD_SQL_DYNAMIC
 
+/* define to enable rewriting in back-ldap and back-meta */
+#undef ENABLE_REWRITE
+
 
 /* begin of postamble */
 
diff --git a/include/rewrite.h b/include/rewrite.h
new file mode 100644 (file)
index 0000000..58315bc
--- /dev/null
@@ -0,0 +1,222 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#ifndef REWRITE_H
+#define REWRITE_H
+
+LDAP_BEGIN_DECL
+
+/*
+ * Default rewrite context
+ */
+#define REWRITE_DEFAULT_CONTEXT                "default"
+
+/*
+ * Rewrite engine states
+ */
+#define REWRITE_OFF                    0x0000
+#define REWRITE_ON                     0x0001
+#define REWRITE_DEFAULT                        REWRITE_OFF
+
+/*
+ * Rewrite internal status returns
+ */
+#define REWRITE_SUCCESS                        LDAP_SUCCESS
+#define REWRITE_ERR                    LDAP_OPERATIONS_ERROR
+#define REWRITE_NO_SUCH_OBJECT         LDAP_NO_SUCH_OBJECT
+
+/*
+ * Rewrite modes (input values for rewrite_info_init); determine the
+ * behavior in case a null or non existent context is required:
+ * 
+ *     REWRITE_MODE_ERR                error
+ *     REWRITE_MODE_OK                 no error but no rewrite
+ *     REWRITE_MODE_COPY_INPUT         a copy of the input is returned
+ *     REWRITE_MODE_USE_DEFAULT        the default context is used.
+ */
+#define REWRITE_MODE_ERR               0x0010
+#define REWRITE_MODE_OK                        0x0011
+#define REWRITE_MODE_COPY_INPUT                0x0012
+#define REWRITE_MODE_USE_DEFAULT       0x0013
+
+/*
+ * Rewrite status returns
+ * 
+ *     REWRITE_REGEXEC_OK              success (result may be empty in case
+ *                                     of no match)
+ *     REWRITE_REGEXEC_ERR             error (internal error,
+ *                                     misconfiguration, map not working ...)
+ *     REWRITE_REGEXEC_STOP            internal use; never returned
+ *     REWRITE_REGEXEC_UNWILLING       the server should issue an 'unwilling
+ *                                     to perform' error
+ */
+#define REWRITE_REGEXEC_OK              0x0000
+#define REWRITE_REGEXEC_ERR             0x0001
+#define REWRITE_REGEXEC_STOP            0x0002
+#define REWRITE_REGEXEC_UNWILLING       0x0004
+
+/*
+ * Rewrite info
+ */
+struct rewrite_info;
+struct berval;
+
+/*
+ * Inits the info
+ */
+extern struct rewrite_info *
+rewrite_info_init(
+               int mode
+);
+
+/*
+ * Cleans up the info structure
+ */
+extern int
+rewrite_info_delete(
+                struct rewrite_info *info
+);
+
+
+/*
+ * Parses a config line and takes actions to fit content in rewrite structure;
+ * lines handled are of the form:
+ *
+ *      rewriteEngine          {on|off}
+ *      rewriteMaxPasses       numPasses
+ *      rewriteContext                 contextName [alias aliasedRewriteContex]
+ *      rewriteRule            pattern substPattern [ruleFlags]
+ *      rewriteMap             mapType mapName [mapArgs]
+ *      rewriteParam           paramName paramValue
+ */
+extern int
+rewrite_parse(
+               struct rewrite_info *info,
+                const char *fname,
+                int lineno,
+                int argc,
+                char **argv
+);
+
+/*
+ * Rewrites a string according to context.
+ * If the engine is off, OK is returned, but the return string will be NULL.
+ * In case of 'unwilling to perform', UNWILLING is returned, and the
+ * return string will also be null. The same in case of error.
+ * Otherwise, OK is returned, and result will hold a newly allocated string
+ * with the rewriting.
+ *
+ * What to do in case of non-existing rewrite context is still an issue.
+ * Four possibilities:
+ *      - error, 
+ *      - ok with NULL result, 
+ *      - ok with copy of string as result,
+ *      - use the default rewrite context.
+ */
+extern int
+rewrite(
+               struct rewrite_info *info,
+               const char *rewriteContext,
+               const char *string,
+               char **result
+);
+
+/*
+ * Same as above; the cookie relates the rewrite to a session
+ */
+extern int
+rewrite_session(
+               struct rewrite_info *info,
+               const char *rewriteContext,
+               const char *string,
+               const void *cookie,
+               char **result
+);
+
+/*
+ * Inits a session
+ */
+extern struct rewrite_session *
+rewrite_session_init(
+                struct rewrite_info *info,
+                const void *cookie
+);
+
+/*
+ * Defines and inits a variable with session scope
+ */
+extern int
+rewrite_session_var_set(
+               struct rewrite_info *info,
+               const void *cookie,
+               const char *name,
+               const char *value
+);
+
+/*
+ * Deletes a session
+ */
+extern int
+rewrite_session_delete(
+               struct rewrite_info *info,
+               const void *cookie
+);
+
+
+/*
+ * Params
+ */
+
+/*
+ * Defines and inits a variable with global scope
+ */
+extern int
+rewrite_param_set(
+                struct rewrite_info *info,
+                const char *name,
+                const char *value
+);
+
+/*
+ * Gets a var with global scope
+ */
+extern int
+rewrite_param_get(
+                struct rewrite_info *info,
+                const char *name,
+                struct berval *value
+);
+
+/* 
+ * Destroys the parameter tree
+ */
+extern int
+rewrite_param_destroy(
+                struct rewrite_info *info
+);
+
+LDAP_END_DECL
+
+#endif /* REWRITE_H */
+
index 9d22aafffdb6438f4696dc598b5d515e4267f645..b8f9ab8aa925a6ba4789a085c4d71d567c7a2430 100644 (file)
@@ -6,5 +6,5 @@
 
 SUBDIRS= liblunicode liblutil libldif \
        liblber libldap libldap_r \
-       libavl libldbm
+       libavl libldbm librewrite
 
diff --git a/libraries/librewrite/Copyright b/libraries/librewrite/Copyright
new file mode 100644 (file)
index 0000000..64a25f5
--- /dev/null
@@ -0,0 +1,23 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
diff --git a/libraries/librewrite/Makefile.in b/libraries/librewrite/Makefile.in
new file mode 100644 (file)
index 0000000..5cd92cd
--- /dev/null
@@ -0,0 +1,27 @@
+# $OpenLDAP$
+## Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
+## COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+##
+## LIBREWRITE
+##
+## Copyright 2000-2001 Pierangelo Masarati <ando@sys-net.it>
+##
+
+SRCS = config.c context.c info.c ldapmap.c map.c params.c rule.c \
+       session.c subst.c var.c \
+       parse.c rewrite.c
+XSRCS = version.c
+OBJS = config.o context.o info.o ldapmap.o map.o params.o rule.o \
+       session.o subst.o var.o
+
+LDAP_INCDIR= ../../include       
+LDAP_LIBDIR= ../../libraries
+
+LIBRARY = librewrite.a
+PROGRAMS       = rewrite
+XLIBS = -lrewrite -lavl -llutil -lldap_r -llber
+XXLIBS  = $(SECURITY_LIBS) $(LDIF_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+rewrite:       $(LIBRARY) rewrite.o parse.o
+       $(LTLINK) -o $@ rewrite.o parse.o $(LIBS)
diff --git a/libraries/librewrite/RATIONALE b/libraries/librewrite/RATIONALE
new file mode 100644 (file)
index 0000000..f059bd7
--- /dev/null
@@ -0,0 +1,397 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+/*
+ * Description
+ *
+ *      A string is rewritten according to a set of rules, called
+ *     a `rewrite context'.
+ *      The rules are based on Regular Expressions (POSIX regex) with
+ *      substring matching; extensions are planned to allow basic variable
+ *      substitution and map resolution of substrings.
+ *      The behavior of pattern matching/substitution can be altered by a
+ *      set of flags.
+ *
+ *      The underlying concept is to build a lightweight rewrite module
+ *      for the slapd server (initially dedicated to the back-ldap module).
+ *
+ *
+ * Passes
+ *
+ *      An incoming string is matched agains a set of rules. Rules are made
+ *      of a match pattern, a substitution pattern and a set of actions.
+ *     In case of match a string rewriting is performed according to the
+ *     substitution pattern that allows to refer to substrings matched
+ *     in the incoming string. The actions, if any, are finally performed.
+ *     The substitution pattern allows map resolution of substrings.
+ *     A map is a generic object that maps a substitution pattern to a
+ *     value.
+ *
+ *
+ * Pattern Matching Flags
+ *
+ *      'C'     honors case in matching (default is case insensitive)
+ *      'R'     use POSIX Basic Regular Expressions (default is Extended)
+ *
+ *
+ * Action Flags
+ *
+ *      ':'     apply the rule once only (default is recursive)
+ *      '@'     stop applying rules in case of match.
+ *      '#'     stop current operation if the rule matches, and issue an
+ *              `unwilling to perform' error.
+ *      'G{n}'  jump n rules back and forth (watch for loops!). Note that
+ *             'G{1}' is implicit in every rule.
+ *      'I'    ignores errors in rule; this means, in case of error, e.g.
+ *             issued by a map, the error is treated as a missed match.
+ *             The 'unwilling to perform' is not overridden.
+ *
+ *     the ordering of the flags is significant. For instance:
+ *
+ *     'IG{2}' means ignore errors and jump two lines ahead both in case
+ *             of match and in case of error, while
+ *     'G{2}I' means ignore errors, but jump thwo lines ahead only in case
+ *             of match.
+ *
+ *     More flags (mainly Action Flags) will be added as needed.
+ *
+ *
+ * Pattern matching: 
+ *
+ *      see regex(7)
+ *
+ *
+ * String Substitution:
+ *
+ *      the string substitution happens according to a substitution pattern.
+ *      -       susbtring substitution is allowed with the syntax '\d'
+ *              where 'd' is a digit ranging 0-9 (0 is the full match).
+ *             I see that 0-9 digit expansion is a widely accepted
+ *             practise; however there is no technical reason to use
+ *             such a strict limit. A syntax of the form '\{ddd}'
+ *             should be fine if there is any need to use a higher
+ *             number of possible submatches.
+ *      -       variable substitution will be allowed (at least when I
+ *              figure out which kind of variable could be proficiently
+ *              substituted)
+ *      -       map lookup will be allowed (map lookup of substring matches
+ *              in gdbm, ldap(!), math(?) and so on maps 'a la sendmail'.
+ *      -       subroutine invocation will make it possible to rewrite a
+ *              submatch in terms of the output of another rewriteContext
+ *
+ *     Old syntax:
+ *
+ *             '\' {0-9} [ '{' <name> [ '(' <args> ')' ] '}' ]
+ *
+ *             where <name> is the name of a built-in map, and
+ *             <args> are optional arguments to the map, if
+ *             the map <name> requires them.
+ *             The following experimental maps have been implemented:
+ *
+ *     \n{xpasswd}
+ *                     maps the n-th substring match as uid to 
+ *                     the gecos field in /etc/passwd;
+ *
+ *     \n{xfile(/absolute/path)}
+ *                     maps the n-th substring match 
+ *                     to a 'key value' style plain text file.
+ *
+ *     \n{xldap(ldap://url/with?%0?in?filter)
+ *                     maps the n-th substring match to an
+ *                     attribute retrieved by means of an LDAP
+ *                     url with substitution of %0 in the filter
+ *                     (NOT IMPL.)
+ *
+ *     New scheme:
+ *
+ *     -       everything starting with '\' requires substitution;
+ *     -       the only obvious exception is '\\', which is left as is;
+ *     -       the basic substitution is '\d', where 'd' is a digit;
+ *             0 means the whole string, while 1-9 is a submatch;
+ *     -       in the outdated schema, the digit may be optionally
+ *             followed by a '{', which means pipe the submatch into
+ *             the map described by the string up to the following '}';
+ *     -       the output of the map is used instead of the submatch;
+ *     -       in the new schema, a '\' followed by a '{' invokes an
+ *             advanced substitution scheme. The pattern is:
+ *
+ *             '\' '{' [{ <op> }] <name> '(' <substitution schema> ')' '}'
+ *
+ *             where <name> must be a legal name for the map, i.e.
+ *             
+ *             <name> ::= [a-z][a-z0-9]* (case insensitive)
+ *             <op> ::= '>' '|' '&' '&&' '*' '**' '$'
+ *
+ *             and <substitution schema> must be a legal substitution
+ *             schema, with no limits on the nesting level.
+ *             The operators are:
+ *             >       sub context invocation; <name> must be a legal,
+ *                     already defined rewrite context name
+ *             |       external command invocation; <name> must refer
+ *                     to a legal, already defined command name (NOT IMPL.)
+ *             &       variable assignment; <name> defines a variable
+ *                     in the running operation structure which can be
+ *                     dereferenced later (NOT IMPL.)
+ *             *       variable dereferencing; <name> must refer to a
+ *                     variable that is defined and assigned for the
+ *                     running operation (NOT IMPL.)
+ *             $       parameter dereferencing; <name> must refer to
+ *                     an existing parameter; the idea is to make
+ *                     some run-time parameters set by the system
+ *                     available to the rewrite engine, as the client
+ *                     host name, the bind dn if any, constant
+ *                     parameters initialized at config time, and so
+ *                     on (NOT IMPL.)
+ *
+ *     Note: as the slapd parsing routines escape backslashes ('\'),
+ *     a double backslash is required inside substitution patterns.
+ *     To overcome the resulting heavy notation, the substitution escaping
+ *     has been delegated to the '%' symbol, which should be used 
+ *     instead of '\' in string substitution patterns. The symbol can
+ *     be altered at will by redefining the related macro in "rewrite-int.h".
+ *     In the current snapshot, all the '\' on the left side of each rule
+ *     (the regex pattern) must be converted in '\\'; all the '\' on the
+ *     right side of the rule (the substitution pattern) must be turned
+ *     into '%'. In the following examples, the original (more readable)
+ *     syntax is used; however, in the servers/slapd/back-ldap/slapd.conf
+ *     example file, the working syntax is used.
+ *
+ *
+ *
+ * Rewrite context:
+ *
+ *     a rewrite context is a set of rules which are applied in sequence.
+ *     The basic idea is to have an application initialize a rewrite
+ *     engine (think of Apache's mod_rewrite ...) with a set of rewrite
+ *     contexts; when string rewriting is required, one invokes the
+ *     appropriate rewrite context with the input string and obtains the
+ *     newly rewritten one if no errors occur.
+ *     
+ *     An interesting application, in back-ldap or in slapd itself,
+ *     could associate each basic server operation to a rewrite context
+ *     (most of them possibly aliasing the default one). Then, DN rewriting
+ +     could take place at any invocation of a backend operation.
+ *
+ *     client -> server:
+ *             default         if defined and no specific context is available
+ *             bindDn          bind
+ *             searchBase      search
+ *             searchFilter    search
+ *             compareDn       compare
+ *             addDn           add
+ *             modifyDn        modify
+ *             modrDn          modrdn
+ *             newSuperiorDn   modrdn
+ *             deleteDn        delete
+ *
+ *     server -> client:
+ *             searchResult    search (only if defined; no default)
+ *
+ *
+ * Configuration syntax:
+ *
+ *             Basics:
+ *
+ *     rewriteEngine   { on | off }
+ *
+ *     rewriteContext  <context name> [ alias <aliased context name> ]
+ *
+ *     rewriteRule     <regex pattern> <substitution pattern> [ <flags> ]
+ *
+ *
+ *             Additional:
+ *
+ *     rewriteMap      <map name> <map type> [ <map attrs> ]
+ *
+ *     rewriteParam    <param name> <param value>
+ *
+ *     rewriteMaxPasses <number of passes>
+ *
+ *
+ *
+ *     rewriteEngine:
+ *
+ *     if 'on', the requested rewriting is performed; if 'off', no
+ *     rewriting takes place (an easy way to stop rewriting without
+ *     altering too much the configuration file)
+ *
+ *     rewriteContext:
+ *
+ *     <context name> is the name that identifies the context, i.e.
+ *     the name used by the application to refer to the set of rules
+ *     it contains. It is used also to reference sub contexts in
+ *     string rewriting. A context may aliase another one. In this
+ *     case the alias context contains no rule, and any reference to
+ *     it will result in accessing the aliased one.
+ *
+ *     rewriteRule:
+ *
+ *     determines how a tring can be rewritten if a pattern is matched.
+ *     Examples are reported below.
+ *
+ *     rewriteMap:
+ *
+ *     allows to define a map that transforms substring rewriting into
+ *     something else. The map is referenced inside the substitution
+ *     pattern of a rule.
+ *
+ *     rewriteParam:
+ *
+ *     sets a value with global scope, that can be dereferenced by the
+ *     command '\{$paramName}'.
+ *
+ *     rewriteMaxPasses:
+ *
+ *     sets the maximum number of total rewriting passes taht can be
+ *     performed in a signle rewriting operation (to avoid loops).
+ *
+ *
+ * Configuration examples:
+ *
+ *     # set to 'off' to disable rewriting
+ *
+ *     rewriteEngine   on
+ *
+ *
+ *     # everything defined here goes into the 'default' context
+ *     # this rule changes the naming context of anything sent to
+ *     # 'dc=home,dc=net' to 'dc=OpenLDAP, dc=org'
+ *
+ *     rewriteRule     "(.*)dc=home,[ ]?dc=net" "\1dc=OpenLDAP, dc=org" ":"
+ *
+ *
+ *     # start a new context (ends input of the previous one)
+ *     # this rule adds blancs between dn parts if not present.
+ *
+ *     rewriteContext  addBlancs
+ *     rewriteRule     "(.*),([^ ].*)" "\1, \2"
+ *
+ *
+ *     # this one eats blancs
+ *
+ *     rewriteContext  eatBlancs
+ *     rewriteRule     "(.*),[ ](.*)" "\1,\2"
+ *
+ *
+ *     # here control goes back to the default rewrite context; rules are
+ *     # appended to the existing ones.
+ *     # anything that gets here is piped into rule 'addBlancs'
+ *
+ *     rewriteContext  default
+ *     rewriteRule     ".*" "\{>addBlancs(\0)}" ":"
+ *
+ *
+ *     # anything with 'uid=username' gets looked up in /etc/passwd for
+ *     # gecos (I know it's nearly useless, but it is there just to
+ *     # test something fancy!). Note the 'I' flag that leaves
+ *     # 'uid=username' in place if 'username' does not have a valid
+ *     # account. Note also the ':' that forces the rule to be processed
+ *     # exactly once.
+ *
+ *     rewriteContext  uid2Gecos
+ *     rewriteRule     "(.*)uid=([a-z0-9]+),(.+)" "\1cn=\2{xpasswd},\3" "I:"
+ *
+ *
+ *     # finally, in case of bind, if one uses a 'uid=username' dn,
+ *     # it is rewritten in 'cn=name surname' if possible.
+ *
+ *     rewriteContext  bindDn
+ *     rewriteRule     ".*" "\{>addBlancs(\{>uid2Gecos(\0)})}" ":"
+ *
+ *
+ *     # the search base is rewritten according to 'default' rules
+ *
+ *     rewriteContext  searchBase alias default
+ *
+ *
+ *     # search results with OpenLDAP dn are rewritten back with
+ *     # 'dc=home,dc=net' naming context, with spaces eaten.
+ *
+ *     rewriteContext  searchResult
+ *     rewriteRule     "(.*[^ ]?)[ ]?dc=OpenLDAP,[ ]?dc=org" 
+ *             "\{>eatBlancs(\1)}dc=home,dc=net" ":"
+ *
+ *     # bind with email instead of full dn: we first need an ldap map
+ *     # that turns attributes into a dn (the filter is provided by the
+ *     # substitution string):
+ *
+ *     rewriteMap      ldap attr2dn "ldap://host/dc=my,dc=org?dn?sub"
+ *     
+ *     # then we need to detect emails; note that the rule in case of match
+ *     # stops rewriting; in case of error, it is ignored.
+ *     # In case we are mapping virtual to real naming contexts, we also
+ *     # need to rewrite regular dns, because the definition of a bindDn
+ *     # rewrite context overrides the default definition.
+ *
+ *     rewriteContext bindDn
+ *     rewriteRule     "(mail=[^,]+@[^,]+)" "\{attr2dn(\1)}" "@I"
+ *
+ *     # This is a rather sophisticate example. It massages a search filter
+ *     # in case who performs the search has administrative privileges.
+ *     # First we need to keep track of the bind dn of the incoming request:
+ *
+ *     rewriteContext  bindDn
+ *     rewriteRule     ".+" "\{**&binddn(\0)}" ":"
+ *
+ *     # a search filter containing 'uid=' is rewritten only if an
+ *     # appropriate dn is bound.
+ *     # to do this, in the first rule the bound dn is dereferenced, while
+ *     # the filter is decomposed in a prefix, the argument of the 'uid=',
+ *     # and in a suffix. A tag '<>' is appended to the dn. If the dn 
+ *     # refers to an entry in the 'ou=admin' subtree, the filter is
+ *     # rewritten OR-ing the 'uid=<arg>' with 'cn=<arg>'; otherwise
+ *     # it is left as is. This could be useful, for instance, to allow
+ *     # apache's auth_ldap-1.4 module to authenticate users with both
+ *     # 'uid' and 'cn', but only if the request comes from a possible
+ *     # 'dn: cn=Web auth, ou=admin, dc=home, dc=net' user.
+ *
+ *     rewriteContext  searchFilter
+ *     rewriteRule     "(.*\()uid=([a-z0-9_]+)(\).*)"
+ *             "\{**binddn}<>\{&prefix(\1)}\{&arg(\2)}\{&suffix(\3)}" ":I"
+ *     rewriteRule     "[^,]+,[ ]?ou=admin,[ ]?dc=home,[ ]?dc=net"
+ *             "\{*prefix}|(uid=\{*arg})(cn=\{*arg})\{*suffix}" "@I"
+ *     rewriteRule     ".*<>" "\{*prefix}uid=\{*arg}\{*suffix}"
+ *
+ *
+ * LDAP Proxy resolution (a possible evolution of the back-ldap):
+ *
+ *     in case the rewritten dn is an LDAP URL, the operation is initiated
+ *     towards the host[:port] indicated in the url, if it does not refer
+ *     to the local server.
+ *
+ *     e.g.:
+ *
+ *     rewriteRule     '^cn=root,.*'   '\0'                            'G{3}'
+ *     rewriteRule     '^cn=[a-l].*'   'ldap://ldap1.my.org/\0'        '@'
+ *     rewriteRule     '^cn=[m-z].*'   'ldap://ldap2.my.org/\0'        '@'
+ *     rewriteRule     '.*'            'ldap://ldap3.my.org/\0'        '@'
+ *
+ *     (rule 1 is simply there to illustrate the 'G{n}' action; it could
+ *     have been written:
+ *
+ *     rewriteRule     '^cn=root,.*'   'ldap://ldap3.my.org/\0'        '@'
+ *
+ *     with the advantage of saving one rewrite pass ...)
+ */
+
diff --git a/libraries/librewrite/config.c b/libraries/librewrite/config.c
new file mode 100644 (file)
index 0000000..437d475
--- /dev/null
@@ -0,0 +1,412 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * Global data
+ */
+extern struct rewrite_context *__curr_context;
+
+/*
+ * Parses a plugin map
+ */
+static int
+rewrite_parse_builtin_map(
+               struct rewrite_info *info,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+);
+
+/*
+ * Parses a config line and takes actions to fit content in rewrite structure;
+ * lines handled are of the form:
+ *
+ *      rewriteEngine          {on|off}
+ *      rewriteMaxPasses        numPasses
+ *      rewriteContext                 contextName [alias aliasedContextName]
+ *      rewriteRule            pattern substPattern [ruleFlags]
+ *      rewriteMap             mapType mapName [mapArgs]
+ *      rewriteParam           paramName paramValue
+ */
+int
+rewrite_parse(
+               struct rewrite_info *info,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+)
+{
+       int rc = -1;
+
+       assert( info != NULL );
+       assert( fname != NULL );
+       assert( argv != NULL );
+       assert( argc > 0 );
+       
+       /*
+        * Switch on the rewrite engine
+        */
+       if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteEngine needs 'state'\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               } else if ( argc > 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] extra fields in rewriteEngine"
+                                       " will be discarded\n%s",
+                                       fname, lineno, "" );
+               }
+
+               if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) {
+                       info->li_state = REWRITE_ON;
+               } else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) {
+                       info->li_state = REWRITE_OFF;
+               } else {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] unknown 'state' in rewriteEngine;"
+                                       " assuming 'on'\n%s",
+                                       fname, lineno, "" );
+                       info->li_state = REWRITE_ON;
+               }
+               rc = REWRITE_SUCCESS;
+       
+       /*
+        * Alter max passes
+        */
+       } else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteMaxPasses needs 'value'\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               }
+               info->li_max_passes = atoi( argv[ 1 ] );
+               rc = REWRITE_SUCCESS;
+       
+       /*
+        * Start a new rewrite context and set current context
+        */
+       } else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteContext needs 'name'\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               } 
+
+               /*
+                * Checks for existence (lots of contexts should be
+                * available by default ...)
+                */
+                __curr_context = rewrite_context_find( info, argv[ 1 ] );
+                if ( __curr_context == NULL ) {
+                        __curr_context = rewrite_context_create( info,
+                                        argv[ 1 ] );                       
+                }
+                if ( __curr_context == NULL ) {
+                        return -1;
+                }
+                                               
+                if ( argc > 2 ) {
+
+                        /*
+                         * A context can alias another (e.g., the `builtin'
+                         * contexts for backend operations, if not defined,
+                         * alias the `default' rewrite context (with the
+                         * notable exception of the searchResult context,
+                         * which can be undefined)
+                         */
+                        if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) {
+                                struct rewrite_context *aliased;
+                                
+                                if ( argc == 3 ) {
+                                        Debug( LDAP_DEBUG_ANY,
+                                                        "[%s:%d] rewriteContext"
+                                                        " needs 'name' after"
+                                                        " 'alias'\n%s",
+                                                        fname, lineno, "" );
+                                        return -1;
+                                } else if ( argc > 4 ) {
+                                        Debug( LDAP_DEBUG_ANY,
+                                                        "[%s:%d] extra fields in"
+                                                        " rewriteContext"
+                                                        " after aliased name"
+                                                        " will be"
+                                                        " discarded\n%s",
+                                                        fname, lineno, "" );
+                                }
+                                
+                                aliased = rewrite_context_find( info, 
+                                                argv[ 3 ] );
+                                if ( aliased == NULL ) {
+                                        Debug( LDAP_DEBUG_ANY,
+                                                        "[%s:%d] aliased"
+                                                        " rewriteContext '%s'"
+                                                        " does not exists\n",
+                                                        fname, lineno,
+                                                        argv[ 3 ] );
+                                        return -1;
+                                }
+                                
+                                __curr_context->lc_alias = aliased;
+                                __curr_context = aliased;
+                        } else {
+                                Debug( LDAP_DEBUG_ANY,
+                                                "[%s:%d] extra fields"
+                                                " in rewriteContext"
+                                                " will be discarded\n%s",
+                                                fname, lineno, "" );
+                        }
+                }
+                rc = REWRITE_SUCCESS;
+                
+       /*
+        * Compile a rule in current context
+        */
+       } else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) {
+               if ( argc < 3 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteRule needs 'pattern'"
+                                       " 'subst' ['flags']\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               } else if ( argc > 4 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] extra fields in rewriteRule"
+                                       " will be discarded\n%s",
+                                       fname, lineno, "" );
+               }
+
+               if ( __curr_context == NULL ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteRule outside a"
+                                       " context; will add to default\n%s",
+                                       fname, lineno, "" );
+                       __curr_context = rewrite_context_find( info,
+                                       REWRITE_DEFAULT_CONTEXT );
+
+                       /*
+                        * Default context MUST exist in a properly initialized
+                        * struct rewrite_info
+                        */
+                       assert( __curr_context != NULL );
+               }
+               
+               rc = rewrite_rule_compile( info, __curr_context, argv[ 1 ],
+                               argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) );
+       
+       /*
+        * Add a plugin map to the map tree
+        */
+       } else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) {
+               if ( argc < 3 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteMap needs at least 'type'"
+                                       " and 'name' ['args']\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               }
+
+               rc = rewrite_parse_builtin_map( info, fname, lineno,
+                               argc, argv );
+
+       /*
+        * Set the value of a global scope parameter
+        */
+       } else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) {
+               if ( argc < 3 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                       "[%s:%d] rewriteParam needs 'name'"
+                                       " and 'value'\n%s",
+                                       fname, lineno, "" );
+                       return -1;
+               }
+
+               rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] );
+               
+       /*
+        * Error
+        */
+       } else {
+               Debug( LDAP_DEBUG_ANY,
+                               "[%s:%d] unknown command '%s'\n",
+                               fname, lineno, "" );
+               return -1;
+       }
+
+       return rc;
+}
+
+/*
+ * Compares two maps
+ */
+static int
+rewrite_builtin_map_cmp(
+               const void *c1,
+                const void *c2
+)
+{
+       const struct rewrite_builtin_map *m1, *m2;
+
+        m1 = ( struct rewrite_builtin_map * )c1;
+        m2 = ( struct rewrite_builtin_map * )c2;
+
+        assert( m1 != NULL );
+        assert( m2 != NULL );
+        assert( m1->lb_name != NULL );
+        assert( m2->lb_name != NULL );
+
+        return strcasecmp( m1->lb_name, m2->lb_name );
+}
+
+/*
+ * Duplicate map ?
+ */
+static int
+rewrite_builtin_map_dup(
+                       void *c1,
+                       void *c2
+)
+{
+        struct rewrite_builtin_map *m1, *m2;
+
+        m1 = ( struct rewrite_builtin_map * )c1;
+        m2 = ( struct rewrite_builtin_map * )c2;
+
+        assert( m1 != NULL );
+        assert( m2 != NULL );
+        assert( m1->lb_name != NULL );
+        assert( m2->lb_name != NULL );
+
+        return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 );
+}
+
+/*
+ * Adds a map to the info map tree
+ */
+static int
+rewrite_builtin_map_insert(
+               struct rewrite_info *info,
+               struct rewrite_builtin_map *map
+)
+{
+       /*
+        * May need a mutex?
+        */
+       return avl_insert( &info->li_maps, ( caddr_t )map,
+                       rewrite_builtin_map_cmp,
+                       rewrite_builtin_map_dup );
+}
+
+/*
+ * Retrieves a map
+ */
+struct rewrite_builtin_map *
+rewrite_builtin_map_find(
+               struct rewrite_info *info,
+               const char *name
+)
+{
+       struct rewrite_builtin_map tmp;
+
+       assert( info != NULL );
+       assert( name != NULL );
+
+       tmp.lb_name = ( char * )name;
+
+       return ( struct rewrite_builtin_map * )avl_find( info->li_maps,
+                       ( caddr_t )&tmp, rewrite_builtin_map_cmp );
+}
+
+/*
+ * Parses a plugin map
+ */
+static int
+rewrite_parse_builtin_map(
+               struct rewrite_info *info,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+)
+{
+       struct rewrite_builtin_map *map;
+       
+#define MAP_TYPE       1
+#define MAP_NAME       2
+       
+       assert( info != NULL );
+       assert( fname != NULL );
+       assert( argc > 2 );
+       assert( argv != NULL );
+       assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 );
+
+       map = calloc( sizeof( struct rewrite_builtin_map ), 1 );
+       if ( map == NULL ) {
+               return REWRITE_ERR;
+       }
+
+       map->lb_name = strdup( argv[ MAP_NAME ] );
+       if ( map->lb_name == NULL ) {
+               free( map );
+               return REWRITE_ERR;
+       }
+       
+       /*
+        * Built-in ldap map
+        */
+       if ( strcasecmp( argv[ MAP_TYPE ], "ldap" ) == 0 ) {
+               map->lb_type = REWRITE_BUILTIN_MAP_LDAP;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) {
+                       free( map->lb_name );
+                       free( map );
+                       return REWRITE_ERR;
+               }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+               
+               map->lb_private = map_ldap_parse( info, fname, lineno,
+                               argc - 3, argv + 3 );
+               
+       /* 
+        * Error
+        */     
+       } else {
+               Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n%s",
+                               fname, lineno, "" );
+               return -1;
+       }
+
+       return rewrite_builtin_map_insert( info, map );
+}
diff --git a/libraries/librewrite/context.c b/libraries/librewrite/context.c
new file mode 100644 (file)
index 0000000..4e527e8
--- /dev/null
@@ -0,0 +1,415 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two struct rewrite_context based on the name;
+ * used by avl stuff
+ */
+static int
+rewrite_context_cmp(
+               const void *c1,
+               const void *c2
+)
+{
+       struct rewrite_context *lc1, *lc2;
+       
+       lc1 = (struct rewrite_context *)c1;
+       lc2 = (struct rewrite_context *)c2;
+       
+       assert( c1 != NULL );
+       assert( c2 != NULL );
+       assert( lc1->lc_name != NULL );
+       assert( lc2->lc_name != NULL );
+       
+       return strcasecmp( lc1->lc_name, lc2->lc_name );
+}
+
+/*
+ * Returns -1 in case a duplicate struct rewrite_context
+ * has been inserted; used by avl stuff
+ */
+static int
+rewrite_context_dup(
+               void *c1,
+               void *c2
+               )
+{
+       struct rewrite_context *lc1, *lc2;
+       
+       lc1 = (struct rewrite_context *)c1;
+       lc2 = (struct rewrite_context *)c2;
+       
+       assert( c1 != NULL );
+       assert( c2 != NULL );
+       assert( lc1->lc_name != NULL );
+       assert( lc2->lc_name != NULL );
+       
+       return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
+}
+
+/*
+ * Finds the context named rewriteContext in the context tree
+ */
+struct rewrite_context *
+rewrite_context_find(
+               struct rewrite_info *info,
+               const char *rewriteContext
+)
+{
+       struct rewrite_context *context, c;
+
+       assert( info != NULL );
+       assert( rewriteContext != NULL );
+
+       /*
+        * Fetches the required rewrite context
+        */
+       c.lc_name = (char *)rewriteContext;
+       context = (struct rewrite_context *)avl_find( info->li_context, 
+                       (caddr_t)&c, rewrite_context_cmp );
+       if ( context == NULL ) {
+               return NULL;
+       }
+
+       /*
+        * De-aliases the context if required
+        */
+       if ( context->lc_alias ) {
+               return context->lc_alias;
+       }
+
+       return context;
+}
+
+/*
+ * Creates a new context called rewriteContext and stores in into the tree
+ */
+struct rewrite_context *
+rewrite_context_create(
+               struct rewrite_info *info,
+               const char *rewriteContext
+)
+{
+       struct rewrite_context *context;
+       int rc;
+
+       assert( info != NULL );
+       assert( rewriteContext != NULL );
+       
+       context = calloc( sizeof( struct rewrite_context ), 1 );
+       if ( context == NULL ) {
+               return NULL;
+       }
+       
+       /*
+        * Context name
+        */
+       context->lc_name = strdup( rewriteContext );
+       if ( context->lc_name == NULL ) {
+               free( context );
+               return NULL;
+       }
+
+       /*
+        * The first, empty rule
+        */
+       context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
+       if ( context->lc_rule == NULL ) {
+               free( context->lc_name );
+               free( context );
+               return NULL;
+       }
+       
+       /*
+        * Add context to tree
+        */
+       rc = avl_insert( &info->li_context, (caddr_t)context,
+                       rewrite_context_cmp, rewrite_context_dup );
+       if ( rc == -1 ) {
+               free( context->lc_rule );
+               free( context->lc_name );
+               free( context );
+               return NULL;
+       }
+
+       return context;
+}
+
+/*
+ * Finds the next rule according to a goto action statement,
+ * or null in case of error.
+ * Helper for rewrite_context_apply.
+ */
+static struct rewrite_rule *
+rewrite_action_goto(
+               struct rewrite_action *action,
+               struct rewrite_rule *rule
+)
+{
+       int n;
+       
+       assert( action != NULL );
+       assert( action->la_args != NULL );
+       assert( rule != NULL );
+       
+       n = ((int *)action->la_args)[ 0 ];
+       
+       if ( n > 0 ) {
+               for ( ; n > 1 && rule != NULL ; n-- ) {
+                       rule = rule->lr_next;
+               }
+       } else if ( n <= 0 ) {
+               for ( ; n < 1 && rule != NULL ; n++ ) {
+                       rule = rule->lr_prev;
+               }
+       }
+
+       return rule;
+}
+
+/*
+ * Rewrites string according to context; may return:
+ *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
+ *      STOP:   fine, rule matched; stop processing following rules
+ *      UNWILL: rule matched; force 'unwilling to perform'
+ */
+int
+rewrite_context_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_context *context,
+               const char *string,
+               char **result
+)
+{
+       struct rewrite_rule *rule;
+       char *s, *res = NULL;
+       int return_code = REWRITE_REGEXEC_OK;
+       
+       assert( info != NULL );
+       assert( op != NULL );
+       assert( context != NULL );
+       assert( context->lc_rule != NULL );
+       assert( string != NULL );
+       assert( result != NULL );
+
+       op->lo_depth++;
+       assert( op->lo_depth > 0 );
+
+       Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+                       " [depth=%d] string='%s'\n%s",
+                       op->lo_depth, string, "" );
+       
+       s = strdup( string );
+       
+       for ( rule = context->lc_rule->lr_next;
+                       rule != NULL && op->lo_num_passes < info->li_max_passes;
+                       rule = rule->lr_next, op->lo_num_passes++ ) {
+               int rc;
+               
+               /*
+                * Apply a single rule
+                */
+               rc = rewrite_rule_apply( info, op, rule, s, &res );
+               
+               /*
+                * A rule may return:
+                *      OK              with result != NULL if matched
+                *      ERR             if anything was wrong
+                *      UNWILLING       if the server should drop the request
+                * the latter case in honored immediately;
+                * the other two may require some special actions to take
+                * place.
+                */
+               switch ( rc ) {
+                       
+               case REWRITE_REGEXEC_ERR:
+                       Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
+                                       " error ...\n%s%s%s", "", "",  "");
+
+                       /*
+                        * Checks for special actions to be taken
+                        * in case of error ...
+                        */
+                       if ( rule->lr_action != NULL ) {
+                               struct rewrite_action *action;
+                               int do_continue = 0;
+                               
+                               for ( action = rule->lr_action;
+                                               action != NULL;
+                                               action = action->la_next ) {
+                                       switch ( action->la_type ) {
+                                       
+                                       /*
+                                        * This action takes precedence
+                                        * over the others in case of failure
+                                        */
+                                       case REWRITE_ACTION_IGNORE_ERR:
+                                               Debug( LDAP_DEBUG_ANY,
+                                       "==> rewrite_context_apply"
+                                       " ignoring error ...\n%s%s%s",
+                                                       "", "", "" );
+                                               do_continue = 1;
+                                               break;
+
+                                       /*
+                                        * Goto is honored only if it comes
+                                        * after ignore error
+                                        */
+                                       case REWRITE_ACTION_GOTO:
+                                               if ( do_continue ) {
+                                                       rule = rewrite_action_goto( action, rule );
+                                                       if ( rule == NULL ) {
+                                                               return_code = REWRITE_REGEXEC_ERR;
+                                                               goto rc_end_of_context;
+                                                       }
+                                               }
+                                               break;
+
+                                       /*
+                                        * Other actions are ignored
+                                        */
+                                       default:
+                                               break;
+                                       }
+                               }
+
+                               if ( do_continue ) {
+                                       if ( rule->lr_next == NULL ) {
+                                               res = s;
+                                       }
+                                       goto rc_continue;
+                               }
+                       }
+
+                       /* 
+                        * Default behavior is to bail out ...
+                        */
+                       return_code = REWRITE_REGEXEC_ERR;
+                       goto rc_end_of_context;
+               
+               /*
+                * OK means there were no errors or special return codes;
+                * if res is defined, it means the rule matched and we
+                * got a sucessful rewriting
+                */
+               case REWRITE_REGEXEC_OK:
+
+                       /*
+                        * It matched! Check for actions ...
+                        */
+                       if ( res != NULL ) {
+                               struct rewrite_action *action;
+                               
+                               free( s );
+                               s = res;
+
+                               for ( action = rule->lr_action;
+                                               action != NULL;
+                                               action = action->la_next ) {
+
+                                       switch ( action->la_type ) {
+
+                                       /*
+                                        * This ends the rewrite context
+                                        * successfully
+                                        */
+                                       case REWRITE_ACTION_STOP:
+                                               goto rc_end_of_context;
+                                       
+                                       /*
+                                        * This instructs the server to return
+                                        * an `unwilling to perform' error
+                                        * message
+                                        */
+                                       case REWRITE_ACTION_UNWILLING:
+                                               return_code = REWRITE_REGEXEC_UNWILLING;
+                                               goto rc_end_of_context;
+                                       
+                                       /*
+                                        * This causes the processing to
+                                        * jump n rules back and forth
+                                        */
+                                       case REWRITE_ACTION_GOTO:
+                                               rule = rewrite_action_goto( action, rule );
+                                               if ( rule == NULL ) {
+                                                       return_code = REWRITE_REGEXEC_ERR;
+                                                       goto rc_end_of_context;
+                                               }
+                                               break;
+                                       
+                                       default:
+                                               /* ... */
+                                               break;
+                                       }
+                               }
+
+                       /*
+                        * If result was OK and string didn't match,
+                        * in case of last rule we need to set the
+                        * result back to the string
+                        */
+                       } else if ( rule->lr_next == NULL ) {
+                               res = s;
+                       }
+                       
+                       break;
+
+               /*
+                * A STOP has propagated ...
+                */
+               case REWRITE_REGEXEC_STOP:
+                       goto rc_end_of_context;
+
+               /*
+                * This will instruct the server to return
+                * an `unwilling to perform' error message
+                */
+                case REWRITE_REGEXEC_UNWILLING:
+                       return_code = REWRITE_REGEXEC_UNWILLING;
+                       goto rc_end_of_context;
+
+               }
+               
+rc_continue:   /* sent here by actions that require to continue */
+
+       }
+
+rc_end_of_context:
+       *result = res;
+
+       Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+                       " [depth=%d] res={%d,'%s'}\n",
+                       op->lo_depth, return_code, ( res ? res : "NULL" ) );
+
+       assert( op->lo_depth > 0 );
+       op->lo_depth--;
+
+       return return_code;
+}
+
diff --git a/libraries/librewrite/info.c b/libraries/librewrite/info.c
new file mode 100644 (file)
index 0000000..2b8184e
--- /dev/null
@@ -0,0 +1,254 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Global data
+ */
+
+/*
+ * This becomes the running context for subsequent calls to
+ * rewrite_parse; it can be altered only by a 
+ * rewriteContext config line or by a change in info.
+ */
+struct rewrite_context *__curr_context = NULL;
+
+/*
+ * Inits the info
+ */
+struct rewrite_info *
+rewrite_info_init(
+               int mode
+)
+{
+       struct rewrite_info *info;
+       struct rewrite_context *context;
+
+       switch ( mode ) {
+       case REWRITE_MODE_ERR:
+       case REWRITE_MODE_OK:
+       case REWRITE_MODE_COPY_INPUT:
+       case REWRITE_MODE_USE_DEFAULT:
+               break;
+       default:
+               mode = REWRITE_MODE_USE_DEFAULT;
+               break;
+               /* return NULL */
+       }
+
+       /*
+        * Resets the running context for parsing ...
+        */
+       __curr_context = NULL;
+
+       info = calloc( sizeof( struct rewrite_info ), 1 );
+       if ( info == NULL ) {
+               return NULL;
+       }
+
+       info->li_state = REWRITE_DEFAULT;
+       info->li_max_passes = REWRITE_MAX_PASSES;
+       info->li_rewrite_mode = mode;
+
+       /*
+        * Add the default (empty) rule
+        */
+       context = rewrite_context_create( info, REWRITE_DEFAULT_CONTEXT );
+       if ( context == NULL ) {
+               free( info );
+               return NULL;
+       }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       if ( ldap_pvt_thread_rdwr_init( &info->li_cookies_mutex ) ) {
+               free( info );
+               return NULL;
+       }
+       if ( ldap_pvt_thread_rdwr_init( &info->li_params_mutex ) ) {
+               free( info );
+               return NULL;
+       }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       return info;
+}
+
+/*
+ * Cleans up the info structure
+ */
+int
+rewrite_info_delete(
+               struct rewrite_info *info
+)
+{
+       assert( info != NULL );
+       
+       rewrite_session_destroy( info );
+
+       rewrite_param_destroy( info );
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex );
+       ldap_pvt_thread_rdwr_destroy( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Rewrites a string according to context.
+ * If the engine is off, OK is returned, but the return string will be NULL.
+ * In case of 'unwilling to perform', UNWILLING is returned, and the
+ * return string will also be null. The same in case of error.
+ * Otherwise, OK is returned, and result will hold a newly allocated string
+ * with the rewriting.
+ * 
+ * What to do in case of non-existing rewrite context is still an issue.
+ * Four possibilities:
+ *     - error, 
+ *     - ok with NULL result, 
+ *     - ok with copy of string as result,
+ *     - use the default rewrite context.
+ */
+int
+rewrite(
+               struct rewrite_info *info,
+               const char *rewriteContext,
+               const char *string,
+               char **result
+)
+{
+       return rewrite_session( info, rewriteContext, 
+                       string, NULL, result );
+}
+
+int
+rewrite_session(
+               struct rewrite_info *info,
+               const char *rewriteContext,
+               const char *string,
+               const void *cookie,
+               char **result
+)
+{
+       struct rewrite_context *context;
+       struct rewrite_op op = { 0, 0, NULL, NULL, NULL, NULL };
+       int rc;
+       
+       assert( info != NULL );
+       assert( rewriteContext != NULL );
+       assert( string != NULL );
+       assert( result != NULL );
+
+       /*
+        * cookie can be null; means: don't care about session stuff
+        */
+
+       *result = NULL;
+       op.lo_cookie = cookie;
+       
+       /*
+        * Engine not on means no failure, but explicit no rewriting
+        */
+       if ( info->li_state != REWRITE_ON ) {
+               rc = REWRITE_REGEXEC_OK;
+               goto rc_return;
+       }
+       
+       /*
+        * Undefined context means no rewriting also
+        * (conservative, are we sure it's what we want?)
+        */
+       context = rewrite_context_find( info, rewriteContext );
+       if ( context == NULL ) {
+               switch ( info->li_rewrite_mode ) {
+               case REWRITE_MODE_ERR:
+                       rc = REWRITE_REGEXEC_ERR;
+                       goto rc_return;
+               case REWRITE_MODE_OK:
+                       rc = REWRITE_REGEXEC_OK;
+                       goto rc_return;
+               case REWRITE_MODE_COPY_INPUT:
+                       *result = strdup( string );
+                       rc = REWRITE_REGEXEC_OK;
+                       goto rc_return;
+               case REWRITE_MODE_USE_DEFAULT:
+                       context = rewrite_context_find( info,
+                                       REWRITE_DEFAULT_CONTEXT );
+                       break;
+               }
+       }
+       
+       op.lo_string = strdup( string );
+       if ( op.lo_string == NULL ) {
+               rc = REWRITE_REGEXEC_ERR;
+               goto rc_return;
+       }
+       
+       /*
+        * Applies rewrite context
+        */
+       rc = rewrite_context_apply(info, &op, context, string, result );
+       assert( op.lo_depth == 0 );
+
+       /* ?!? */
+       free( op.lo_string );
+       
+       switch ( rc ) {
+       /*
+        * Success
+        */
+       case REWRITE_REGEXEC_OK:
+       case REWRITE_REGEXEC_STOP:
+               /*
+                * If rewrite succeeded return OK regardless of how
+                * the successful rewriting was obtained!
+                */
+               rc = REWRITE_REGEXEC_OK;
+               break;
+               
+       
+       /*
+        * Internal or forced error, return = NULL; rc already OK.
+        */
+       case REWRITE_REGEXEC_UNWILLING:
+       case REWRITE_REGEXEC_ERR:
+       default:
+               if ( *result != NULL ) {
+                       free( *result );
+                       *result = NULL;
+               }
+       }
+
+rc_return:
+       if ( op.lo_vars ) {
+               rewrite_var_delete( op.lo_vars );
+       }
+       
+       return rc;
+}
+
diff --git a/libraries/librewrite/ldapmap.c b/libraries/librewrite/ldapmap.c
new file mode 100644 (file)
index 0000000..16db729
--- /dev/null
@@ -0,0 +1,333 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * LDAP map data structure
+ */
+struct ldap_map_data {
+       char                           *url;
+       LDAPURLDesc                    *lud;
+       int                             attrsonly;
+       char                           *binddn;
+       char                           *bindpw;
+
+#define MAP_LDAP_EVERYTIME             0x00
+#define MAP_LDAP_NOW                   0x01
+#define MAP_LDAP_LATER                 0x02
+       int                             when;
+
+       LDAP                           *ld;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_mutex_t         mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+static void
+map_ldap_free(
+               struct ldap_map_data *data
+)
+{
+       assert( data != NULL );
+
+       if ( data->url != NULL ) {
+               free( data->url );
+       }
+
+       if ( data->lud != NULL ) {
+               ldap_free_urldesc( data->lud );
+       }
+
+       if ( data->binddn != NULL ) {
+               free( data->binddn );
+       }
+
+       if ( data->bindpw != NULL ) {
+               free( data->bindpw );
+       }
+
+       if ( data->when != MAP_LDAP_EVERYTIME && data->ld != NULL ) {
+               ldap_unbind_s( data->ld );
+       }
+
+       free( data );
+}
+
+void *
+map_ldap_parse(
+               struct rewrite_info *info,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+)
+{
+       struct ldap_map_data *data;
+       char *p;
+
+       assert( info != NULL );
+       assert( fname != NULL );
+       assert( argv != NULL );
+
+       data = calloc( sizeof( struct ldap_map_data ), 1 );
+       if ( data == NULL ) {
+               return NULL;
+       }
+
+       if ( argc < 1 ) {
+               Debug( LDAP_DEBUG_ANY,
+                               "[%s:%d] ldap map needs URI\n%s",
+                               fname, lineno, "" );
+               free( data );
+               return NULL;
+       }
+
+       data->url = strdup( argv[ 0 ] );
+       if ( data->url == NULL ) {
+               map_ldap_free( data );
+               return NULL;
+       }
+       
+       if ( ldap_url_parse( argv[ 0 ], &data->lud ) != REWRITE_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY,
+                               "[%s:%d] illegal URI '%s'\n",
+                               fname, lineno, argv[ 0 ] );
+               map_ldap_free( data );
+               return NULL;
+       }
+
+       p = strchr( data->url, '/' );
+       assert( p[ 1 ] == '/' );
+       if ( ( p = strchr( p + 2, '/' ) ) != NULL ) {
+               p[ 0 ] = '\0';
+       }
+
+       if ( strcasecmp( data->lud->lud_attrs[ 0 ], "dn" ) == 0 ) {
+               data->attrsonly = 1;
+       }
+             
+       for ( argc--, argv++; argc > 0; argc--, argv++ ) {
+               if ( strncasecmp( argv[ 0 ], "binddn=", 7 ) == 0 ) {
+                       char *p = argv[ 0 ] + 7;
+                       int l;
+
+                       if ( p[ 0 ] == '\"' || p [ 0 ] == '\'' ) {
+                               l = strlen( p ) - 2;
+                               p++;
+                               if ( p[ l ] != p[ 0 ] ) {
+                                       map_ldap_free( data );
+                                       return NULL;
+                               }
+                       } else {
+                               l = strlen( p );
+                       }
+                       
+                       data->binddn = strdup( p );                     
+                       if ( data->binddn == NULL ) {
+                               map_ldap_free( data );
+                               return NULL;
+                       }
+
+                       if ( data->binddn[ l ] == '\"' 
+                                       || data->binddn[ l ] == '\'' ) {
+                               data->binddn[ l ] = '\0';
+                       }
+               } else if ( strncasecmp( argv[ 0 ], "bindpw=", 7 ) == 0 ) {
+                       data->bindpw = strdup( argv[ 2 ] + 7 );
+                       if ( data->bindpw == NULL ) {
+                               map_ldap_free( data );
+                               return NULL;
+                       }
+               } else if ( strncasecmp( argv[ 0 ], "bindwhen=", 9 ) == 0 ) {
+                       char *p = argv[ 0 ] + 9;
+
+                       if ( strcasecmp( p, "now" ) == 0 ) {
+                               int rc;
+                               
+                               data->when = MAP_LDAP_NOW;
+                               
+                               /*
+                                * Init LDAP handler ...
+                                */
+                               rc = ldap_initialize( &data->ld, data->url );
+                               if ( rc != LDAP_SUCCESS ) {
+                                       map_ldap_free( data );
+                                       return NULL;
+                               }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                               ldap_pvt_thread_mutex_init( &data->mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+                       } else if ( strcasecmp( p, "later" ) == 0 ) {
+                               data->when = MAP_LDAP_LATER;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                               ldap_pvt_thread_mutex_init( &data->mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+                       } else if ( strcasecmp( p, "everytime" ) == 0 ) {
+                               data->when = MAP_LDAP_EVERYTIME;
+                       } else {
+                               /* ignore ... */
+                       }
+               }
+       }
+
+       return ( void * )data;
+}
+
+int
+map_ldap_apply(
+               struct rewrite_builtin_map *map,
+               const char *filter,
+               struct berval *val
+
+)
+{
+       LDAP *ld;
+       LDAPMessage *res = NULL, *entry;
+       char **values;
+       int rc;
+       struct ldap_map_data *data = ( struct ldap_map_data * )map->lb_private;
+       LDAPURLDesc *lud = data->lud;
+       
+       int first_try = 1;
+
+       assert( map != NULL );
+       assert( map->lb_type == REWRITE_BUILTIN_MAP_LDAP );
+       assert( map->lb_private != NULL );
+       assert( filter != NULL );
+       assert( val != NULL );
+
+       val->bv_val = NULL;
+       val->bv_len = 0;
+
+       if ( data->when == MAP_LDAP_EVERYTIME ) {
+               rc = ldap_initialize( &ld, data->url );
+       } else {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_lock( &data->mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+               rc = LDAP_SUCCESS;
+
+               if ( data->when == MAP_LDAP_LATER && data->ld == NULL ) {
+                       rc = ldap_initialize( &data->ld, data->url );
+               }
+               
+               ld = data->ld;
+       }
+
+       if ( rc != LDAP_SUCCESS ) {
+               rc = REWRITE_ERR;
+               goto rc_return;
+       }
+
+do_bind:
+       if ( data->binddn != NULL ) {
+               rc = ldap_simple_bind_s( ld, data->binddn, data->bindpw );
+               if ( rc == LDAP_SERVER_DOWN && first_try ) {
+                       first_try = 0;
+                       if ( ldap_initialize( &ld, data->url ) != LDAP_SUCCESS ) {
+                               rc = REWRITE_ERR;
+                               goto rc_return;
+                       }
+                       goto do_bind;
+               } else if ( rc != REWRITE_SUCCESS ) {
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+       }
+
+       rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter,
+                       lud->lud_attrs, data->attrsonly, &res );
+       if ( rc == LDAP_SERVER_DOWN && first_try ) {
+               first_try = 0;
+                if ( ldap_initialize( &ld, data->url ) != LDAP_SUCCESS ) {
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+               goto do_bind;
+       } else if ( rc != REWRITE_SUCCESS ) {
+               rc = REWRITE_ERR;
+               goto rc_return;
+       }
+
+       if ( ldap_count_entries( ld, res ) != 1 ) {
+               ldap_msgfree( res );
+               rc = REWRITE_ERR;
+               goto rc_return;
+       }
+
+       entry = ldap_first_entry( ld, res );
+       assert( entry != NULL );
+
+       if ( data->attrsonly == 1 ) {
+               /*
+                * dn is newly allocated, so there's no need to strdup it
+                */
+               val->bv_val = ldap_get_dn( ld, entry );
+       } else {
+               values = ldap_get_values( ld, entry, lud->lud_attrs[ 0 ] );
+               if ( values == NULL || values[ 0 ] == NULL ) {
+                       if ( values != NULL ) {
+                               ldap_value_free( values );
+                       }
+                       ldap_msgfree( res );
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+               val->bv_val = strdup( values[ 0 ] );
+               ldap_value_free( values );
+       }
+       
+       ldap_msgfree( res );
+
+       if ( val->bv_val == NULL ) {
+               rc = REWRITE_ERR;
+               goto rc_return;
+       }
+       val->bv_len = strlen( val->bv_val );
+
+rc_return:
+       if ( data->when == MAP_LDAP_EVERYTIME ) {
+               if ( ld != NULL ) {
+                       ldap_unbind_s( ld );
+               }
+       } else {
+               data->ld = ld;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_unlock( &data->mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       }
+       
+       return rc;
+}
+
diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c
new file mode 100644 (file)
index 0000000..f22bb92
--- /dev/null
@@ -0,0 +1,807 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include <pwd.h>
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * Global data
+ */
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ldap_pvt_thread_mutex_t xpasswd_mutex;
+static int xpasswd_mutex_init = 0;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+/*
+ * Map parsing
+ * NOTE: these are old-fashion maps; new maps will be parsed on separate
+ * config lines, and referred by name.
+ */
+struct rewrite_map *
+rewrite_xmap_parse(
+               struct rewrite_info *info,
+               const char *s,
+               const char **currpos
+)
+{
+       struct rewrite_map *map;
+
+       assert( info != NULL );
+       assert( s != NULL );
+       assert( currpos != NULL );
+
+       Debug( LDAP_DEBUG_ARGS, "rewrite_xmap_parse: %s\n%s%s",
+                       s, "", "" );
+
+       *currpos = NULL;
+
+       map = calloc( sizeof( struct rewrite_map ), 1 );
+       if ( map == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "rewrite_xmap_parse:"
+                               " calloc failed\n%s%s%s", "", "", "" );
+               return NULL;
+       }
+
+       /*
+        * Experimental passwd map:
+        * replaces the uid with the matching gecos from /etc/passwd file 
+        */
+       if ( strncasecmp(s, "xpasswd", 7 ) == 0 ) {
+               map->lm_type = REWRITE_MAP_XPWDMAP;
+               map->lm_name = strdup( "xpasswd" );
+
+               assert( s[7] == '}' );
+               *currpos = s + 8;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               if ( !xpasswd_mutex_init ) {
+                       xpasswd_mutex_init = 1;
+                       if ( ldap_pvt_thread_mutex_init( &xpasswd_mutex ) ) {
+                               free( map );
+                               return NULL;
+                       }
+               }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+               /* Don't really care if fails */
+               return map;
+       
+       /*
+        * Experimental file map:
+        * looks up key in a `key value' ascii file
+        */
+       } else if ( strncasecmp(s, "xfile", 5 ) == 0 ) {
+               char *filename;
+               const char *p;
+               int l;
+               int c = 5;
+               
+               map->lm_type = REWRITE_MAP_XFILEMAP;
+               
+               if ( s[ c ] != '(' ) {
+                       free( map );
+                       return NULL;
+               }
+
+               /* Must start with '/' for security concerns */
+               c++;
+               if ( s[ c ] != '/' ) {
+                       free( map );
+                       return NULL;
+               }
+
+               for ( p = s + c; p[ 0 ] != '\0' && p[ 0 ] != ')'; p++ );
+               if ( p[ 0 ] != ')' ) {
+                       free( map );
+                       return NULL;
+               }
+
+               l = p - s - c;
+               filename = calloc( sizeof( char ), l + 1 );
+               strncpy( filename, s + c, l );
+               filename[ l ] = '\0';
+               
+               map->lm_args = ( void * )fopen( filename, "r" );
+               free( filename );
+
+               if ( map->lm_args == NULL ) {
+                       free( map );
+                       return NULL;
+               }
+
+               *currpos = p + 1;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+                       fclose( ( FILE * )map->lm_args );
+                       free( map );
+                       return NULL;
+               }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */      
+               
+               return map;
+
+       /*
+         * Experimental ldap map:
+         * looks up key on the fly (not implemented!)
+         */
+        } else if ( strncasecmp(s, "xldap", 5 ) == 0 ) {
+               char *p;
+               char *url;
+               int l, rc;
+               int c = 5;
+               LDAPURLDesc *lud;
+
+               if ( s[ c ] != '(' ) {
+                       free( map );
+                       return NULL;
+               }
+               c++;
+               
+               p = strchr( s, '}' );
+               if ( p == NULL ) {
+                       free( map );
+                       return NULL;
+               }
+               p--;
+
+               *currpos = p + 2;
+       
+               /*
+                * Add two bytes for urlencoding of '%s'
+                */
+               l = p - s - c;
+               url = calloc( sizeof( char ), l + 3 );
+               strncpy( url, s + c, l );
+               url[ l ] = '\0';
+
+               /*
+                * Urlencodes the '%s' for ldap_url_parse
+                */
+               p = strchr( url, '%' );
+               if ( p != NULL ) {
+                       memmove( p + 3, p + 1, strlen( p + 1 ) + 1 );
+                       p[ 1 ] = '2';
+                       p[ 2 ] = '5';
+               }
+
+               rc =  ldap_url_parse( url, &lud );
+               free( url );
+
+               if ( rc != LDAP_SUCCESS ) {
+                       free( map );
+                       return NULL;
+               }
+               assert( lud != NULL );
+
+               map->lm_args = ( void * )lud;
+               map->lm_type = REWRITE_MAP_XLDAPMAP;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+                       ldap_free_urldesc( lud );
+                       free( map );
+                       return NULL;
+               }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+               return map;
+       
+       /* Unhandled map */
+       }
+       
+       return NULL;
+}
+
+struct rewrite_map *
+rewrite_map_parse(
+               struct rewrite_info *info,
+               const char *string,
+               const char **currpos
+)
+{
+       struct rewrite_map *map = NULL;
+       struct rewrite_subst *subst = NULL;
+       char *s, *begin = NULL, *end;
+       const char *p;
+       int l, cnt;
+
+       assert( info != NULL );
+       assert( string != NULL );
+       assert( currpos != NULL );
+
+       *currpos = NULL;
+
+       /*
+        * Go to the end of the map invocation (the right closing brace)
+        */
+       for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) {
+               if ( p[ 0 ] == REWRITE_SUBMATCH_ESCAPE ) {
+                       /*
+                        * '\' marks the beginning of a new map
+                        */
+                       if ( p[ 1 ] == '{' ) {
+                               cnt++;
+                       /*
+                        * '\' followed by a digit may mark the beginning
+                        * of an old map
+                        */
+                       } else if ( isdigit( p[ 1 ] ) && p[ 2 ] == '{' ) {
+                               cnt++;
+                               p++;
+                       }
+                       p++;
+               } else if ( p[ 0 ] == '}' ) {
+                       cnt--;
+               }
+       }
+       if ( cnt != 0 ) {
+               return NULL;
+       }
+       *currpos = p;
+       
+       /*
+        * Copy the map invocation
+        */
+       l = p - string - 1;
+       s = calloc( sizeof( char ), l + 1 );
+       strncpy( s, string, l );
+       s[ l ] = 0;
+
+       /*
+        * Isolate the map name (except for variable deref)
+        */
+       switch ( s[ 0 ] ) {
+       case REWRITE_OPERATOR_VARIABLE_GET:
+       case REWRITE_OPERATOR_PARAM_GET:
+               break;
+       default:
+               begin = strchr( s, '(' );
+               if ( begin == NULL ) {
+                       free( s );
+                       return NULL;
+               }
+               begin[ 0 ] = '\0';
+               begin++;
+               break;
+       }
+
+       /*
+        * Check for special map types
+        */
+       p = s;
+       switch ( p[ 0 ] ) {
+       case REWRITE_OPERATOR_SUBCONTEXT:
+       case REWRITE_OPERATOR_COMMAND:
+       case REWRITE_OPERATOR_VARIABLE_SET:
+       case REWRITE_OPERATOR_VARIABLE_GET:
+       case REWRITE_OPERATOR_PARAM_GET:
+               p++;
+               break;
+       }
+
+       /*
+        * Variable set and get may be repeated to indicate session-wide
+        * instead of operation-wide variables
+        */
+       switch ( p[ 0 ] ) {
+        case REWRITE_OPERATOR_VARIABLE_SET:
+       case REWRITE_OPERATOR_VARIABLE_GET:
+               p++;
+               break;
+       }
+
+       /*
+        * Variable get token can be appended to variable set to mean store
+        * AND rewrite
+        */
+       if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+               p++;
+       }
+       
+       /*
+        * Check the syntax of the variable name
+        */
+       if ( !isalpha( p[ 0 ] ) ) {
+               free( s );
+               return NULL;
+       }
+       for ( p++; p[ 0 ] != '\0'; p++ ) {
+               if ( !isalnum( p[ 0 ] ) ) {
+                       free( s );
+                       return NULL;
+               }
+       }
+
+       /*
+        * Isolate the argument of the map (except for variable deref)
+        */
+       switch ( s[ 0 ] ) {
+       case REWRITE_OPERATOR_VARIABLE_GET:
+       case REWRITE_OPERATOR_PARAM_GET:
+               break;
+       default:
+               end = strrchr( begin, ')' );
+               if ( end == NULL ) {
+                       free( s );
+                       return NULL;
+               }
+               end[ 0 ] = '\0';
+
+               /*
+                * Compile the substitution pattern of the map argument
+                */
+               subst = rewrite_subst_compile( info, begin );
+               if ( subst == NULL ) {
+                       free( s );
+                       return NULL;
+               }
+               break;
+       }
+
+       /*
+        * Create the map
+        */
+       map = calloc( sizeof( struct rewrite_map ), 1 );
+       if ( map == NULL ) {
+               if ( subst != NULL ) {
+                       free( subst );
+               }
+               free( s );
+               return NULL;
+       }
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+        if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+               if ( subst != NULL ) {
+                       free( subst );
+               }
+               free( s );
+               free( map );
+               return NULL;
+       }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                       
+       /*
+        * No subst for variable deref
+        */
+       switch ( s[ 0 ] ) {
+       case REWRITE_OPERATOR_VARIABLE_GET:
+       case REWRITE_OPERATOR_PARAM_GET:
+               break;
+       default:
+               map->lm_subst = subst;
+               break;
+       }
+
+       /*
+        * Parses special map types
+        */
+       switch ( s[ 0 ] ) {
+       
+       /*
+        * Subcontext
+        */
+       case REWRITE_OPERATOR_SUBCONTEXT:               /* '>' */
+
+               /*
+                * Fetch the rewrite context
+                * it MUST have been defined previously
+                */
+               map->lm_type = REWRITE_MAP_SUBCONTEXT;
+               map->lm_name = strdup( s + 1 );
+               map->lm_data = rewrite_context_find( info, s + 1 );
+               if ( map->lm_data == NULL ) {
+                       free( s );
+                       free( map );
+                       return NULL;
+               }
+               break;
+
+       /*
+        * External command
+        */
+       case REWRITE_OPERATOR_COMMAND:          /* '|' */
+               free( map );
+               map = NULL;
+               break;
+       
+       /*
+        * Variable set
+        */
+       case REWRITE_OPERATOR_VARIABLE_SET:     /* '&' */
+               if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) {
+                       if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+                               map->lm_type = REWRITE_MAP_SETW_SESN_VAR;
+                               map->lm_name = strdup( s + 3 );
+                       } else {
+                               map->lm_type = REWRITE_MAP_SET_SESN_VAR;
+                               map->lm_name = strdup( s + 2 );
+                       }
+               } else {
+                       if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+                               map->lm_type = REWRITE_MAP_SETW_OP_VAR;
+                               map->lm_name = strdup( s + 2 );
+                       } else {
+                               map->lm_type = REWRITE_MAP_SET_OP_VAR;
+                               map->lm_name = strdup( s + 1 );
+                       }
+               }
+               break;
+       
+       /*
+        * Variable dereference
+        */
+       case REWRITE_OPERATOR_VARIABLE_GET:     /* '*' */
+               if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+                       map->lm_type = REWRITE_MAP_GET_SESN_VAR;
+                       map->lm_name = strdup( s + 2 );
+               } else {
+                       map->lm_type = REWRITE_MAP_GET_OP_VAR;
+                       map->lm_name = strdup( s + 1 );
+               }
+               break;
+       
+       /*
+        * Parameter
+        */
+       case REWRITE_OPERATOR_PARAM_GET:                /* '$' */
+               map->lm_type = REWRITE_MAP_GET_PARAM;
+               map->lm_name = strdup( s + 1 );
+               break;
+       
+       /*
+        * Built-in map
+        */
+       default:
+               map->lm_type = REWRITE_MAP_BUILTIN;
+               map->lm_name = strdup( s );
+               map->lm_data = rewrite_builtin_map_find( info, s );
+               if ( map->lm_data == NULL ) {
+                       return NULL;
+               }
+               break;
+
+       }
+       
+       free( s );
+       return map;
+}
+
+/*
+ * Map key -> value resolution
+ * NOTE: these are old-fashion maps; new maps will be parsed on separate
+ * config lines, and referred by name.
+ */
+int
+rewrite_xmap_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_map *map,
+               struct berval *key,
+               struct berval *val
+)
+{
+       int rc = REWRITE_SUCCESS;
+       
+       assert( info != NULL );
+       assert( op != NULL );
+       assert( map != NULL );
+       assert( key != NULL );
+       assert( val != NULL );
+       
+       val->bv_val = NULL;
+       val->bv_len = 0;
+       
+       switch ( map->lm_type ) {
+       case REWRITE_MAP_XPWDMAP: {
+               struct passwd *pwd;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_lock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+               
+               pwd = getpwnam( key->bv_val );
+               if ( pwd == NULL ) {
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                       ldap_pvt_thread_mutex_unlock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+                       rc = REWRITE_NO_SUCH_OBJECT;
+                       break;
+               }
+
+               if ( pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0' ) {
+                       int l = strlen( pwd->pw_gecos );
+                       
+                       val->bv_val = strdup( pwd->pw_gecos );
+                       if ( val->bv_val == NULL ) {
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                               ldap_pvt_thread_mutex_unlock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+                               rc = REWRITE_ERR;
+                               break;
+                       }
+                       val->bv_len = l;
+               } else {
+                       val->bv_val = strdup( key->bv_val );
+                       val->bv_len = key->bv_len;
+               }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_unlock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                       
+               break;
+       }
+       
+       case REWRITE_MAP_XFILEMAP: {
+               char buf[1024];
+               
+               if ( map->lm_args == NULL ) {
+                       rc = REWRITE_ERR;
+                       break;
+               }
+               
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+               rewind( ( FILE * )map->lm_args );
+               
+               while ( fgets( buf, sizeof( buf ), ( FILE * )map->lm_args ) ) {
+                       char *p;
+                       int blen;
+                       
+                       blen = strlen( buf );
+                       if ( buf[ blen - 1 ] == '\n' ) {
+                               buf[ blen - 1 ] = '\0';
+                       }
+                       
+                       p = strtok( buf, " " );
+                       if ( p == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                               ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                               rc = REWRITE_ERR;
+                               goto rc_return;
+                       }
+                       if ( strcasecmp( p, key->bv_val ) == 0 
+                                       && ( p = strtok( NULL, "" ) ) ) {
+                               val->bv_val = strdup( p );
+                               if ( val->bv_val == NULL ) {
+                                       return REWRITE_ERR;
+                               }
+
+                               val->bv_len = strlen( p );
+                               
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                               ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                               
+                               goto rc_return;
+                       }
+               }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+               rc = REWRITE_ERR;
+               
+               break;
+       }
+
+       case REWRITE_MAP_XLDAPMAP: {
+               LDAP *ld;
+               char filter[ LDAP_FILT_MAXSIZ ];
+               LDAPMessage *res = NULL, *entry;
+               LDAPURLDesc *lud = ( LDAPURLDesc * )map->lm_args;
+               int attrsonly = 0;
+               char **values;
+
+               assert( lud != NULL );
+
+               /*
+                * No mutex because there is no write on the map data
+                */
+               
+               ld = ldap_init( lud->lud_host, lud->lud_port );
+               if ( ld == NULL ) {
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+
+               snprintf( filter, sizeof( filter ), lud->lud_filter,
+                               key->bv_val );
+
+               if ( strcasecmp( lud->lud_attrs[ 0 ], "dn" ) == 0 ) {
+                       attrsonly = 1;
+               }
+               rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope,
+                               filter, lud->lud_attrs, attrsonly, &res );
+               if ( rc != LDAP_SUCCESS ) {
+                       ldap_unbind( ld );
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+
+               if ( ldap_count_entries( ld, res ) != 1 ) {
+                       ldap_unbind( ld );
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+
+               entry = ldap_first_entry( ld, res );
+               if ( entry == NULL ) {
+                       ldap_msgfree( res );
+                       ldap_unbind( ld );
+                       rc = REWRITE_ERR;
+                       goto rc_return;
+               }
+               if ( attrsonly == 1 ) {
+                       val->bv_val = ldap_get_dn( ld, entry );
+                       if ( val->bv_val == NULL ) {
+                               ldap_msgfree( res );
+                                ldap_unbind( ld );
+                                rc = REWRITE_ERR;
+                                goto rc_return;
+                        }
+               } else {
+                       values = ldap_get_values( ld, entry,
+                                       lud->lud_attrs[0] );
+                       if ( values == NULL ) {
+                               ldap_msgfree( res );
+                               ldap_unbind( ld );
+                               rc = REWRITE_ERR;
+                               goto rc_return;
+                       }
+                       val->bv_val = strdup( values[ 0 ] );
+                       ldap_value_free( values );
+               }
+               val->bv_len = strlen( val->bv_val );
+
+               ldap_msgfree( res );
+               ldap_unbind( ld );
+               
+               rc = REWRITE_SUCCESS;
+       }
+       }
+
+rc_return:
+       return rc;
+}
+
+/*
+ * Applies the new map type
+ */
+int
+rewrite_map_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_map *map,
+               struct berval *key,
+               struct berval *val
+)
+{
+       int rc = REWRITE_SUCCESS;
+
+       assert( info != NULL );
+       assert( op != NULL );
+       assert( map != NULL );
+       assert( key != NULL );
+       assert( val != NULL );
+
+       val->bv_val = NULL;
+       val->bv_len = 0;
+       
+       switch ( map->lm_type ) {
+       case REWRITE_MAP_SUBCONTEXT:
+               rc = rewrite_context_apply( info, op, 
+                               ( struct rewrite_context * )map->lm_data,
+                               key->bv_val, &val->bv_val );
+               if ( val->bv_val != NULL ) {
+                       val->bv_len = strlen( val->bv_val );
+               }
+               break;
+
+       case REWRITE_MAP_SET_OP_VAR:
+       case REWRITE_MAP_SETW_OP_VAR:
+               rc = rewrite_var_set( &op->lo_vars, map->lm_name,
+                               key->bv_val, 1 )
+                       ? REWRITE_SUCCESS : REWRITE_ERR;
+               if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) {
+                       val->bv_val = strdup( "" );
+               } else {
+                       val->bv_val = strdup( key->bv_val );
+                       val->bv_len = key->bv_len;
+               }
+               break;
+       
+       case REWRITE_MAP_GET_OP_VAR: {
+               struct rewrite_var *var;
+
+               var = rewrite_var_find( op->lo_vars, map->lm_name );
+               if ( var == NULL ) {
+                       rc = REWRITE_ERR;
+               } else {
+                       val->bv_val = strdup( var->lv_value.bv_val );
+                       val->bv_len = var->lv_value.bv_len;
+               }
+               break;  
+       }
+
+       case REWRITE_MAP_SET_SESN_VAR:
+       case REWRITE_MAP_SETW_SESN_VAR:
+               if ( op->lo_cookie == NULL ) {
+                       rc = REWRITE_ERR;
+                       break;
+               }
+               rc = rewrite_session_var_set( info, op->lo_cookie, 
+                               map->lm_name, key->bv_val );
+               if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) {
+                       val->bv_val = strdup( "" );
+               } else {
+                       val->bv_val = strdup( key->bv_val );
+                       val->bv_len = key->bv_len;
+               }
+               break;
+
+       case REWRITE_MAP_GET_SESN_VAR:
+               rc = rewrite_session_var_get( info, op->lo_cookie,
+                               map->lm_name, val );
+               break;          
+
+       case REWRITE_MAP_GET_PARAM:
+               rc = rewrite_param_get( info, map->lm_name, val );
+               break;
+
+       case REWRITE_MAP_BUILTIN: {
+               struct rewrite_builtin_map *bmap = map->lm_data;
+               switch ( bmap->lb_type ) {
+               case REWRITE_BUILTIN_MAP_LDAP:
+                       rc = map_ldap_apply( bmap, key->bv_val, val );
+                       break;
+               default:
+                       rc = REWRITE_ERR;
+                       break;
+               }
+               break;
+       }
+
+       default:
+               rc = REWRITE_ERR;
+               break;
+       }
+
+       return rc;
+}
+
diff --git a/libraries/librewrite/params.c b/libraries/librewrite/params.c
new file mode 100644 (file)
index 0000000..07daa0a
--- /dev/null
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Defines and inits a variable with global scope
+ */
+int
+rewrite_param_set(
+               struct rewrite_info *info,
+               const char *name,
+               const char *value
+)
+{
+       struct rewrite_var *var;
+
+       assert( info != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       var = rewrite_var_find( info->li_params, name );
+       if ( var != NULL ) {
+               assert( var->lv_value.bv_val != NULL );
+               free( var->lv_value.bv_val );
+               var->lv_value.bv_val = strdup( value );
+               var->lv_value.bv_len = strlen( value );
+       } else {
+               var = rewrite_var_insert( &info->li_params, name, value );
+               if ( var == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                       ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                       return REWRITE_ERR;
+               }
+       }       
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Gets a var with global scope
+ */
+int
+rewrite_param_get(
+               struct rewrite_info *info,
+               const char *name,
+               struct berval *value
+)
+{
+       struct rewrite_var *var;
+
+       assert( info != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+
+       value->bv_val = NULL;
+       value->bv_len = 0;
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_rlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       var = rewrite_var_find( info->li_params, name );
+       if ( var == NULL ) {
+               
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+               
+               return REWRITE_ERR;
+       } else {
+               value->bv_val = strdup( var->lv_value.bv_val );
+               value->bv_len = var->lv_value.bv_len;
+       }
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+        ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Destroys the parameter tree
+ */
+int
+rewrite_param_destroy(
+               struct rewrite_info *info
+)
+{
+       int count;
+
+       assert( info != NULL );
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       count = avl_free( info->li_params, NULL );
+       info->li_params = NULL;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/parse.c b/libraries/librewrite/parse.c
new file mode 100644 (file)
index 0000000..bf4426c
--- /dev/null
@@ -0,0 +1,127 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+static int
+parse_line(
+               char **argv,
+               int *argc,
+               int maxargs, 
+               char *buf
+)
+{
+       char *p, *begin;
+       int in_quoted_field = 0, cnt = 0;
+       char quote = '\0';
+       
+       for ( p = buf; isspace( p[ 0 ] ); p++ );
+       
+       if ( p[ 0 ] == '#' ) {
+               return 0;
+       }
+       
+       for ( begin = p;  p[ 0 ] != '\0'; p++ ) {
+               if ( p[ 0 ] == '\\' ) {
+                       p++;
+               } else if ( p[ 0 ] == '\'' || p[ 0 ] == '\"') {
+                       if ( in_quoted_field && p[ 0 ] == quote ) {
+                               in_quoted_field = 1 - in_quoted_field;
+                               quote = '\0';
+                               p[ 0 ] = '\0';
+                               argv[ cnt ] = begin;
+                               if ( ++cnt == maxargs ) {
+                                       *argc = cnt;
+                                       return 1;
+                               }
+                               for ( p++; isspace( p[ 0 ] ); p++ );
+                               begin = p;
+                               p--;
+                               
+                       } else if ( !in_quoted_field ) {
+                               if ( p != begin ) {
+                                       return -1;
+                               }
+                               begin++;
+                               in_quoted_field = 1 - in_quoted_field;
+                               quote = p[ 0 ];
+                       }
+               } else if ( isspace( p[ 0 ] ) && !in_quoted_field ) {
+                       p[ 0 ] = '\0';
+                       argv[ cnt ] = begin;
+
+                       if ( ++cnt == maxargs ) {
+                               *argc = cnt;
+                               return 1;
+                       }
+
+                       for ( p++; isspace( p[ 0 ] ); p++ );
+                       begin = p;
+                       p--;
+               }
+       }
+       
+       *argc = cnt;
+
+       return 1;
+}
+
+int
+read_rewrite( 
+               FILE *fin,
+               struct rewrite_info *info
+)
+{
+       char buf[ 1024 ];
+       char *argv[11];
+       int argc, lineno;
+
+       /* 
+        * Empty rule at the beginning of the context
+        */
+
+       for ( lineno = 0; fgets( buf, sizeof( buf ), fin ); lineno++ ) {
+               switch ( parse_line( argv, &argc, sizeof( argv ) - 1, buf ) ) {
+               case -1:
+                       return REWRITE_ERR;
+               case 0:
+                       break;
+               case 1:
+                       if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
+                               int rc;
+                               rc = rewrite_parse( info, "file", lineno, 
+                                               argc, argv );
+                               if ( rc != REWRITE_SUCCESS ) {
+                                       return rc;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/rewrite-int.h b/libraries/librewrite/rewrite-int.h
new file mode 100644 (file)
index 0000000..375e6d5
--- /dev/null
@@ -0,0 +1,554 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#ifndef REWRITE_INT_H
+#define REWRITE_INT_H
+
+/*
+ * These are required by every file of the library, so they're included here
+ */
+#include <ac/stdlib.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+
+#include <lber.h>
+#include <ldap.h>
+#include "../libldap/ldap-int.h"
+
+#include <avl.h>
+
+#include <rewrite.h>
+
+/* Uncomment to use ldap pvt threads */
+#define USE_REWRITE_LDAP_PVT_THREADS
+#include <ldap_pvt_thread.h>
+
+/*
+ * For details, see RATIONALE.
+ */
+
+#define REWRITE_MAX_MATCH      11      /* 0: overall string; 1-9: submatches */
+#define REWRITE_MAX_PASSES     100
+
+/*
+ * Submatch escape char
+ */
+//#define REWRITE_SUBMATCH_ESCAPE                      '\\'
+#define REWRITE_SUBMATCH_ESCAPE                 '%'
+
+/*
+ * REGEX flags
+ */
+
+#define REWRITE_FLAG_HONORCASE                 'C'
+#define REWRITE_FLAG_BASICREGEX                        'R'
+
+/*
+ * Action flags
+ */
+#define REWRITE_FLAG_EXECONCE                  ':'
+#define REWRITE_FLAG_STOP                      '@'
+#define REWRITE_FLAG_UNWILLING                 '#'
+#define REWRITE_FLAG_GOTO                      'G'     /* requires an arg */
+#define REWRITE_FLAG_IGNORE_ERR                        'I'
+
+/*
+ * Map operators
+ */
+#define REWRITE_OPERATOR_SUBCONTEXT            '>'
+#define REWRITE_OPERATOR_COMMAND               '|'
+#define REWRITE_OPERATOR_VARIABLE_SET          '&'
+#define REWRITE_OPERATOR_VARIABLE_GET          '*'
+#define REWRITE_OPERATOR_PARAM_GET             '$'
+
+
+/***********
+ * PRIVATE *
+ ***********/
+
+/*
+ * Action
+ */
+struct rewrite_action {
+       struct rewrite_action          *la_next;
+       
+#define REWRITE_ACTION_STOP            0x0001
+#define REWRITE_ACTION_UNWILLING       0x0002
+#define REWRITE_ACTION_GOTO            0x0003
+#define REWRITE_ACTION_IGNORE_ERR      0x0004
+       int                             la_type;
+       void                           *la_args;
+};
+
+/*
+ * Map
+ */
+struct rewrite_map {
+
+       /*
+        * Legacy stuff
+        */
+#define REWRITE_MAP_XFILEMAP           0x0001  /* Rough implementation! */
+#define REWRITE_MAP_XPWDMAP            0x0002  /* uid -> gecos */
+#define REWRITE_MAP_XLDAPMAP           0x0003  /* Not implemented yet! */
+
+       /*
+        * Maps with args
+        */
+#define REWRITE_MAP_SUBCONTEXT         0x0101
+       
+#define REWRITE_MAP_SET_OP_VAR         0x0102
+#define REWRITE_MAP_SETW_OP_VAR                0x0103
+#define REWRITE_MAP_GET_OP_VAR         0x0104
+#define        REWRITE_MAP_SET_SESN_VAR        0x0105
+#define REWRITE_MAP_SETW_SESN_VAR      0x0106
+#define        REWRITE_MAP_GET_SESN_VAR        0x0107
+#define REWRITE_MAP_GET_PARAM          0x0108
+#define REWRITE_MAP_BUILTIN            0x0109
+       int                             lm_type;
+
+       char                           *lm_name;
+       void                           *lm_data;
+
+       /*
+        * Old maps store private data in _lm_args;
+        * new maps store the substitution pattern in _lm_subst
+        */
+       union {         
+               void                   *_lm_args;
+               struct rewrite_subst   *_lm_subst;
+       } lm_union;
+#define        lm_args lm_union._lm_args
+#define        lm_subst lm_union._lm_subst
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_mutex_t         lm_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+/*
+ * Builtin maps
+ */
+struct rewrite_builtin_map {
+#define REWRITE_BUILTIN_MAP_LDAP       0x0201
+       int                             lb_type;
+       char                           *lb_name;
+       void                           *lb_private;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_mutex_t         lb_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+/*
+ * Submatch substitution
+ */
+struct rewrite_submatch {
+#define REWRITE_SUBMATCH_ASIS          0x0000
+#define REWRITE_SUBMATCH_XMAP          0x0001
+#define REWRITE_SUBMATCH_MAP_W_ARG     0x0002
+       int                             ls_type;
+       struct rewrite_map             *ls_map;
+       int                             ls_submatch;
+       /*
+        * The first one represents the index of the submatch in case
+        * the map has single submatch as argument;
+        * the latter represents the map argument scheme in case
+        * the map has substitution string argument form
+        */
+};
+
+/*
+ * Pattern substitution
+ */
+struct rewrite_subst {
+       size_t                          lt_subs_len;
+       struct berval                 **lt_subs;
+       
+       int                             lt_num_submatch;
+       struct rewrite_submatch       **lt_submatch;
+};
+
+/*
+ * Rule
+ */
+struct rewrite_rule {
+       struct rewrite_rule            *lr_next;
+       struct rewrite_rule            *lr_prev;
+
+       char                           *lr_pattern;
+       char                           *lr_subststring;
+       char                           *lr_flagstring;
+       regex_t                         lr_regex;
+
+       /*
+        * I was thinking about some kind of per-rule mutex, but there's
+        * probably no need, because rules after compilation are only read;
+        * however, I need to check whether regexec is reentrant ...
+        */
+
+       struct rewrite_subst           *lr_subst;
+       
+#define REWRITE_REGEX_ICASE            REG_ICASE
+#define REWRITE_REGEX_EXTENDED         REG_EXTENDED    
+       int                             lr_flags;
+
+#define REWRITE_RECURSE                        0x0001
+#define REWRITE_EXEC_ONCE              0x0002
+       int                             lr_mode;
+
+       struct rewrite_action          *lr_action;
+};
+
+/*
+ * Rewrite Context (set of rules)
+ */
+struct rewrite_context {
+       char                           *lc_name;
+       struct rewrite_context         *lc_alias;
+       struct rewrite_rule            *lc_rule;
+};
+
+/*
+ * Session
+ */
+struct rewrite_session {
+       void                           *ls_cookie;
+       Avlnode                        *ls_vars;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_t          ls_vars_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+/*
+ * Variable
+ */
+struct rewrite_var {
+       char                           *lv_name;
+       struct berval                   lv_value;
+};
+
+/*
+ * Operation
+ */
+struct rewrite_op {
+       int                             lo_num_passes;
+       int                             lo_depth;
+       char                           *lo_string;
+       char                           *lo_result;
+       Avlnode                        *lo_vars;
+       const void                     *lo_cookie;
+};
+
+
+/**********
+ * PUBLIC *
+ **********/
+
+/*
+ * Rewrite info
+ */
+struct rewrite_info {
+       Avlnode                        *li_context;
+       Avlnode                        *li_maps;
+       /*
+        * No global mutex because maps are read only at 
+        * config time
+        */
+       Avlnode                        *li_params;
+       Avlnode                        *li_cookies;
+       int                             li_num_cookies;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_t          li_params_mutex;
+        ldap_pvt_thread_rdwr_t          li_cookies_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       /*
+        * Default to `off';
+        * use `rewriteEngine {on|off}' directive to alter
+        */
+       int                             li_state;
+
+       /*
+        * Defaults to REWRITE_MAXPASSES;
+        * use `rewriteMaxPasses numPasses' directive to alter
+        */
+#define REWRITE_MAXPASSES              100
+       int                             li_max_passes;
+
+       /*
+        * Behavior in case a NULL or non-existent context is required
+        */
+       int                             li_rewrite_mode;
+};
+
+/***********
+ * PRIVATE *
+ ***********/
+
+
+/*
+ * Maps
+ */
+
+/*
+ * Parses a map (also in legacy 'x' version)
+ */
+extern struct rewrite_map *
+rewrite_map_parse(
+               struct rewrite_info *info,
+               const char *s,
+               const char **end
+);
+
+extern struct rewrite_map *
+rewrite_xmap_parse(
+               struct rewrite_info *info,
+               const char *s,
+               const char **end
+);
+
+/*
+ * Resolves key in val by means of map (also in legacy 'x' version)
+ */
+extern int
+rewrite_map_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_map *map,
+               struct berval *key,
+               struct berval *val
+);
+
+extern int
+rewrite_xmap_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_map *map,
+               struct berval *key,
+               struct berval *val
+);
+
+
+/*
+ * Submatch substitution
+ */
+
+/*
+ * Compiles a substitution pattern
+ */
+extern struct rewrite_subst *
+rewrite_subst_compile(
+               struct rewrite_info *info,
+               const char *result
+);
+
+/*
+ * Substitutes a portion of rewritten string according to substitution
+ * pattern using submatches
+ */
+extern int
+rewrite_subst_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_subst *subst,
+               const char *string,
+               const regmatch_t *match,
+               struct berval *val
+);
+
+
+/*
+ * Rules
+ */
+
+/*
+ * Compiles the rule and appends it at the running context
+ */
+extern int
+rewrite_rule_compile(
+               struct rewrite_info *info,
+               struct rewrite_context *context,
+               const char *pattern,
+               const char *result,
+               const char *flagstring
+);
+
+/*
+ * Rewrites string according to rule; may return:
+ *      REWRITE_REGEXEC_OK:    fine; if *result != NULL rule matched
+ *                             and rewrite succeeded.
+ *      REWRITE_REGEXEC_STOP:   fine, rule matched; stop processing
+ *                             following rules
+ *      REWRITE_REGEXEC_UNWILL: rule matched; force 'unwilling to perform'
+ *      REWRITE_REGEXEC_ERR:   an error occurred
+ */
+extern int
+rewrite_rule_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_rule *rule,
+               const char *string,
+               char **result
+);
+
+/*
+ * Sessions
+ */
+
+/*
+ * Fetches a struct rewrite_session
+ */
+extern struct rewrite_session *
+rewrite_session_find(
+                struct rewrite_info *info,
+                const void *cookie
+);
+
+/*
+ * Defines and inits a variable with session scope
+ */
+extern int
+rewrite_session_var_set(
+                struct rewrite_info *info,
+                const void *cookie,
+                const char *name,
+                const char *value
+);
+
+/*
+ * Gets a var with session scope
+ */
+extern int
+rewrite_session_var_get(
+                struct rewrite_info *info,
+                const void *cookie,
+                const char *name,
+                struct berval *val
+);
+
+/*
+ * Deletes a session
+ */
+extern int
+rewrite_session_delete(
+                struct rewrite_info *info,
+                const void *cookie
+);
+
+/*
+ * Destroys the cookie tree
+ */
+extern int
+rewrite_session_destroy(
+                struct rewrite_info *info
+);
+
+
+/*
+ * Vars
+ */
+
+/*
+ * Finds a var
+ */
+extern struct rewrite_var *
+rewrite_var_find(
+                Avlnode *tree,
+                const char *name
+);
+
+/*
+ * Inserts a newly created var
+ */
+extern struct rewrite_var *
+rewrite_var_insert(
+                Avlnode **tree,
+                const char *name,
+                const char *value
+);
+
+/*
+ * Sets/inserts a var
+ */
+extern struct rewrite_var *
+rewrite_var_set(
+                Avlnode **tree,
+                const char *name,
+                const char *value,
+                int insert
+);
+
+/*
+ * Deletes a var tree
+ */
+extern int
+rewrite_var_delete(
+                Avlnode *tree
+);
+
+
+/*
+ * Contexts
+ */
+
+/*
+ * Finds the context named rewriteContext in the context tree
+ */
+extern struct rewrite_context *
+rewrite_context_find(
+               struct rewrite_info *info,
+               const char *rewriteContext
+);
+
+/*
+ * Creates a new context called rewriteContext and stores in into the tree
+ */
+extern struct rewrite_context *
+rewrite_context_create(
+               struct rewrite_info *info,
+               const char *rewriteContext
+);
+
+/*
+ * Rewrites string according to context; may return:
+ *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
+ *      STOP:   fine, rule matched; stop processing following rules
+ *      UNWILL: rule matched; force 'unwilling to perform'
+ */
+extern int
+rewrite_context_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_context *context,
+               const char *string,
+               char **result
+);
+
+#endif /* REWRITE_INT_H */
+
diff --git a/libraries/librewrite/rewrite-map.h b/libraries/librewrite/rewrite-map.h
new file mode 100644 (file)
index 0000000..c01a4c7
--- /dev/null
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#ifndef MAP_H
+#define MAP_H
+
+/*
+ * Retrieves a builtin map
+ */
+struct rewrite_builtin_map *
+rewrite_builtin_map_find(
+                struct rewrite_info *info,
+                const char *name
+);
+
+
+/*
+ * LDAP map
+ */
+void  * 
+map_ldap_parse(
+               struct rewrite_info *info,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+);
+
+int
+map_ldap_apply( struct rewrite_builtin_map *map,
+               const char *filter,
+               struct berval *val
+);
+
+#endif /* MAP_H */
diff --git a/libraries/librewrite/rewrite.c b/libraries/librewrite/rewrite.c
new file mode 100644 (file)
index 0000000..6753f99
--- /dev/null
@@ -0,0 +1,147 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include <ac/stdlib.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+
+#include <rewrite.h>
+
+int ldap_debug;
+int ldap_syslog;
+int ldap_syslog_level;
+
+extern int
+read_rewrite(
+               FILE *fin,
+               struct rewrite_info *info
+);
+
+char *
+apply( 
+               FILE *fin, 
+               const char *rewriteContext,
+               const char *arg
+)
+{
+       struct rewrite_info *info;
+       char *string, *sep, *result = NULL;
+       int rc;
+       void *cookie = &info;
+
+       info = rewrite_info_init(REWRITE_MODE_ERR);
+
+       if ( read_rewrite( fin, info ) != 0 ) {
+               exit( EXIT_FAILURE );
+       }
+
+       rewrite_param_set( info, "prog", "rewrite" );
+
+       rewrite_session_init( info, cookie );
+
+       string = strdup( arg );
+       for ( sep = strchr( rewriteContext, ',' );
+                       rewriteContext != NULL;
+                       rewriteContext = sep,
+                       sep ? sep = strchr( rewriteContext, ',' ) : NULL ) {
+               if ( sep != NULL ) {
+                       sep[ 0 ] = '\0';
+                       sep++;
+               }
+               // rc = rewrite( info, rewriteContext, string, &result );
+               rc = rewrite_session( info, rewriteContext, string,
+                               cookie, &result );
+               
+               fprintf( stdout, "%s -> %s\n", string, 
+                               ( result ? result : "unwilling to perform" ) );
+               if ( result == NULL ) {
+                       break;
+               }
+               free( string );
+               string = result;
+       }
+
+       rewrite_session_delete( info, cookie );
+
+       return result;
+}
+
+int
+main( int argc, char *argv[] )
+{
+       FILE *fin = NULL;
+       char *rewriteContext = REWRITE_DEFAULT_CONTEXT;
+
+       while ( 1 ) {
+               int opt = getopt( argc, argv, "f:hr:" );
+
+               if ( opt == EOF ) {
+                       break;
+               }
+
+               switch ( opt ) {
+               case 'f':
+                       fin = fopen( optarg, "r" );
+                       if ( fin == NULL ) {
+                               fprintf( stderr, "unable to open file '%s'\n",
+                                               optarg );
+                               exit( EXIT_FAILURE );
+                       }
+                       break;
+                       
+               case 'h':
+                       fprintf( stderr, 
+       "usage: rewrite [options] string\n"
+       "\n"
+       "\t\t-f file\t\tconfiguration file\n"
+       "\t\t-r rule[s]\tlist of comma-separated rules\n"
+       "\n"
+       "\tsyntax:\n"
+       "\t\trewriteEngine\t{on|off}\n"
+       "\t\trewriteContext\tcontextName [alias aliasedContextName]\n"
+       "\t\trewriteRule\tpattern subst [flags]\n"
+       "\n" 
+                               );
+                       exit( EXIT_SUCCESS );
+                       
+               case 'r':
+                       rewriteContext = strdup( optarg );
+                       break;
+               }
+       }
+       
+       if ( optind >= argc ) {
+               return -1;
+       }
+
+       apply( ( fin ? fin : stdin ), rewriteContext, argv[ optind ] );
+
+       return 0;
+}
+
diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c
new file mode 100644 (file)
index 0000000..2347383
--- /dev/null
@@ -0,0 +1,393 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Appends a rule to the double linked list of rules
+ * Helper for rewrite_rule_compile
+ */
+static int
+append_rule(
+               struct rewrite_context *context,
+               struct rewrite_rule *rule
+)
+{
+       struct rewrite_rule *r;
+
+       assert( context != NULL );
+       assert( context->lc_rule != NULL );
+       assert( rule != NULL );
+
+       for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
+       r->lr_next = rule;
+       rule->lr_prev = r;
+       
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Appends an action to the linked list of actions
+ * Helper for rewrite_rule_compile
+ */
+static int
+append_action(
+               struct rewrite_action *base,
+               struct rewrite_action *action
+)
+{
+       struct rewrite_action *a;
+
+       assert( base != NULL );
+       assert( action != NULL );
+       
+       for ( a = base; a->la_next != NULL; a = a->la_next );
+       a->la_next = action;
+       
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * In case of error it returns NULL and does not free all the memory
+ * it allocated; as this is a once only phase, and an error at this stage
+ * would require the server to stop, there is no need to be paranoid
+ * about memory allocation
+ */
+int
+rewrite_rule_compile(
+               struct rewrite_info *info,
+               struct rewrite_context *context,
+               const char *pattern,
+               const char *result,
+               const char *flagstring
+)
+{
+       int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
+       int mode = REWRITE_RECURSE;
+
+       struct rewrite_rule *rule = NULL;
+       struct rewrite_subst *subst = NULL;
+       struct rewrite_action *action = NULL, *first_action = NULL;
+
+       const char *p;
+
+       assert( info != NULL );
+       assert( context != NULL );
+       assert( pattern != NULL );
+       assert( result != NULL );
+
+       /*
+        * A null flagstring should be allowed
+        */
+
+       /*
+        * Take care of substitution string
+        */
+       subst = rewrite_subst_compile( info, result );
+       if ( subst == NULL ) {
+               return REWRITE_ERR;
+       }
+
+       /*
+        * Take care of flags
+        */
+       for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
+               switch( p[ 0 ] ) {
+                       
+               /*
+                * REGEX flags
+                */
+               case REWRITE_FLAG_HONORCASE:            /* 'C' */
+                       /*
+                        * Honor case (default is case insensitive)
+                        */
+                       flags &= ~REWRITE_REGEX_ICASE;
+                       break;
+                       
+               case REWRITE_FLAG_BASICREGEX:           /* 'R' */
+                       /*
+                        * Use POSIX Basic Regular Expression syntax
+                        * instead of POSIX Extended Regular Expression 
+                        * syntax (default)
+                        */
+                       flags &= ~REWRITE_REGEX_EXTENDED;
+                       break;
+                       
+               /*
+                * Execution mode flags
+                */
+               case REWRITE_FLAG_EXECONCE:             /* ':' */
+                       /*
+                        * Apply rule once only
+                        */
+                       mode &= ~REWRITE_RECURSE;
+                       mode |= REWRITE_EXEC_ONCE;
+                       break;
+               
+               /*
+                * Special action flags
+                */
+               case REWRITE_FLAG_STOP:                 /* '@' */
+                       /*
+                        * Bail out after applying rule
+                        */
+                       action = calloc( sizeof( struct rewrite_action ), 1 );
+                       if ( action == NULL ) {
+                               /* cleanup ... */
+                               return REWRITE_ERR;
+                       }
+                       
+                       mode &= ~REWRITE_RECURSE;
+                       mode |= REWRITE_EXEC_ONCE;
+                       action->la_type = REWRITE_ACTION_STOP;
+                       break;
+                       
+               case REWRITE_FLAG_UNWILLING:            /* '#' */
+                       /*
+                        * Matching objs will be marked as gone!
+                        */
+                       action = calloc( sizeof( struct rewrite_action ), 1 );
+                       if ( action == NULL ) {
+                               /* cleanup ... */
+                               return REWRITE_ERR;
+                       }
+                       
+                       mode &= ~REWRITE_RECURSE;
+                       mode |= REWRITE_EXEC_ONCE;
+                       action->la_type = REWRITE_ACTION_UNWILLING;
+                       break;
+
+               case REWRITE_FLAG_GOTO: {                       /* 'G' */
+                       /*
+                        * After applying rule, jump N rules
+                        */
+
+                       char buf[16], *q;
+                       size_t l;
+                       int *d;
+                       
+                       if ( p[ 1 ] != '{' ) {
+                               /* XXX Need to free stuff */
+                               return REWRITE_ERR;
+                       }
+
+                       q = strchr( p + 2, '}' );
+                       if ( q == NULL ) {
+                               /* XXX Need to free stuff */
+                               return REWRITE_ERR;
+                       }
+
+                       l = q - p + 2;
+                       if ( l >= sizeof( buf ) ) {
+                               /* XXX Need to free stuff */
+                               return REWRITE_ERR;
+                       }
+                       strncpy( buf, p + 2, l );
+                       buf[ l ] = '\0';
+
+                       d = malloc( sizeof( int ) );
+                       if ( d == NULL ) {
+                               /* XXX Need to free stuff */
+                               return REWRITE_ERR;
+                       }
+                       d[ 0 ] = atoi( buf );
+
+                       action = calloc( sizeof( struct rewrite_action ), 1 );
+                       if ( action == NULL ) {
+                               /* cleanup ... */       
+                               return REWRITE_ERR;
+                       }
+                       action->la_type = REWRITE_ACTION_GOTO;
+                       action->la_args = (void *)d;
+
+                       p = q;  /* p is incremented by the for ... */
+               
+                       break;
+               }
+
+               case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
+                       /*
+                        * Ignore errors!
+                        */
+                       action = calloc( sizeof( struct rewrite_action ), 1 );
+                       if ( action == NULL ) {
+                               /* cleanup ... */
+                               return REWRITE_ERR;
+                       }
+                       
+                       action->la_type = REWRITE_ACTION_IGNORE_ERR;
+                       break;
+                       
+               /*
+                * Other flags ...
+                */
+               default:
+                       /*
+                        * Unimplemented feature (complain only)
+                        */
+                       break;
+               }
+               
+               /*
+                * Stupid way to append to a list ...
+                */
+               if ( action != NULL ) {
+                       if ( first_action == NULL ) {
+                               first_action = action;
+                       } else {
+                               append_action( first_action, action );
+                       }
+                       action = NULL;
+               }
+       }
+       
+       /*
+        * Finally, rule allocation
+        */
+       rule = calloc( sizeof( struct rewrite_rule ), 1 );
+       if ( rule == NULL ) {
+               // charray_free( res );
+               /*
+                * XXX need to free the value subst stuff!
+                */
+               return REWRITE_ERR;
+       }
+       
+       /*
+        * REGEX compilation (luckily I don't need to take care of this ...)
+        */
+       if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
+               // charray_free( res );
+               /*
+                *XXX need to free the value subst stuff!
+                */
+               free( rule );
+               return REWRITE_ERR;
+       }
+       
+       /*
+        * Just to remember them ...
+        */
+       rule->lr_pattern = strdup( pattern );
+       rule->lr_subststring = strdup( result );
+       rule->lr_flagstring = strdup( flagstring );
+       
+       /*
+        * Load compiled data into rule
+        */
+       rule->lr_subst = subst;
+
+       /*
+        * Set various parameters
+        */
+       rule->lr_flags = flags;         /* don't really need any longer ... */
+       rule->lr_mode = mode;
+       rule->lr_action = first_action;
+       
+       /*
+        * Append rule at the end of the rewrite context
+        */
+       append_rule( context, rule );
+
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Rewrites string according to rule; may return:
+ *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
+ *      STOP:   fine, rule matched; stop processing following rules
+ *      UNWILL: rule matched; force 'unwilling to perform'
+ */
+int
+rewrite_rule_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_rule *rule,
+               const char *arg,
+               char **result
+               )
+{
+       size_t nmatch = REWRITE_MAX_MATCH;
+       regmatch_t match[ REWRITE_MAX_MATCH ];
+
+       int rc = REWRITE_SUCCESS;
+
+       char *string;
+       struct berval val;
+
+       assert( info != NULL );
+       assert( op != NULL );
+       assert( rule != NULL );
+       assert( arg != NULL );
+       assert( result != NULL );
+
+       *result = NULL;
+
+       string = strdup( arg );
+       if ( string == NULL ) {
+               return REWRITE_REGEXEC_ERR;
+       }
+       
+       /*
+        * In case recursive match is required (default)
+        */
+recurse:;
+
+       Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
+                       " rule='%s' string='%s'\n%s", 
+                       rule->lr_pattern, string, "" );
+       
+       op->lo_num_passes++;
+       if ( regexec( &rule->lr_regex, string, nmatch, match, 0 ) != 0 ) {
+               if ( *result == NULL ) {
+                       free( string );
+               }
+
+               /*
+                * No match is OK; *result = NULL means no match
+                */
+               return REWRITE_REGEXEC_OK;
+       }
+
+       rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
+                       match, &val );
+
+       *result = val.bv_val;
+       free( string );
+
+       if ( rc != REWRITE_REGEXEC_OK ) {
+               return rc;
+       }
+
+       if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE 
+                       && op->lo_num_passes <= info->li_max_passes ) {
+               string = *result;
+               goto recurse;
+       }
+
+       return REWRITE_REGEXEC_OK;
+}
+
diff --git a/libraries/librewrite/session.c b/libraries/librewrite/session.c
new file mode 100644 (file)
index 0000000..b372eff
--- /dev/null
@@ -0,0 +1,330 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two cookies
+ */
+static int
+rewrite_cookie_cmp(
+                const void *c1,
+                const void *c2
+)
+{
+       struct rewrite_session *s1, *s2;
+
+       s1 = ( struct rewrite_session * )c1;
+       s2 = ( struct rewrite_session * )c2;
+
+       assert( s1 != NULL );
+       assert( s2 != NULL );
+       assert( s1->ls_cookie != NULL );
+       assert( s2->ls_cookie != NULL );
+       
+        return ( ( s1->ls_cookie < s2->ls_cookie ) ? -1 :
+                       ( ( s1->ls_cookie > s2->ls_cookie ) ? 1 : 0 ) );
+}
+
+/*
+ * Duplicate cookies?
+ */
+static int
+rewrite_cookie_dup(
+                void *c1,
+                void *c2
+)
+{
+       struct rewrite_session *s1, *s2;
+
+       s1 = ( struct rewrite_session * )c1;
+       s2 = ( struct rewrite_session * )c2;
+       
+       assert( s1 != NULL );
+       assert( s2 != NULL );
+       assert( s1->ls_cookie != NULL );
+       assert( s2->ls_cookie != NULL );
+       
+        return ( ( s1->ls_cookie == s2->ls_cookie ) ? -1 : 0 );
+}
+
+/*
+ * Inits a session
+ */
+struct rewrite_session *
+rewrite_session_init(
+               struct rewrite_info *info,
+               const void *cookie
+)
+{
+       struct rewrite_session *session;
+       int rc;
+
+       assert( info != NULL );
+       assert( cookie != NULL );
+       
+       session = calloc( sizeof( struct rewrite_session ), 1 );
+       if ( session == NULL ) {
+               return NULL;
+       }
+       session->ls_cookie = ( void * )cookie;
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       if ( ldap_pvt_thread_rdwr_init( &session->ls_vars_mutex ) ) {
+               free( session );
+               return NULL;
+       }
+       ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       rc = avl_insert( &info->li_cookies, ( caddr_t )session,
+                       rewrite_cookie_cmp, rewrite_cookie_dup );
+       info->li_num_cookies++;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+        ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       if ( rc != 0 ) {
+               free( session );
+               return NULL;
+       }
+       
+       return session;
+}
+
+/*
+ * Fetches a session
+ */
+struct rewrite_session *
+rewrite_session_find(
+               struct rewrite_info *info,
+               const void *cookie
+)
+{
+       struct rewrite_session *session, tmp;
+
+       assert( info != NULL );
+       assert( cookie != NULL );
+       
+       tmp.ls_cookie = ( void * )cookie;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_rlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       session = ( struct rewrite_session * )avl_find( info->li_cookies,
+                       ( caddr_t )&tmp, rewrite_cookie_cmp );
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_runlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return session;
+               
+}
+
+/*
+ * Defines and inits a var with session scope
+ */
+int
+rewrite_session_var_set(
+               struct rewrite_info *info,
+               const void *cookie,
+               const char *name,
+               const char *value
+)
+{
+       struct rewrite_session *session;
+       struct rewrite_var *var;
+
+       assert( info != NULL );
+       assert( cookie != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+
+       session = rewrite_session_find( info, cookie );
+       if ( session == NULL ) {
+               session = rewrite_session_init( info, cookie );
+       }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       var = rewrite_var_find( session->ls_vars, name );
+       if ( var != NULL ) {
+               assert( var->lv_value.bv_val != NULL );
+               free( var->lv_value.bv_val );
+               var->lv_value.bv_val = strdup( value );
+               var->lv_value.bv_len = strlen( value );
+       } else {
+               var = rewrite_var_insert( &session->ls_vars, name, value );
+               if ( var == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+                       ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+                       return REWRITE_ERR;
+               }
+       }       
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Gets a var with session scope
+ */
+int
+rewrite_session_var_get(
+               struct rewrite_info *info,
+               const void *cookie,
+               const char *name,
+               struct berval *value
+)
+{
+       struct rewrite_session *session;
+       struct rewrite_var *var;
+
+       assert( info != NULL );
+       assert( cookie != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+
+       value->bv_val = NULL;
+       value->bv_len = 0;
+       
+       if ( cookie == NULL ) {
+               return REWRITE_ERR;
+       }
+
+       session = rewrite_session_find( info, cookie );
+       if ( session == NULL ) {
+               return REWRITE_ERR;
+       }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_rlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       var = rewrite_var_find( session->ls_vars, name );
+       if ( var == NULL ) {
+               
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+               
+               return REWRITE_ERR;
+       } else {
+               value->bv_val = strdup( var->lv_value.bv_val );
+               value->bv_len = var->lv_value.bv_len;
+       }
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+        ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Deletes a session
+ */
+int
+rewrite_session_delete(
+               struct rewrite_info *info,
+               const void *cookie
+)
+{
+       struct rewrite_session *session, tmp;
+
+       assert( info != NULL );
+       assert( cookie != NULL );
+
+       tmp.ls_cookie = ( void * )cookie;
+       
+       session = rewrite_session_find( info, cookie );
+
+       if ( session != NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+               rewrite_var_delete( session->ls_vars );
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+               ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+       }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       assert( info->li_num_cookies > 0 );
+       info->li_num_cookies--;
+       
+       /*
+        * There is nothing to delete in the return value
+        */
+       avl_delete( &info->li_cookies, ( caddr_t )&tmp, rewrite_cookie_cmp );
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Destroys the cookie tree
+ */
+int
+rewrite_session_destroy(
+               struct rewrite_info *info
+)
+{
+       int count;
+
+       assert( info != NULL );
+       
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       /*
+        * Should call per-session destruction routine ...
+        */
+       
+       count = avl_free( info->li_cookies, NULL );
+       info->li_cookies = NULL;
+       assert( count == info->li_num_cookies );
+       info->li_num_cookies = 0;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+       ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+       return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/subst.c b/libraries/librewrite/subst.c
new file mode 100644 (file)
index 0000000..2f0645f
--- /dev/null
@@ -0,0 +1,427 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compiles a substitution pattern
+ */
+struct rewrite_subst *
+rewrite_subst_compile(
+               struct rewrite_info *info,
+               const char *result
+)
+{
+       size_t subs_len;
+       struct berval **subs = NULL;
+       struct rewrite_submatch **submatch = NULL;
+
+       struct rewrite_subst *s = NULL;
+
+       const char *begin, *p;
+       int nsub = 0, l;
+
+       assert( info != NULL );
+       assert( result != NULL );
+
+       /*
+        * Take care of substitution string
+        */
+       for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
+               
+               /*
+                * Keep only single escapes '\'
+                */
+               if ( p[ 0 ] != REWRITE_SUBMATCH_ESCAPE ) {
+                       continue;
+               } else if ( p[ 1 ] == REWRITE_SUBMATCH_ESCAPE ) {
+                       continue;
+               }
+
+               nsub++;
+               
+               subs = (struct berval **)realloc( subs,
+                               sizeof( struct berval * )*( nsub + 1 ) );
+               if ( subs == NULL ) {
+                       /* cleanup */
+                       return NULL;
+               }
+               subs[ nsub ] = NULL;
+               
+               /*
+                * I think an `if l > 0' at runtime is better outside than
+                * inside a function call ...
+                */
+               l = p - begin;
+               if ( l > 0 ) {
+                       subs_len += l;
+                       subs[ nsub - 1 ] =
+                               calloc( sizeof( struct berval ), 1 );
+                       if ( subs[ nsub - 1 ] == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+                       subs[ nsub - 1 ]->bv_len = l;
+                       subs[ nsub - 1 ]->bv_val = malloc( l + 1 );
+                       if ( subs[ nsub - 1 ]->bv_val == NULL ) {
+                               return NULL;
+                       }
+                       strncpy( subs[ nsub - 1 ]->bv_val, begin, l );
+                       subs[ nsub - 1 ]->bv_val[ l ] = '\0';
+               } else {
+                       subs[ nsub - 1 ] = NULL;
+               }
+               
+               /*
+                * Substitution pattern
+                */
+               if ( isdigit( p[ 1 ] ) ) {
+                       int d = p[ 1 ] - '0';
+
+                       /*
+                        * Add a new value substitution scheme
+                        */
+                       submatch = realloc( submatch, 
+       sizeof( struct rewrite_submatch * )*( nsub + 1 ) );
+                       if ( submatch == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+                       submatch[ nsub ] = NULL;
+                       submatch[ nsub - 1 ] = 
+       calloc( sizeof(  struct rewrite_submatch ), 1 );
+                       if ( submatch[ nsub - 1 ] == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+                       submatch[ nsub - 1 ]->ls_submatch = d;
+
+                       /*
+                        * If there is no argument, use default
+                        * (substitute substring as is)
+                        */
+                       if ( p[ 2 ] != '{' ) {
+                               submatch[ nsub - 1 ]->ls_type = 
+                                       REWRITE_SUBMATCH_ASIS;
+                               begin = ++p + 1;
+                       } else {
+                               struct rewrite_map *map;
+
+                               submatch[ nsub - 1 ]->ls_type =
+                                       REWRITE_SUBMATCH_XMAP;
+
+                               map = rewrite_xmap_parse( info,
+                                               p + 3, &begin );
+                               if ( map == NULL ) {
+                                       /* cleanup */
+                                       return NULL;
+                               }
+                               p = begin - 1;
+
+                               submatch[ nsub - 1 ]->ls_map = map;
+                       }
+
+               /*
+                * Map with args ...
+                */
+               } else if ( p[ 1 ] == '{' ) {
+                       struct rewrite_map *map;
+
+                       map = rewrite_map_parse( info, p + 2, &begin );
+                       if ( map == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+                       p = begin - 1;
+
+                       /*
+                        * Add a new value substitution scheme
+                        */
+                       submatch = realloc( submatch,
+                                       sizeof( struct rewrite_submatch * )*( nsub + 1 ) );
+                       if ( submatch == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+                       submatch[ nsub ] = NULL;
+                       submatch[ nsub - 1 ] =
+                               calloc( sizeof(  struct rewrite_submatch ), 1 );
+                       if ( submatch[ nsub - 1 ] == NULL ) {
+                               /* cleanup */
+                               return NULL;
+                       }
+
+                       submatch[ nsub - 1 ]->ls_type =
+                               REWRITE_SUBMATCH_MAP_W_ARG;
+                       
+                       submatch[ nsub - 1 ]->ls_map = map;
+               }
+       }
+       
+       /*
+        * Last part of string
+        */
+       subs = realloc( subs, sizeof( struct berval *)*( nsub + 2 ) );
+       if ( subs == NULL ) {
+               /*
+                * XXX need to free the value subst stuff!
+                */
+               free( submatch );
+               return NULL;
+       }
+       
+       subs[ nsub + 1 ] = NULL;
+       l = p - begin;
+       if ( l > 0 ) {
+               subs[ nsub ] = calloc( sizeof( struct berval ), 1 );
+               subs_len += l;
+               subs[ nsub ]->bv_len = l;
+               subs[ nsub ]->bv_val = malloc( l + 1 );
+               strncpy( subs[ nsub ]->bv_val, begin, l );
+               subs[ nsub ]->bv_val[ l ] = '\0';
+       } else {
+               subs[ nsub ] = NULL;
+       }
+
+       s = calloc( sizeof( struct rewrite_subst ), 1 );
+       if ( s == NULL ) {
+               /* cleanup */
+               return NULL;
+       }
+
+       s->lt_subs_len = subs_len;
+        s->lt_subs = subs;
+        s->lt_num_submatch = nsub;
+        s->lt_submatch = submatch;
+
+       return s;
+}
+
+/*
+ * Copies the match referred to by submatch and fetched in string by match.
+ * Helper for rewrite_rule_apply.
+ */
+static int
+submatch_copy(
+               struct rewrite_submatch *submatch,
+               const char *string,
+               const regmatch_t *match,
+               struct berval *val
+)
+{
+       int c, l;
+       const char *s;
+
+       assert( submatch != NULL );
+       assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
+                       || submatch->ls_type == REWRITE_SUBMATCH_XMAP );
+       assert( string != NULL );
+       assert( match != NULL );
+       assert( val != NULL );
+       
+       c = submatch->ls_submatch;
+       s = string + match[ c ].rm_so;
+       l = match[ c ].rm_eo - match[ c ].rm_so;
+       
+       val->bv_val = NULL;
+       val->bv_len = l;
+       val->bv_val = calloc( sizeof( char ), l + 1 );
+       if ( val->bv_val == NULL ) {
+               return REWRITE_ERR;
+       }
+       
+       strncpy( val->bv_val, s, l );
+       val->bv_val[ l ] = '\0';
+       
+       return REWRITE_SUCCESS;
+}
+
+/*
+ * Substitutes a portion of rewritten string according to substitution
+ * pattern using submatches
+ */
+int
+rewrite_subst_apply(
+               struct rewrite_info *info,
+               struct rewrite_op *op,
+               struct rewrite_subst *subst,
+               const char *string,
+               const regmatch_t *match,
+               struct berval *val
+)
+{
+       struct berval *submatch = NULL;
+       char *res = NULL;
+       int n, l, cl;
+       int rc = REWRITE_REGEXEC_OK;
+
+       assert( info != NULL );
+       assert( op != NULL );
+       assert( subst != NULL );
+       assert( string != NULL );
+       assert( match != NULL );
+       assert( val != NULL );
+
+       val->bv_val = NULL;
+       val->bv_len = 0;
+
+       /*
+        * Prepare room for submatch expansion
+        */
+       submatch = calloc( sizeof( struct berval ),
+                       subst->lt_num_submatch );
+       if ( submatch == NULL ) {
+               return REWRITE_REGEXEC_ERR;
+       }
+       
+       /*
+        * Resolve submatches (simple subst, map expansion and so).
+        */
+       for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
+               struct berval key;
+               int rc;
+               
+               /*
+                * Get key
+                */
+               switch( subst->lt_submatch[ n ]->ls_type ) {
+               case REWRITE_SUBMATCH_ASIS:
+               case REWRITE_SUBMATCH_XMAP:
+                       rc = submatch_copy( subst->lt_submatch[ n ],
+                                       string, match, &key );
+                       if ( rc != REWRITE_SUCCESS ) {
+                               free( submatch );
+                               return REWRITE_REGEXEC_ERR;
+                       }
+                       break;
+                       
+               case REWRITE_SUBMATCH_MAP_W_ARG:
+                       switch ( subst->lt_submatch[ n ]->ls_map->lm_type ) {
+                       case REWRITE_MAP_GET_OP_VAR:
+                       case REWRITE_MAP_GET_SESN_VAR:
+                       case REWRITE_MAP_GET_PARAM:
+                               rc = REWRITE_SUCCESS;
+                               break;
+                       default:
+                               rc = rewrite_subst_apply( info, op, 
+                                       subst->lt_submatch[ n ]->ls_map->lm_subst,
+                                       string, match, &key);
+                       }
+                       
+                       if ( rc != REWRITE_SUCCESS ) {
+                               free( submatch );
+                               return REWRITE_REGEXEC_ERR;
+                       }
+                       break;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY, "Not Implemented\n%s%s%s", 
+                                       "", "", "" );
+                       rc = REWRITE_ERR;
+                       break;
+               }
+               
+               if ( rc != REWRITE_SUCCESS ) {
+                       free( submatch );
+                       return REWRITE_REGEXEC_ERR;
+               }
+
+               /*
+                * Resolve key
+                */
+               switch ( subst->lt_submatch[ n ]->ls_type ) {
+               case REWRITE_SUBMATCH_ASIS:
+                       submatch[ n ] = key;
+                       rc = REWRITE_SUCCESS;
+                       break;
+                       
+               case REWRITE_SUBMATCH_XMAP:
+                       rc = rewrite_xmap_apply( info, op,
+                                       subst->lt_submatch[ n ]->ls_map,
+                                       &key, &submatch[ n ] );
+                       break;
+                       
+               case REWRITE_SUBMATCH_MAP_W_ARG:
+                       rc = rewrite_map_apply( info, op,
+                                       subst->lt_submatch[ n ]->ls_map,
+                                       &key, &submatch[ n ] );
+                       break;
+                                               
+               default:
+                       /*
+                        * When implemented, this might return the
+                         * exit status of a rewrite context,
+                         * which may include a stop, or an
+                         * unwilling to perform
+                         */
+                       rc = REWRITE_ERR;
+                       break;
+               }
+               
+               if ( rc != REWRITE_SUCCESS ) {
+                       free( submatch );
+                       return REWRITE_REGEXEC_ERR;
+               }
+               
+               /*
+                 * Increment the length of the resulting string
+                 */
+               l += submatch[ n ].bv_len;
+       }
+       
+       /*
+         * Alloc result buffer as big as the constant part 
+         * of the subst pattern and initialize it
+         */
+       l += subst->lt_subs_len;
+       res = calloc( sizeof( char ), l + 1 );
+       if ( res == NULL ) {
+               free( submatch );
+               return REWRITE_REGEXEC_ERR;
+       }
+
+       /*
+        * Apply submatches (possibly resolved thru maps
+        */
+        for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
+               if ( subst->lt_subs[ n ] != NULL ) {
+                       strcpy( res + cl, subst->lt_subs[ n ]->bv_val);
+                       cl += subst->lt_subs[ n ]->bv_len;
+               }
+               strcpy( res + cl, submatch[ n ].bv_val );
+               cl += submatch[ n ].bv_len;
+               free( submatch[ n ].bv_val );
+       }
+       if ( subst->lt_subs[ n ] != NULL ) {
+               strcpy( res + cl, subst->lt_subs[ n ]->bv_val );
+       }
+
+       val->bv_val = res;
+       val->bv_len = l;
+       
+       return rc;
+}
+
diff --git a/libraries/librewrite/var.c b/libraries/librewrite/var.c
new file mode 100644 (file)
index 0000000..ca39ed1
--- /dev/null
@@ -0,0 +1,199 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission.  Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.  Since few users
+ * ever read sources, credits should appear in the documentation.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two vars
+ */
+static int
+rewrite_var_cmp(
+               const void *c1,
+               const void *c2
+)
+{
+       const struct rewrite_var *v1, *v2;
+
+       v1 = ( struct rewrite_var * )c1;
+       v2 = ( struct rewrite_var * )c2;
+       
+       assert( v1 != NULL );
+       assert( v2 != NULL );
+       assert( v1->lv_name != NULL );
+       assert( v2->lv_name != NULL );
+
+       return strcasecmp( v1->lv_name, v2->lv_name );
+}
+
+/*
+ * Duplicate var ?
+ */
+static int
+rewrite_var_dup(
+               void *c1,
+               void *c2
+)
+{
+       struct rewrite_var *v1, *v2;
+
+       v1 = ( struct rewrite_var * )c1;
+       v2 = ( struct rewrite_var * )c2;
+
+       assert( v1 != NULL );
+       assert( v2 != NULL );
+       assert( v1->lv_name != NULL );
+       assert( v2->lv_name != NULL );
+
+       return ( strcasecmp( v1->lv_name, v2->lv_name ) == 0 ? -1 : 0 );
+}
+
+/*
+ * Finds a var
+ */
+struct rewrite_var *
+rewrite_var_find(
+               Avlnode *tree,
+               const char *name
+)
+{
+       struct rewrite_var var;
+
+       assert( name != NULL );
+
+       var.lv_name = ( char * )name;
+       return ( struct rewrite_var * )avl_find( tree, 
+                       ( caddr_t )&var, rewrite_var_cmp );
+}
+
+/*
+ * Inserts a newly created var
+ */
+struct rewrite_var *
+rewrite_var_insert(
+               Avlnode **tree,
+               const char *name,
+               const char *value
+)
+{
+       struct rewrite_var *var;
+       int rc;
+
+       assert( tree != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+       
+       var = calloc( sizeof( struct rewrite_var ), 1 );
+       if ( var == NULL ) {
+               return NULL;
+       }
+       var->lv_name = ( char * )strdup( name );
+       if ( var->lv_name == NULL ) {
+               free( var );
+               return NULL;
+       }
+       var->lv_value.bv_val = strdup( value );
+       if ( var->lv_value.bv_val == NULL ) {
+               free( var );
+               free( var->lv_name );
+               return NULL;
+       }
+       var->lv_value.bv_len = strlen( value );
+       rc = avl_insert( tree, ( caddr_t )var,
+                       rewrite_var_cmp, rewrite_var_dup );
+       if ( rc != 0 ) { 
+               free( var );
+               free( var->lv_name );
+               free( var->lv_value.bv_val );
+               return NULL;
+       }
+
+       return var;
+}
+
+/*
+ * Sets/inserts a var
+ */
+struct rewrite_var *
+rewrite_var_set(
+               Avlnode **tree,
+               const char *name,
+               const char *value,
+               int insert
+)
+{
+       struct rewrite_var *var;
+
+       assert( tree != NULL );
+       assert( name != NULL );
+       assert( value != NULL );
+       
+       var = rewrite_var_find( *tree, name );
+       if ( var == NULL ) {
+               if ( insert ) {
+                       return rewrite_var_insert( tree, name, value );
+               } else {
+                       return NULL;
+               }
+       } else {
+               assert( var->lv_value.bv_val != NULL );
+
+               free( var->lv_value.bv_val );
+               var->lv_value.bv_val = ( char * )value;
+               var->lv_value.bv_len = strlen( value );
+       }
+
+       return var;
+}
+
+/*
+ * Frees a var
+ */
+static void 
+rewrite_var_free(
+                struct rewrite_var *var
+)
+{
+       assert( var != NULL );
+
+       assert( var->lv_name != NULL );
+       assert( var->lv_value.bv_val != NULL );
+
+       free( var->lv_name );
+       free( var->lv_value.bv_val );
+}
+
+/*
+ * Deletes a var tree
+ */
+int
+rewrite_var_delete(
+               Avlnode *tree
+)
+{
+       avl_free( tree, ( AVL_FREE )rewrite_var_free );
+       return REWRITE_SUCCESS;
+}
+
index de1b4c1a9703bbb8ffc783f60fc122c665e5d46c..cccc79e2bae61f2cd9dec622bad034c2597dc6af 100644 (file)
@@ -58,17 +58,36 @@ ldap_back_add(
        int i;
        Attribute *a;
        LDAPMod **attrs;
-       char *mdn, *mapped;
+       char *mdn = NULL, *mapped;
 
        lc = ldap_back_getconn(li, conn, op);
        if ( !lc || !ldap_back_dobind( lc, op ) ) {
                return( -1 );
        }
 
-       mdn = ldap_back_dn_massage( li, ch_strdup( e->e_dn ), 0 );
+       /*
+        * Rewrite the add dn, if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch (rewrite_session( li->rwinfo, "addDn", e->e_dn, conn, &mdn )) {
+       case REWRITE_REGEXEC_OK:
        if ( mdn == NULL ) {
+                       mdn = e->e_dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> addDn: \"%s\" -> \"%s\"\n%s", 
+                               e->e_dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+               
+       case REWRITE_REGEXEC_ERR:
                return( -1 );
        }
+#else /* !ENABLE_REWRITE */
+       mdn = ldap_back_dn_massage( li, ch_strdup( e->e_dn ), 0 );
+#endif /* !ENABLE_REWRITE */
 
        /* Count number of attributes in entry */
        for (i = 1, a = e->e_attrs; a; i++, a = a->a_next)
@@ -78,16 +97,52 @@ ldap_back_add(
        attrs = (LDAPMod **)ch_malloc(sizeof(LDAPMod *)*i);
 
        for (i=0, a=e->e_attrs; a; a=a->a_next) {
+               /*
+                * lastmod should always be <off>, so that
+                * creation/modification operational attrs
+                * of the target directory are used, if available
+                */
+#if 0
+               if ( !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_creatorsName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_createTimestamp->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifiersName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val )
+               ) {
+                       continue;
+               }
+#endif
+               
                mapped = ldap_back_map(&li->at_map, a->a_desc->ad_cname->bv_val, 0);
-               if (mapped != NULL) {
-                       attrs[i] = (LDAPMod *)ch_malloc(sizeof(LDAPMod));
-                       if (attrs[i] != NULL) {
-                               attrs[i]->mod_op = LDAP_MOD_BVALUES;
-                               attrs[i]->mod_type = mapped;
-                               attrs[i]->mod_vals.modv_bvals = a->a_vals;
-                               i++;
-                       }
+               if (mapped == NULL) {
+                       continue;
+               }
+
+               attrs[i] = (LDAPMod *)ch_malloc(sizeof(LDAPMod));
+               if (attrs[i] == NULL) {
+                       continue;
+               }
+
+               attrs[i]->mod_op = LDAP_MOD_BVALUES;
+               attrs[i]->mod_type = mapped;
+
+#ifdef ENABLE_REWRITE
+               /*
+                * FIXME: dn-valued attrs should be rewritten
+                * to allow their use in ACLs at the back-ldap
+                * level.
+                */
+               if ( strcmp( a->a_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       ldap_dnattr_rewrite( li->rwinfo, a->a_vals, conn );
                }
+#endif /* ENABLE_REWRITE */
+
+               attrs[i]->mod_vals.modv_bvals = a->a_vals;
+               i++;
        }
        attrs[i] = NULL;
 
@@ -95,6 +150,60 @@ ldap_back_add(
        for (--i; i>= 0; --i)
                free(attrs[i]);
        free(attrs);
+#ifdef ENABLE_REWRITE
+       if ( mdn != e->e_dn ) {
+#endif /* ENABLE_REWRITE */
        free( mdn );
-       return( ldap_back_op_result( lc, op ));
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
+       
+       return( ldap_back_op_result( lc, op ) );
+}
+
+#ifdef ENABLE_REWRITE
+int
+ldap_dnattr_rewrite(
+               struct rewrite_info     *rwinfo,
+               struct berval           **a_vals,
+               void                    *cookie
+)
+{
+       int j;
+       char *mattr;
+       
+       for ( j = 0; a_vals[ j ] != NULL; j++ ) {
+               switch ( rewrite_session( rwinfo, "bindDn", a_vals[ j ]->bv_val,
+                                       cookie, &mattr )) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mattr == NULL ) {
+                               /* no substitution */
+                               continue;
+                       }
+                       Debug( LDAP_DEBUG_ARGS,
+                                       "rw> bindDn (in add of dn-valued attr):"
+                                       " \"%s\" -> \"%s\"\n%s",
+                                       a_vals[ j ]->bv_val, mattr, "" );
+                       
+                       free( a_vals[ j ]->bv_val );
+                       a_vals[ j ]->bv_val = mattr;
+                       a_vals[ j ]->bv_len = strlen( mattr );
+                       
+                       break;
+                       
+               case REWRITE_REGEXEC_UNWILLING:
+                       
+               case REWRITE_REGEXEC_ERR:
+                       /*
+                        * FIXME: better give up,
+                        * skip the attribute
+                        * or leave it untouched?
+                        */
+                       break;
+               }
+       }
+       
+       return 0;
 }
+#endif /* ENABLE_REWRITE */
+
index aafb09f794f0701ec93864461ff8494c4b30c516..45fb33bea6074df2c6e7aeaae0fffac5c608025d 100644 (file)
 
 #include "external.h"
 
+/* String rewrite library */
+#ifdef ENABLE_REWRITE
+#include "rewrite.h"
+#endif /* ENABLE_REWRITE */
+
 LDAP_BEGIN_DECL
 
 struct slap_conn;
@@ -66,14 +71,15 @@ struct ldapmapping {
 
 struct ldapinfo {
        char *url;
-#if 0 /* unused! */
-       char *suffix;
-#endif /* 0 */
-       char **suffix_massage;
        char *binddn;
        char *bindpw;
        ldap_pvt_thread_mutex_t         conn_mutex;
        Avlnode *conntree;
+#ifdef ENABLE_REWRITE
+       struct rewrite_info *rwinfo;
+#else /* !ENABLE_REWRITE */
+       char **suffix_massage;
+#endif /* !ENABLE_REWRITE */
 
        struct ldapmap oc_map;
        struct ldapmap at_map;
@@ -89,15 +95,33 @@ int back_ldap_LTX_init_module(int argc, char *argv[]);
 char *ldap_back_dn_massage(struct ldapinfo *li, char *dn, int normalized);
 char *ldap_back_dn_restore(struct ldapinfo *li, char *dn, int normalized);
 
-int conn_cmp(const void *, const void *);
-int conn_dup(void *, void *);
+extern int ldap_back_conn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conn_dup( void *c1, void *c2 );
 
 int mapping_cmp (const void *, const void *);
 int mapping_dup (void *, void *);
 
 char *ldap_back_map ( struct ldapmap *map, char *s, int remap );
-char *ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap );
-char **ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap );
+char *
+ldap_back_map_filter(
+               struct ldapmap *at_map,
+               struct ldapmap *oc_map,
+               char *f,
+               int remap
+);
+char **
+ldap_back_map_attrs(
+               struct ldapmap *at_map,
+               char **a,
+               int remap
+);
+
+extern void mapping_free ( struct ldapmapping *mapping );
+
+#ifdef ENABLE_REWRITE
+extern int suffix_massage_config( struct rewrite_info *info, int argc, char **argv );
+extern int ldap_dnattr_rewrite( struct rewrite_info *rwinfo, struct berval **a_vals, void *cookie );
+#endif /* ENABLE_REWRITE */
 
 LDAP_END_DECL
 
index 89ef3f517b27dcb69e15e71c52a783112daf71f0..0a6c7aed00612c5dc98c25445863b65c34c78454 100644 (file)
@@ -47,6 +47,8 @@
 #include "slap.h"
 #include "back-ldap.h"
 
+#define PRINT_CONNTREE 0
+
 int
 ldap_back_bind(
     Backend            *be,
@@ -72,30 +74,56 @@ ldap_back_bind(
                return( -1 );
        }
 
-       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
-       if ( mdn == NULL ) {
-               return -1;
+       /*
+        * Rewrite the bind dn if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "bindDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> bindDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
+               return( -1 );
        }
+#else /* !ENABLE_REWRITE */
+       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
+#endif /* !ENABLE_REWRITE */
 
-       if (ldap_bind_s(lc->ld, mdn, cred->bv_val, method) != LDAP_SUCCESS) {
+       rc = ldap_bind_s(lc->ld, mdn, cred->bv_val, method);
+       if (rc != LDAP_SUCCESS) {
                rc = ldap_back_op_result( lc, op );
        } else {
                lc->bound = 1;
        }
-       
+
+#ifdef ENABLE_REWRITE  
+       if ( mdn != dn ) {
+#endif /* ENABLE_REWRITE */
        free( mdn );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
        
        return( rc );
 }
 
 /*
- * conn_cmp
+ * ldap_back_conn_cmp
  *
  * compares two struct ldapconn based on the value of the conn pointer;
  * used by avl stuff
  */
 int
-conn_cmp(
+ldap_back_conn_cmp(
        const void *c1,
        const void *c2
        )
@@ -107,13 +135,13 @@ conn_cmp(
 }
 
 /*
- * conn_dup
+ * ldap_back_conn_dup
  *
  * returns -1 in case a duplicate struct ldapconn has been inserted;
  * used by avl stuff
  */
 int
-conn_dup(
+ldap_back_conn_dup(
        void *c1,
        void *c2
        )
@@ -124,6 +152,7 @@ conn_dup(
        return( ( lc1->conn == lc2->conn ) ? -1 : 0 );
 }
 
+#if PRINT_CONNTREE > 0
 static void ravl_print( Avlnode *root, int depth )
 {
        int     i;
@@ -136,7 +165,7 @@ static void ravl_print( Avlnode *root, int depth )
        for ( i = 0; i < depth; i++ )
                printf( "   " );
 
-       printf( "c(%d) %d\n", ((struct ldapconn *) root->avl_data)->conn->c_connid, root->avl_bf );
+       printf( "c(%ld) %d\n", ((struct ldapconn *) root->avl_data)->conn->c_connid, root->avl_bf );
        
        ravl_print( root->avl_left, depth+1 );
 }
@@ -153,6 +182,7 @@ static void myprint( Avlnode *root )
        
        printf( "********\n" );
 }
+#endif /* PRINT_CONNTREE */
 
 struct ldapconn *
 ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
@@ -164,7 +194,7 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
        lc_curr.conn = conn;
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
        lc = (struct ldapconn *)avl_find( li->conntree, 
-               (caddr_t)&lc_curr, conn_cmp );
+               (caddr_t)&lc_curr, ldap_back_conn_cmp );
        ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
 
        /* Looks like we didn't get a bind. Open a new session... */
@@ -186,9 +216,49 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
                lc = (struct ldapconn *)ch_malloc(sizeof(struct ldapconn));
                lc->conn = conn;
                lc->ld = ld;
+
+#ifdef ENABLE_REWRITE
+               /*
+                * Sets a cookie for the rewrite session
+                */
+               ( void )rewrite_session_init( li->rwinfo, conn );
+#endif /* ENABLE_REWRITE */
+
                if ( lc->conn->c_cdn != NULL && lc->conn->c_cdn[0] != '\0' ) {
+                       
+                       /*
+                        * Rewrite the bind dn if needed
+                        */
+#ifdef ENABLE_REWRITE                  
+                       lc->bound_dn = NULL;
+                       switch ( rewrite_session( li->rwinfo, "bindDn",
+                                               lc->conn->c_cdn, conn,
+                                               &lc->bound_dn ) ) {
+                       case REWRITE_REGEXEC_OK:
+                               if ( lc->bound_dn == NULL ) {
+                                       lc->bound_dn = 
+                                               ch_strdup( lc->conn->c_cdn );
+                               }
+                               Debug( LDAP_DEBUG_ARGS,
+                                               "rw> bindDn: \"%s\" ->"
+                                              " \"%s\"\n%s",
+                                               lc->conn->c_cdn,
+                                               lc->bound_dn, "" );
+                               break;
+                               
+                       case REWRITE_REGEXEC_UNWILLING:
+                               send_ldap_result( conn, op,
+                                               LDAP_UNWILLING_TO_PERFORM,
+                                               NULL, "Unwilling to perform",
+                                               NULL, NULL );
+                               
+                       case REWRITE_REGEXEC_ERR:
+                               return( NULL );
+                       }
+#else /* !ENABLE_REWRITE */
                        lc->bound_dn = ldap_back_dn_massage( li,
-                               ch_strdup( lc->conn->c_cdn ), 0 );
+                                       ch_strdup( lc->conn->c_cdn ), 0 );              
+#endif /* !ENABLE_REWRITE */
                } else {
                        lc->bound_dn = NULL;
                }
@@ -197,17 +267,17 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
                /* Inserts the newly created ldapconn in the avl tree */
                ldap_pvt_thread_mutex_lock( &li->conn_mutex );
                err = avl_insert( &li->conntree, (caddr_t)lc,
-                       conn_cmp, conn_dup );
+                       ldap_back_conn_cmp, ldap_back_conn_dup );
 
-#if 0
+#if PRINT_CONNTREE > 0
                myprint( li->conntree );
-#endif
+#endif /* PRINT_CONNTREE */
                
                ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
 
                Debug( LDAP_DEBUG_TRACE,
-                       "=>ldap_back_getconn: conn %d inserted\n",
-                       lc->conn->c_connid, 0, 0 );
+                       "=>ldap_back_getconn: conn %ld inserted%s%s\n",
+                       lc->conn->c_connid, "", "" );
                
                /* Err could be -1 in case a duplicate ldapconn is inserted */
                if ( err != 0 ) {
@@ -218,8 +288,8 @@ ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
                }
        } else {
                Debug( LDAP_DEBUG_TRACE,
-                       "=>ldap_back_getconn: conn %d fetched\n",
-                       lc->conn->c_connid, 0, 0 );
+                       "=>ldap_back_getconn: conn %ld fetched%s%s\n",
+                       lc->conn->c_connid, "", "" );
        }
        
        return( lc );
@@ -305,9 +375,23 @@ ldap_back_op_result(struct ldapconn *lc, Operation *op)
        ldap_get_option(lc->ld, LDAP_OPT_ERROR_STRING, &msg);
        ldap_get_option(lc->ld, LDAP_OPT_MATCHED_DN, &match);
        err = ldap_back_map_result(err);
+
+#ifdef ENABLE_REWRITE
+       
+       /*
+        * need rewrite info; mmmh ...
+        */
+
+#else /* !ENABLE_REWRITE */
+
        send_ldap_result( lc->conn, op, err, match, msg, NULL, NULL );
        /* better test the pointers before freeing? */
-       if ( match ) free( match );
+       if ( match ) {
+               free( match );
+       }
+#endif /* !ENABLE_REWRITE */
+
        if ( msg ) free( msg );
        return( (err==LDAP_SUCCESS) ? 0 : -1 );
 }
+
index c8033cc1647349ebbd9533c26c38e6b4760b98e6..99bd3972436fba71e61fd12168f2cd0e333689c3 100644 (file)
@@ -64,10 +64,32 @@ ldap_back_compare(
                return( -1 );
        }
 
+       /*
+        * Rewrite the compare dn, if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "compareDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> compareDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+               
+       case REWRITE_REGEXEC_ERR:
+               return( -1 );
+       }
+#else /* !ENABLE_REWRITE */
        mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
-       if ( mdn == NULL ) {
-               return -1;
-       }       
+       if ( mdn == NULL ) {
+               return -1;
+       }
+#endif /* !ENABLE_REWRITE */
 
        mapped_oc = ldap_back_map(&li->oc_map, ava->aa_desc->ad_cname->bv_val, 0);
        if (mapped_oc == NULL)
@@ -79,7 +101,13 @@ ldap_back_compare(
 
        ldap_compare_s( lc->ld, mdn, mapped_oc, mapped_at );
 
-       free( mdn );
+#ifdef ENABLE_REWRITE
+       if ( mdn != dn ) {
+#endif /* ENABLE_REWRITE */
+               free( mdn );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
        
        return( ldap_back_op_result( lc, op ) );
 }
index 5fb1f3a6c1ea63396e48dc3f79b9eeab91e510bb..f493b0d8e53fddd747b7cd851960da331992bf4c 100644 (file)
@@ -113,34 +113,59 @@ ldap_back_db_config(
        
        /* dn massaging */
        } else if ( strcasecmp( argv[0], "suffixmassage" ) == 0 ) {
+#ifndef ENABLE_REWRITE
                char *dn, *massaged_dn;
+#endif /* ENABLE_REWRITE */
                BackendDB *tmp_be;
                
+               /*
+                * syntax:
+                * 
+                *      suffixmassage <suffix> <massaged suffix>
+                *
+                * the <suffix> field must be defined as a valid suffix
+                * (or suffixAlias?) for the current database;
+                * the <massaged suffix> shouldn't have already been
+                * defined as a valid suffix or suffixAlias for the 
+                * current server
+                */
                if ( argc != 3 ) {
-                       fprintf( stderr,
-       "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
+                       fprintf( stderr, "%s: line %d: syntax is"
+                                      " \"suffixMassage <suffix>"
+                                      " <massaged suffix>\"\n",
                                fname, lineno );
                        return( 1 );
                }
                
                tmp_be = select_backend( argv[1], 0 );
                if ( tmp_be != NULL && tmp_be != be ) {
-                       fprintf( stderr,
-       "%s: line %d: suffix already in use by another backend in"
-       " \"suffixMassage <suffix> <massaged suffix>\"\n",
+                       fprintf( stderr, "%s: line %d: suffix already in use"
+                                      " by another backend in"
+                                      " \"suffixMassage <suffix>"
+                                      " <massaged suffix>\"\n",
                                fname, lineno );
                        return( 1 );                                            
                }
 
                tmp_be = select_backend( argv[2], 0 );
                if ( tmp_be != NULL ) {
-                       fprintf( stderr,
-        "%s: line %d: massaged suffix already in use by another backend in" 
-        " \"suffixMassage <suffix> <massaged suffix>\"\n",
+                       fprintf( stderr, "%s: line %d: massaged suffix"
+                                      " already in use by another backend in" 
+                                      " \"suffixMassage <suffix>"
+                                      " <massaged suffix>\"\n",
                                 fname, lineno );
                         return( 1 );
                }
-               
+
+#ifdef ENABLE_REWRITE
+               /*
+                * The suffix massaging is emulated by means of the
+                * rewrite capabilities
+                * FIXME: no extra rewrite capabilities should be added
+                * to the database
+                */
+               return suffix_massage_config( li->rwinfo, argc, argv );
+#else /* !ENABLE_REWRITE */
                dn = ch_strdup( argv[1] );
                charray_add( &li->suffix_massage, dn );
                (void) dn_normalize( dn );
@@ -153,7 +178,14 @@ ldap_back_db_config(
                
                free( dn );
                free( massaged_dn );
+#endif /* !ENABLE_REWRITE */
 
+#ifdef ENABLE_REWRITE
+       /* rewrite stuff ... */
+       } else if ( strncasecmp( argv[0], "rewrite", 7 ) == 0 ) {
+               return rewrite_parse( li->rwinfo, fname, lineno, argc, argv );
+#endif /* ENABLE_REWRITE */
+               
        /* objectclass/attribute mapping */
        } else if ( strcasecmp( argv[0], "map" ) == 0 ) {
                struct ldapmap *map;
@@ -293,7 +325,12 @@ ldap_back_map ( struct ldapmap *map, char *s, int remap )
 }
 
 char *
-ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap )
+ldap_back_map_filter(
+               struct ldapmap *at_map,
+               struct ldapmap *oc_map,
+               char *f,
+               int remap
+)
 {
        char *nf, *m, *p, *q, *s, c;
        int len, extra, plen, in_quote;
@@ -315,7 +352,7 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap )
        s = nf;
        q = NULL;
        in_quote = 0;
-       for (p = f; c = *p; p++) {
+       for (p = f; (c = *p); p++) {
                if (c == '"') {
                        in_quote = !in_quote;
                        if (q != NULL) {
@@ -339,9 +376,9 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap )
                } else {
                        if (q != NULL) {
                                *p = 0;
-                               m = ldap_back_map(&li->at_map, q, remap);
+                               m = ldap_back_map(at_map, q, remap);
                                if (m == NULL)
-                                       m = ldap_back_map(&li->oc_map, q, remap);
+                                       m = ldap_back_map(oc_map, q, remap);
                                if (m == NULL) {
                                        m = q;
                                }
@@ -374,7 +411,11 @@ ldap_back_map_filter ( struct ldapinfo *li, char *f, int remap )
 }
 
 char **
-ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap )
+ldap_back_map_attrs(
+               struct ldapmap *at_map,
+               char **a,
+               int remap
+)
 {
        int i, j, count;
        char **na, *mapped;
@@ -391,7 +432,7 @@ ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap )
                return(NULL);
 
        for (i = 0, j = 0; i < count; i++) {
-               mapped = ldap_back_map(&li->at_map, a[i], remap);
+               mapped = ldap_back_map(at_map, a[i], remap);
                if (mapped != NULL) {
                        mapped = ch_strdup(mapped);
                        if (mapped == NULL) {
@@ -405,3 +446,137 @@ ldap_back_map_attrs ( struct ldapinfo *li, char **a, int remap )
        return(na);
 }
 
+#ifdef ENABLE_REWRITE
+static char *
+suffix_massage_regexize( const char *s )
+{
+       char *res, *p, *r;
+       int i;
+
+       for ( i = 0, p = ( char * )s; 
+                       ( r = strchr( p, ',' ) ) != NULL; 
+                       p = r + 1, i++ )
+               ;
+
+       res = ch_calloc( sizeof( char ), strlen( s ) + 4 + 4*i + 1 );
+
+       strcpy( res, "(.*)" );
+       for ( i = 0, p = ( char * )s;
+                       ( r = strchr( p, ',' ) ) != NULL;
+                       p = r + 1 , i++ ) {
+               strncat( res, p, r - p + 1 );
+               strcat( res, "[ ]?" );
+
+               if ( r[ 1 ] == ' ' ) {
+                       r++;
+               }
+       }
+       strcat( res, p );
+
+       return res;
+}
+
+static char *
+suffix_massage_patternize( const char *s, int normalize )
+{
+       char *res;
+
+       res = ch_calloc( sizeof( char ), strlen( s ) + 3 );
+
+       sprintf( res, "%%1%s", s );
+
+       if ( normalize ) {
+               char *out = dn_normalize( res + 2 );
+               if ( out != res + 2 ) {
+                       strcpy( res + 2, out );
+                       free( out );
+               }
+       }
+
+       return res;
+}
+
+int
+suffix_massage_config( 
+               struct rewrite_info *info,
+               int argc,
+               char **argv
+)
+{
+       char *rargv[ 5 ];
+
+       rargv[ 0 ] = "rewriteEngine";
+       rargv[ 1 ] = "on";
+       rargv[ 2 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 1, 2, rargv );
+
+       rargv[ 0 ] = "rewriteContext";
+       rargv[ 1 ] = "default";
+       rargv[ 2 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 2, 2, rargv );
+
+       rargv[ 0 ] = "rewriteRule";
+       rargv[ 1 ] = suffix_massage_regexize( argv[ 1 ] );
+       rargv[ 2 ] = suffix_massage_patternize( argv[ 2 ], 0 );
+       rargv[ 3 ] = ":";
+       rargv[ 4 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 3, 4, rargv );
+       ch_free( rargv[ 1 ] );
+       ch_free( rargv[ 2 ] );
+       
+       rargv[ 0 ] = "rewriteContext";
+       rargv[ 1 ] = "searchResult";
+       rargv[ 2 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 4, 2, rargv );
+       
+       rargv[ 0 ] = "rewriteRule";
+       rargv[ 1 ] = suffix_massage_regexize( argv[ 2 ] );
+       rargv[ 2 ] = suffix_massage_patternize( argv[ 1 ], 0 );
+       rargv[ 3 ] = ":";
+       rargv[ 4 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 5, 4, rargv );
+       ch_free( rargv[ 1 ] );
+       ch_free( rargv[ 2 ] );
+
+       /*
+        * the filter should be rewritten as
+        * 
+        * rewriteRule
+        *      "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)"
+        *      "%1member=%2,dc=example,dc=com%3"
+        *
+        * where "o=Foo Bar, c=US" is the virtual naming context,
+        * and "dc=example, dc=com" is the real naming context
+        */
+       rargv[ 0 ] = "rewriteContext";
+       rargv[ 1 ] = "searchFilter";
+       rargv[ 2 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 6, 2, rargv );
+
+#if 0 /*  matched is not normalized */
+       rargv[ 0 ] = "rewriteContext";
+       rargv[ 1 ] = "matchedDn";
+       rargv[ 2 ] = "alias";
+       rargv[ 3 ] = "searchResult";
+       rargv[ 4 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 7, 4, rargv );
+#else /* normalize matched */
+       rargv[ 0 ] = "rewriteContext";
+       rargv[ 1 ] = "matchedDn";
+       rargv[ 2 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 7, 2, rargv );
+
+       rargv[ 0 ] = "rewriteRule";
+       rargv[ 1 ] = suffix_massage_regexize( argv[ 2 ] );
+       rargv[ 2 ] = suffix_massage_patternize( argv[ 1 ], 1 );
+       rargv[ 3 ] = ":";
+       rargv[ 4 ] = NULL;
+       rewrite_parse( info, "<suffix massage>", 8, 4, rargv );
+       ch_free( rargv[ 1 ] );
+       ch_free( rargv[ 2 ] );
+#endif /* normalize matched */
+
+       return 0;
+}
+#endif /* ENABLE_REWRITE */
+
index 4b92412be12769c6c2fe1f4f399ff8705e6569aa..ef1d9475186121462c365cf100ca32dde48fcd39 100644 (file)
@@ -57,7 +57,7 @@ ldap_back_delete(
        struct ldapinfo *li = (struct ldapinfo *) be->be_private;
        struct ldapconn *lc;
 
-       char *mdn;
+       char *mdn = NULL;
 
        lc = ldap_back_getconn( li, conn, op );
        
@@ -65,14 +65,39 @@ ldap_back_delete(
                return( -1 );
        }
 
-       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
+       /*
+        * Rewrite the compare dn, if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "deleteDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
        if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> deleteDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
                return( -1 );
        }
+#else /* !ENABLE_REWRITE */
+       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
+#endif /* !ENABLE_REWRITE */
        
        ldap_delete_s( lc->ld, mdn );
 
-       free( mdn );
+#ifdef ENABLE_REWRITE
+       if ( mdn != dn ) {
+#endif /* ENABLE_REWRITE */
+               free( mdn );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
        
        return( ldap_back_op_result( lc, op ) );
 }
index 52d10b3164d4df805fd9bfd893ebaa3c768a169f..800b873e8d8c85f6f4661139a795ebd22a88e527 100644 (file)
@@ -16,9 +16,9 @@
 #include "back-ldap.h"
 
 
-/* return 0 IFF op_dn is a value in member attribute
+/* return 0 IFF op_dn is a value in group_at (member) attribute
  * of entry with gr_dn AND that entry has an objectClass
- * value of groupOfNames
+ * value of group_oc (groupOfNames)
  */
 int
 ldap_back_group(
@@ -35,12 +35,13 @@ ldap_back_group(
        struct ldapinfo *li = (struct ldapinfo *) be->be_private;    
        int rc = 1;
        Attribute   *attr;
-       Entry *e;
        struct berval bv;
+
        LDAPMessage     *result;
        char *gattr[2];
        char *filter;
        LDAP *ld;
+       char *mop_ndn, *mgr_ndn;
 
        AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass;
        char *group_oc_name = NULL;
@@ -55,65 +56,150 @@ ldap_back_group(
        if (target != NULL && strcmp(target->e_ndn, gr_ndn) == 0) {
                /* we already have a copy of the entry */
                /* attribute and objectclass mapping has already been done */
-               e = target;
 
-               if( is_entry_objectclass( e, group_oc ) ) {
-                       return(1);
+               /*
+                * first we need to check if the objectClass attribute
+                * has been retieved; otherwise we need to repeat the search
+                */
+               attr = attr_find( target->e_attrs, ad_objectClass );
+               if ( attr != NULL ) {
+
+                       /*
+                        * Now we can check for the group objectClass value
+                        */
+                       if( !is_entry_objectclass( target, group_oc ) ) {
+                               return(1);
+                       }
+
+                       /*
+                        * This part has been reworked: the group attr compare
+                        * fails only if the attribute is PRESENT but the value
+                        * is NOT PRESENT; if the attribute is NOT PRESENT, the
+                        * search must be repeated as well.
+                        * This may happen if a search for an entry has already
+                        * been performed (target is not null) but the group
+                        * attribute has not been required
+                        */
+                       if ((attr = attr_find(target->e_attrs, group_at)) != NULL) {
+                               bv.bv_val = (char *) op_ndn;
+                               bv.bv_len = strlen( op_ndn );         
+                               if( value_find( group_at, attr->a_vals, &bv ) != LDAP_SUCCESS  )
+                                       return(1);
+                               return(0);
+                       } /* else: repeat the search */
+               } /* else: repeat the search */
+       } /* else: do the search */
+
+       /*
+        * Rewrite the op ndn if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "bindDn",
+                               op_ndn, conn, &mop_ndn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mop_ndn == NULL ) {
+                       mop_ndn = ( char * )op_ndn;
                }
+               Debug( LDAP_DEBUG_ARGS,
+                       "rw> bindDn (op ndn in group): \"%s\" -> \"%s\"\n%s",
+                       op_ndn, mop_ndn, "" );
+               break;
+       
+       case REWRITE_REGEXEC_UNWILLING:
+       
+       case REWRITE_REGEXEC_ERR:
+               goto cleanup;
+       }
 
-               if ((attr = attr_find(e->e_attrs, group_at)) == NULL)
-                       return(1);
+       /*
+        * Rewrite the gr ndn if needed
+        */
+        switch ( rewrite_session( li->rwinfo, "searchBase",
+                               gr_ndn, conn, &mgr_ndn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mgr_ndn == NULL ) {
+                       mgr_ndn = ( char * )gr_ndn;
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                       "rw> searchBase (gr ndn in group):"
+                       " \"%s\" -> \"%s\"\n%s",
+                       gr_ndn, mgr_ndn, "" );
+               break;
+       
+       case REWRITE_REGEXEC_UNWILLING:
+       
+       case REWRITE_REGEXEC_ERR:
+               goto cleanup;
+       }
+#else /* !ENABLE_REWRITE */
+       mop_ndn = ldap_back_dn_massage( li, ch_strdup( op_ndn ), 1 );
+       if ( mop_ndn == NULL ) {
+               goto cleanup;
+       }
+       mgr_ndn = ldap_back_dn_massage( li, ch_strdup( gr_ndn ), 1 );
+       if ( mgr_ndn == NULL ) {
+               goto cleanup;
+       }
+#endif /* !ENABLE_REWRITE */
+
+       group_oc_name = ldap_back_map(&li->oc_map, group_oc_name, 0);
+       if (group_oc_name == NULL)
+               goto cleanup;
+       group_at_name = ldap_back_map(&li->at_map, group_at_name, 0);
+       if (group_at_name == NULL)
+               goto cleanup;
+
+       filter = ch_malloc(sizeof("(&(objectclass=)(=))")
+                                               + strlen(group_oc_name)
+                                               + strlen(group_at_name)
+                                               + strlen(mop_ndn) + 1);
+       if (filter == NULL)
+               goto cleanup;
+
+       if (ldap_initialize(&ld, li->url) != LDAP_SUCCESS) {
+               goto cleanup;
+       }
 
-               bv.bv_val = (char *) op_ndn;
-               bv.bv_len = strlen( op_ndn );         
-               if( value_find( group_at, attr->a_vals, &bv ) == 0  )
-                       return(1);
+       if (ldap_bind_s(ld, li->binddn, li->bindpw, LDAP_AUTH_SIMPLE)
+                       != LDAP_SUCCESS) {
+               goto cleanup;
+       }
 
-       } else {
-               group_oc_name = ldap_back_map(&li->oc_map, group_oc_name, 0);
-               if (group_oc_name == NULL)
-                       return(1);
-               group_at_name = ldap_back_map(&li->at_map, group_at_name, 0);
-               if (group_at_name == NULL)
-                       return(1);
-
-               filter = ch_malloc(sizeof("(&(objectclass=)(=))")
-                                                       + strlen(group_oc_name)
-                                                       + strlen(group_at_name)
-                                                       + strlen(op_ndn) + 1);
-               if (filter == NULL)
-                       return(1);
-
-               if (ldap_initialize(&ld, li->url) != LDAP_SUCCESS) {
-                       ch_free(filter);
-                       return(1);
-               }
+       strcpy(filter, "(&(objectclass=");
+       strcat(filter, group_oc_name);
+       strcat(filter, ")(");
+       strcat(filter, group_at_name);
+       strcat(filter, "=");
+       strcat(filter, mop_ndn);
+       strcat(filter, "))");
+
+       gattr[0] = "objectclass";
+       gattr[1] = NULL;
+       if (ldap_search_ext_s(ld, mgr_ndn, LDAP_SCOPE_BASE, filter,
+               gattr, 0, NULL, NULL, LDAP_NO_LIMIT,
+               LDAP_NO_LIMIT, &result) == LDAP_SUCCESS) {
+               if (ldap_first_entry(ld, result) != NULL)
+                       rc = 0;
+               ldap_msgfree(result);
+       }
 
-               if (ldap_bind_s(ld, li->binddn, li->bindpw, LDAP_AUTH_SIMPLE) == LDAP_SUCCESS) {
-                       strcpy(filter, "(&(objectclass=");
-                       strcat(filter, group_oc_name);
-                       strcat(filter, ")(");
-                       strcat(filter, group_at_name);
-                       strcat(filter, "=");
-                       strcat(filter, op_ndn);
-                       strcat(filter, "))");
-
-                       gattr[0] = "objectclass";
-                       gattr[1] = NULL;
-                       if (ldap_search_ext_s(ld, gr_ndn, LDAP_SCOPE_BASE, filter,
-                                                                       gattr, 0, NULL, NULL, LDAP_NO_LIMIT,
-                                                                       LDAP_NO_LIMIT, &result) == LDAP_SUCCESS)
-                       {
-                               if (ldap_first_entry(ld, result) != NULL)
-                                       rc = 0;
-                               ldap_msgfree(result);
-                       }
-               }
+cleanup:
+       if ( ld != NULL ) {
                ldap_unbind(ld);
-               ch_free(filter);
-               return(rc);
-    }
-
-       return(0);
+       }
+       ch_free(filter);
+#ifdef ENABLE_REWRITE
+       if ( mop_ndn != op_ndn ) {
+#endif /* ENABLE_REWRITE */
+               free( mop_ndn );
+#ifdef ENABLE_REWRITE
+       }
+       if ( mgr_ndn != gr_ndn ) {
+#endif /* ENABLE_REWRITE */
+               free( mgr_ndn );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
+       return(rc);
 }
 
index 96e1acbd79402e1add26e7a0d6495d8f2b6e0b2b..4b4c6975d6a823f422b8478d8ef0c659d703a432 100644 (file)
@@ -106,6 +106,18 @@ ldap_back_db_init(
        struct ldapmapping *mapping;
 
        li = (struct ldapinfo *) ch_calloc( 1, sizeof(struct ldapinfo) );
+       if ( li == NULL ) {
+               return -1;
+       }
+
+#ifdef ENABLE_REWRITE
+       li->rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+       if ( li->rwinfo == NULL ) {
+               ch_free( li );
+               return -1;
+       }
+#endif /* ENABLE_REWRITE */
+
        ldap_pvt_thread_mutex_init( &li->conn_mutex );
 
        mapping = (struct ldapmapping *)ch_calloc( 2, sizeof(struct ldapmapping) );
@@ -123,7 +135,7 @@ ldap_back_db_init(
 
        be->be_private = li;
 
-       return li == NULL;
+       return 0;
 }
 
 static void
@@ -136,7 +148,7 @@ conn_free(
        free( lc );
 }
 
-static void
+void
 mapping_free ( struct ldapmapping *mapping )
 {
        ch_free( mapping->src );
@@ -168,13 +180,18 @@ ldap_back_db_destroy(
                        free(li->bindpw);
                        li->bindpw = NULL;
                }
-               if (li->suffix_massage) {
-                       ldap_value_free( li->suffix_massage );
-                       li->suffix_massage = NULL;
-               }
                 if (li->conntree) {
                        avl_free( li->conntree, (AVL_FREE) conn_free );
                }
+#ifdef ENABLE_REWRITE
+               if (li->rwinfo) {
+                       rewrite_info_delete( li->rwinfo );
+               }
+#else /* !ENABLE_REWRITE */
+               if (li->suffix_massage) {
+                       ldap_value_free( li->suffix_massage );
+               }
+#endif /* !ENABLE_REWRITE */
 
                avl_free( li->oc_map.remap, NULL );
                avl_free( li->oc_map.map, (AVL_FREE) mapping_free );
index fb7bfc669934a136b826f78b218698eadb5c7856..269ebf459114f06ad59dfddc9f68ffc66db74b7f 100644 (file)
@@ -61,45 +61,89 @@ ldap_back_modify(
        LDAPMod *mods;
        Modifications *ml;
        int i;
-       char *mdn, *mapped;
+       char *mdn = NULL, *mapped;
 
        lc = ldap_back_getconn(li, conn, op);
        if ( !lc || !ldap_back_dobind( lc, op ) ) {
                return( -1 );
        }
 
-       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
-       if ( mdn == NULL ) {
+       /*
+        * Rewrite the modify dn, if needed
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "modifyDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> modifyDN: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
                return( -1 );
        }
+#else /* !ENABLE_REWRITE */
+       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
+#endif /* !ENABLE_REWRITE */
 
        for (i=0, ml=modlist; ml; i++,ml=ml->sml_next)
                ;
 
        mods = (LDAPMod *)ch_malloc(i*sizeof(LDAPMod));
-       if (mods == NULL)
-               return( -1 );
+       if (mods == NULL) {
+               goto cleanup;
+       }
        modv = (LDAPMod **)ch_malloc((i+1)*sizeof(LDAPMod *));
        if (modv == NULL) {
-               free(mods);
-               return( -1 );
+               goto cleanup;
        }
 
        for (i=0, ml=modlist; ml; ml=ml->sml_next) {
                mapped = ldap_back_map(&li->at_map, ml->sml_desc->ad_cname->bv_val, 0);
-               if (mapped != NULL) {
-                       modv[i] = &mods[i];
-                       mods[i].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
-                       mods[i].mod_type = mapped;
-                       mods[i].mod_bvalues = ml->sml_bvalues;
-                       i++;
+               if (mapped == NULL) {
+                       continue;
                }
+
+               modv[i] = &mods[i];
+               mods[i].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
+               mods[i].mod_type = mapped;
+
+#ifdef ENABLE_REWRITE
+               /*
+                * FIXME: dn-valued attrs should be rewritten
+                * to allow their use in ACLs at the back-ldap
+                * level.
+                */
+               if ( strcmp( ml->sml_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       ldap_dnattr_rewrite( li->rwinfo,
+                                       ml->sml_bvalues, conn );
+               }
+#endif /* ENABLE_REWRITE */
+       
+               mods[i].mod_bvalues = ml->sml_bvalues;
+               i++;
        }
        modv[i] = 0;
 
        ldap_modify_s( lc->ld, mdn, modv );
-       free( mdn );
+
+cleanup:
+#ifdef ENABLE_REWRITE
+       if ( mdn != dn ) {
+#endif /* ENABLE_REWRITE */
+               free( mdn );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
        free(mods);
        free(modv);
        return( ldap_back_op_result( lc, op ));
 }
+
index 66761c0a739de37d0b3b3ec03a9863cd63f4fe8f..74c2922ce82605a4020c0b2b563358270f5a4cee 100644 (file)
@@ -60,10 +60,10 @@ ldap_back_modrdn(
        struct ldapinfo *li = (struct ldapinfo *) be->be_private;
        struct ldapconn *lc;
 
-       char *mdn, *mnewSuperior;
+       char *mdn = NULL, *mnewSuperior = NULL;
 
        lc = ldap_back_getconn( li, conn, op );
-       if ( !lc ) {
+       if ( !lc || !ldap_back_dobind(lc, op) ) {
                return( -1 );
        }
 
@@ -71,26 +71,78 @@ ldap_back_modrdn(
                int version = LDAP_VERSION3;
                ldap_set_option( lc->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
                
+               /*
+                * Rewrite the new superior, if defined and required
+                */
+#ifdef ENABLE_REWRITE
+               switch ( rewrite_session( li->rwinfo, "newSuperiorDn",
+                                       newSuperior, conn, &mnewSuperior ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mnewSuperior == NULL ) {
+                               mnewSuperior = ( char * )newSuperior;
+                       }
+                       Debug( LDAP_DEBUG_ARGS, "rw> newSuperiorDn:"
+                                       " \"%s\" -> \"%s\"\n%s",
+                                       newSuperior, mnewSuperior, "" );
+                       break;
+
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+
+               case REWRITE_REGEXEC_ERR:
+                       return( -1 );
+               }
+#else /* !ENABLE_REWRITE */
                mnewSuperior = ldap_back_dn_massage( li,
-                       ch_strdup( newSuperior ), 0 );
+                               ch_strdup( newSuperior ), 0 );
                if ( mnewSuperior == NULL ) {
                        return( -1 );
                }
+#endif /* !ENABLE_REWRITE */
        }
 
-       if ( !ldap_back_dobind(lc, op) ) {
-               return( -1 );
-       }
+#ifdef ENABLE_REWRITE
+       /*
+        * Rewrite the modrdn dn, if required
+        */
+       switch ( rewrite_session( li->rwinfo, "modrDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> modrDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
 
-       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
-       if ( mdn == NULL ) {
+       case REWRITE_REGEXEC_ERR:
                return( -1 );
        }
+#else /* !ENABLE_REWRITE */
+       mdn = ldap_back_dn_massage( li, ch_strdup( dn ), 0 );
+#endif /* !ENABLE_REWRITE */
 
        ldap_rename2_s( lc->ld, mdn, newrdn, mnewSuperior, deleteoldrdn );
 
+#ifdef ENABLE_REWRITE
+       if ( mdn != dn ) {
+#endif /* ENABLE_REWRITE */
        free( mdn );
-       if ( mnewSuperior ) free( mnewSuperior );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
+       if ( mnewSuperior != NULL
+#ifdef ENABLE_REWRITE
+                       && mnewSuperior != newSuperior
+#endif /* ENABLE_REWRITE */
+          ) {
+               free( mnewSuperior );
+       }
        
        return( ldap_back_op_result( lc, op ) );
 }
index b65bb3e8d95fbdf3930aa74e645999969acc0f0e..1b0e3a20ee28a785f69dd546ce1b850fbcf560cb 100644 (file)
@@ -70,9 +70,12 @@ ldap_back_search(
        struct ldapconn *lc;
        struct timeval  tv;
        LDAPMessage             *res, *e;
-       int     count, rc, msgid, sres = LDAP_SUCCESS; 
+       int     count, rc = 0, msgid, sres = LDAP_SUCCESS; 
        char *match = NULL, *err = NULL;
-       char *mbase, *mapped_filter, **mapped_attrs;
+       char *mbase = NULL, *mapped_filter = NULL, **mapped_attrs = NULL;
+#ifdef ENABLE_REWRITE
+       char *mfilter = NULL, *mmatch = NULL;
+#endif /* ENABLE_REWRITE */
 
        lc = ldap_back_getconn(li, conn, op);
        if ( !lc ) {
@@ -90,17 +93,73 @@ ldap_back_search(
                return( -1 );
        }
 
-       mbase = ldap_back_dn_massage( li, ch_strdup( base ), 0 );
-       if ( mbase == NULL ) {
-               return -1;
+       /*
+        * Rewrite the search base, if required
+        */
+#ifdef ENABLE_REWRITE
+       switch ( rewrite_session( li->rwinfo, "searchBase",
+                               base, conn, &mbase ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mbase == NULL ) {
+                       mbase = ( char * )base;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> searchBase: \"%s\" -> \"%s\"\n%s",
+                               base, mbase, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
+               rc = -1;
+               goto finish;
        }
+       
+       /*
+        * Rewrite the search filter, if required
+        */
+       switch ( rewrite_session( li->rwinfo, "searchFilter",
+                               filterstr, conn, &mfilter ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mfilter == NULL || mfilter[0] == '\0') {
+                       if ( mfilter != NULL ) {
+                               free( mfilter );
+                       }
+                       mfilter = ( char * )filterstr;
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                               "rw> searchFilter: \"%s\" -> \"%s\"\n%s",
+                               filterstr, mfilter, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+       case REWRITE_REGEXEC_ERR:
+               rc = -1;
+               goto finish;
+       }
+#else /* !ENABLE_REWRITE */
+       mbase = ldap_back_dn_massage( li, ch_strdup( base ), 0 );
+#endif /* !ENABLE_REWRITE */
 
-       mapped_filter = ldap_back_map_filter(li, (char *)filterstr, 0);
+       mapped_filter = ldap_back_map_filter(&li->at_map, &li->oc_map,
+#ifdef ENABLE_REWRITE
+                       (char *)mfilter,
+#else /* !ENABLE_REWRITE */
+                       (char *)filterstr,
+#endif /* !ENABLE_REWRITE */
+                       0);
        if ( mapped_filter == NULL ) {
+#ifdef ENABLE_REWRITE
+               mapped_filter = (char *)mfilter;
+#else /* !ENABLE_REWRITE */
                mapped_filter = (char *)filterstr;
+#endif /* !ENABLE_REWRITE */
        }
 
-       mapped_attrs = ldap_back_map_attrs(li, attrs, 0);
+       mapped_attrs = ldap_back_map_attrs(&li->at_map, attrs, 0);
        if ( mapped_attrs == NULL ) {
                mapped_attrs = attrs;
        }
@@ -109,16 +168,8 @@ ldap_back_search(
                attrsonly)) == -1)
        {
 fail:
-               if (match)
-                       free(match);
-               if (err)
-                       free(err);
-               if (mapped_attrs != attrs)
-                       charray_free(mapped_attrs);
-               if (mapped_filter != filterstr)
-                       free(mapped_filter);
-               free(mbase);
-               return( ldap_back_op_result(lc, op) );
+               rc = ldap_back_op_result(lc, op);
+               goto finish;
        }
 
        /* We pull apart the ber result, stuff it into a slapd entry, and
@@ -139,6 +190,7 @@ fail:
 
                if (ab) {
                        ldap_abandon(lc->ld, msgid);
+                       rc = 0;
                        goto finish;
                }
                if (rc == 0) {
@@ -163,20 +215,78 @@ fail:
        if (rc == -1)
                goto fail;
 
+#ifdef ENABLE_REWRITE
+       /*
+        * Rewrite the matched portion of the search base, if required
+        */
+       if ( match != NULL ) {
+               switch ( rewrite_session( li->rwinfo, "matchedDn",
+                               match, conn, &mmatch ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mmatch == NULL ) {
+                               mmatch = ( char * )match;
+                       }
+                       Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:"
+                                      " \"%s\" -> \"%s\"\n%s",
+                                      match, mmatch, "" );
+                       break;
+                       
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       
+               case REWRITE_REGEXEC_ERR:
+                       rc = -1;
+                       goto finish;
+               }
+       }
+
+       send_search_result( conn, op, sres,
+               mmatch, err, NULL, NULL, count );
+
+#else /* !ENABLE_REWRITE */
        send_search_result( conn, op, sres,
                match, err, NULL, NULL, count );
+#endif /* !ENABLE_REWRITE */
 
 finish:
-       if (match)
+       if ( match ) {
+#ifdef ENABLE_REWRITE
+               if ( mmatch != match ) {
+                       free( mmatch );
+               }
+#endif /* ENABLE_REWRITE */
                free(match);
-       if (err)
-               free(err);
-       if (mapped_attrs != attrs)
-               charray_free(mapped_attrs);
-       if (mapped_filter != filterstr)
-               free(mapped_filter);
-       free(mbase);
-       return( 0 );
+       }
+       if ( err ) {
+               free( err );
+       }
+       if ( mapped_attrs != attrs ) {
+               charray_free( mapped_attrs );
+       }
+#ifdef ENABLE_REWRITE
+       if ( mapped_filter != mfilter ) {
+               free( mapped_filter );
+       }
+       if ( mfilter != filterstr ) {
+               free( mfilter );
+       }
+#else /* !ENABLE_REWRITE */
+       if ( mapped_filter != filterstr ) {
+               free( mapped_filter );
+       }
+#endif /* !ENABLE_REWRITE */
+       
+#ifdef ENABLE_REWRITE
+       if ( mbase != base ) {
+#endif /* ENABLE_REWRITE */
+               free( mbase );
+#ifdef ENABLE_REWRITE
+       }
+#endif /* ENABLE_REWRITE */
+       
+       return rc;
 }
 
 static void
@@ -198,7 +308,39 @@ ldap_send_entry(
        struct berval *bv;
        const char *text;
 
+#ifdef ENABLE_REWRITE
+       char *dn;
+
+       dn = ldap_get_dn(lc->ld, e);
+       if ( dn == NULL ) {
+               return;
+       }
+
+       /*
+        * Rewrite the dn of the result, if needed
+        */
+       switch ( rewrite_session( li->rwinfo, "searchResult",
+                               dn, lc->conn, &ent.e_dn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( ent.e_dn == NULL ) {
+                       ent.e_dn = dn;
+               } else {
+                       Debug( LDAP_DEBUG_ARGS, "rw> searchResult: \"%s\""
+                                       " -> \"%s\"\n%s", dn, ent.e_dn, "" );
+                       free( dn );
+                       dn = NULL;
+               }
+               break;
+               
+       case REWRITE_REGEXEC_ERR:
+       case REWRITE_REGEXEC_UNWILLING:
+               free( dn );
+               return;
+       }
+#else /* !ENABLE_REWRITE */
        ent.e_dn = ldap_back_dn_restore( li, ldap_get_dn(lc->ld, e), 0 );
+#endif /* !ENABLE_REWRITE */
+
        ent.e_ndn = ch_strdup( ent.e_dn );
        (void) dn_normalize( ent.e_ndn );
        ent.e_id = 0;
@@ -228,7 +370,7 @@ ldap_send_entry(
                } else if ( strcasecmp( mapped, "objectclass" ) == 0 ) {
                        int i, last;
                        for ( last = 0; attr->a_vals[last]; last++ ) ;
-                       for ( i = 0; bv = attr->a_vals[i]; i++ ) {
+                       for ( i = 0; ( bv = attr->a_vals[i] ); i++ ) {
                                mapped = ldap_back_map(&li->oc_map, bv->bv_val, 1);
                                if (mapped == NULL) {
                                        ber_bvfree(attr->a_vals[i]);
@@ -244,7 +386,59 @@ ldap_send_entry(
                                        bv->bv_len = strlen( mapped );
                                }
                        }
+
+#ifdef ENABLE_REWRITE
+               /*
+                * It is necessary to try to rewrite attributes with
+                * dn syntax because they might be used in ACLs as
+                * members of groups; since ACLs are applied to the
+                * rewritten stuff, no dn-based subecj clause could
+                * be used at the ldap backend side (see
+                * http://www.OpenLDAP.org/faq/data/cache/452.html)
+                * The problem can be overcome by moving the dn-based
+                * ACLs to the target directory server, and letting
+                * everything pass thru the ldap backend.
+                */
+               } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       int i;
+                       for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) {
+                               char *newval;
+                               
+                               switch ( rewrite_session( li->rwinfo,
+                                                       "searchResult",
+                                                       bv->bv_val,
+                                                       lc->conn, &newval )) {
+                               case REWRITE_REGEXEC_OK:
+                                       /* left as is */
+                                       if ( newval == NULL ) {
+                                               break;
+                                       }
+                                       Debug( LDAP_DEBUG_ARGS,
+               "rw> searchResult on attr=%s: \"%s\" -> \"%s\"\n",
+                                               attr->a_desc->ad_type->sat_cname,
+                                               bv->bv_val, newval );
+                                       
+                                       free( bv->bv_val );
+                                       bv->bv_val = newval;
+                                       bv->bv_len = strlen( newval );
+                                       
+                                       break;
+                                       
+                               case REWRITE_REGEXEC_UNWILLING:
+                                       
+                               case REWRITE_REGEXEC_ERR:
+                                       /*
+                                        * FIXME: better give up,
+                                        * skip the attribute
+                                        * or leave it untouched?
+                                        */
+                                       break;
+                               }
+                       }
+#endif /* ENABLE_REWRITE */
                }
+
                *attrp = attr;
                attrp = &attr->a_next;
        }
@@ -265,3 +459,4 @@ ldap_send_entry(
        if ( ent.e_ndn )
                free( ent.e_ndn );
 }
+
index aa49ef21c43bb8cf46b71d2b752196167558df35..de771055e485843e636194286896dd775e272e52 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "portable.h"
 
+#ifndef ENABLE_REWRITE
+
 #include <stdio.h>
 
 #include <ac/socket.h>
@@ -154,4 +156,5 @@ ldap_back_dn_restore(
 
         return dn;
 }
+#endif /* !ENABLE_REWRITE */
 
index 574ef6ba60e44d64c5784acd60e2a4cd4b383e5a..49b2f3ef19ceac2446fca41b3cef951e99156dba 100644 (file)
@@ -62,14 +62,21 @@ ldap_back_conn_destroy(
        lc_curr.conn = conn;
        
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
-       lc = avl_delete( &li->conntree, (caddr_t)&lc_curr, conn_cmp );
+       lc = avl_delete( &li->conntree, (caddr_t)&lc_curr, ldap_back_conn_cmp );
        ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
 
        if (lc) {
                Debug( LDAP_DEBUG_TRACE,
                        "=>ldap_back_conn_destroy: destroying conn %d\n",
                        lc->conn->c_connid, 0, 0 );
-               
+
+#ifdef ENABLE_REWRITE
+               /*
+                * Cleanup rewrite session
+                */
+               rewrite_session_delete( li->rwinfo, conn );
+#endif /* ENABLE_REWRITE */
+
                /*
                 * Needs a test because the handler may be corrupted,
                 * and calling ldap_unbind on a corrupted header results
diff --git a/servers/slapd/back-meta/Changes b/servers/slapd/back-meta/Changes
new file mode 100644 (file)
index 0000000..a032bd4
--- /dev/null
@@ -0,0 +1,68 @@
+Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+
+* Mon Apr 30 2001 Pierangelo Masarati <ando@sys-net.it>
+- split back-meta from back-ldap
+- inplement init, config, bind, search, unbind
+
+* Tue May 01 2001 Pierangelo Masarati <ando@sys-net.it>
+- refined search propagation based on scope; in case of base shorter
+       than candidate target suffix:
+               "sub" propagates to all candidate targets,
+               "one" propagates to candidate targets only if base is
+               no more than one level shorter than suffix; in such
+               case scope is changed into "base"
+               "base" does not propagate (no such object error)
+
+* Tue May 01 2001 Pierangelo Masarati <ando@sys-net.it>
+- added checks in config.c
+- fixed leaks in search.c
+
+* Fri May 04 2001 Pierangelo Masarati <ando@sys-net.it>
+- added dn cache
+- added test
+
+* Sat May 05 2001 Pierangelo Masarati <ando@sys-net.it>
+- added missing functions
+- fixed flaw in group/attribute funcs (affects also back-ldap)
+  now meta_back_group works fine (group ACLS); nothing to say
+  about meta_back_attribute: unable to trigger it!
+- attributes with dn syntax are rewritten back in search results
+  (otherwise "dnattr"-style ACLs don't work). Now attrs with
+  dn syntax need be rewritten forth in add/modify ...
+
+* Sun May 06 2001 Pierangelo Masarati <ando@sys-net.it>
+- attributes with dn syntax are rewritten during add/modify operations
+  (otherwise "dnattr"-style ACLs don't work).
+- same back/forth rewriting has been applied to back-ldap;
+  actually, the add/modify rewriting function has been implemented
+  in bacl-ldap (ldap_dnattr_rewrite) and recycled in back-meta
+- fixed bug in invocation of meta_back_dobind in delete.c
+- code for deletion of "lastmod" attrs has been commented;
+  explicit "lastmod off" has been recommended in FAQ.
+- fixed a missing return in suffix_massage_config
+
+* Sun May 06 2001 Pierangelo Masarati <ando@sys-net.it>
+- fixed flaw in group.c both in back-ldap and back-meta: the group ndn
+  was not rewritten.
+
+* Tue May 08 2001 Pierangelo Masarati <ando@sys-net.it>
+- removed the limitation on target naming contexts; now they can be the same
+- added support for default target; it may be disabled or set to one of
+  the targets, resulting in that target being selected any time there's
+  a non-unique target selection (the target is missing or multiple targets
+  are selected for a add/modify/delete/compare/... operation).
+  TODO: should be more flexible, e.g. let the administrator decide
+  whether the default target may be used or not.
+- fixed a subtle bind/cache bug.
+- added helpers to clear target connections when they're no longer candidate.
+
+* Wed May 09 2001 Pierangelo Masarati <ando@sys-net.it>
+- reworked connection stuff in a separate file
+- reworked meta_back_getconn to handle singe/multiple target selection
+  correctly and atomicly
+- checked attribute mapping stuff
+
+* Fri May 11 2001 Pierangelo Masarati <ando@sys-net.it>
+- reworked compare to spawn the request on candidate targets (need to check
+  at most one matches!)
+
diff --git a/servers/slapd/back-meta/Copyright b/servers/slapd/back-meta/Copyright
new file mode 100644 (file)
index 0000000..0e0faa4
--- /dev/null
@@ -0,0 +1,63 @@
+Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+
+This work has been developed to fulfill the requirements
+of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+to the OpenLDAP Foundation in the hope that it may be useful
+to the Open Source community, but WITHOUT ANY WARRANTY.
+
+Permission is granted to anyone to use this software for any purpose
+on any computer system, and to alter it and redistribute it, subject
+to the following restrictions:
+
+1. The author and SysNet s.n.c. are not responsible for the consequences
+   of use of this software, no matter how awful, even if they arise from
+   flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+   explicit claim or by omission.  Since few users ever read sources,
+   credits should appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.  Since few users
+   ever read sources, credits should appear in the documentation.
+   SysNet s.n.c. cannot be responsible for the consequences of the
+   alterations.
+
+4. This notice may not be removed or altered.
+
+
+This software is based on the backend back-ldap, implemented
+by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+<kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+contributors. The contribution of the original software to the present
+implementation is acknowledged in this copyright statement.
+
+A special acknowledgement goes to Howard for the overall architecture
+(and for borrowing large pieces of code), and to Mark, who implemented
+from scratch the attribute/objectclass mapping.
+
+The original copyright statement follows.
+
+Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+
+Permission is granted to anyone to use this software for any purpose
+on any computer system, and to alter it and redistribute it, subject
+to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+   software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+   explicit claim or by omission.  Since few users ever read sources,
+   credits should appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.  Since few users
+   ever read sources, credits should appear in the documentation.
+
+4. This notice may not be removed or altered.
+
+
diff --git a/servers/slapd/back-meta/Documentation b/servers/slapd/back-meta/Documentation
new file mode 100644 (file)
index 0000000..a8e570a
--- /dev/null
@@ -0,0 +1,285 @@
+Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+
+
+
+   Metadirectory backend.
+
+This is a brief introduction to the core functionalities of back-meta.
+
+
+
+ - Introduction.
+
+Back-meta implements a backend for OpenLDAP's slapd.  It performs basic
+LDAP proxying with respect to a set of remote LDAP servers, called
+"targets".  The information contained in these servers can be presented
+as belonging to a single Directory Information Tree (DIT).
+
+A basic knowledge of the functionality of back-ldap is recommended.
+This backend has been designed as an enhancement of back-ldap.
+The two backends share many features (actually they also share portions
+of code).  While back-ldap is intended to proxy operations directed
+to a single server, back-meta is mainly intended for proxying
+of multiple servers and possibly naming context masquerading.
+These features, although useful in many scenarios, may result in
+excessive overhead for some applications, so its use should be
+carefully considered.
+
+In the examples section, some typical scenarios will be discussed.
+
+
+
+ - Common configuration directives
+
+The backend uses most of the common configuration directives.  Its
+configuration block starts with the "database" directive:
+
+       database        meta
+
+At least a "suffix" directive is required.
+
+Note: as with back-ldap, operational attributes related to entry
+creation/modification should not be used, as they would be passed
+to the target servers, generating an error.  Moreover, it makes
+little sense to use such attributes in proxying, as the proxy
+server doesn't actually store data, so it should have no knowledge
+of such attributes.  While code to strip the modification attributes
+has been put in place (and #ifdef's), it implies unmotivated overhead.
+So it is strongly recommended to set
+
+       lastmod         off
+
+for every back-ldap/back-meta backend.
+
+
+
+ - Special configuration directives
+
+Target configuration starts with the "uri" directive.  All the 
+configuration directives that are not specific to targets should
+be defined first for clarity, including those that are common to
+all backends.  They are:
+
+       default-target  none
+
+This directive forces the backend to reject all those operations
+that must resolve to a single target in case none or multiple
+targets are selected.  They include: add, delete, modify, modrdn;
+compare is also included, although a different behavior might be
+considered in the future.  This directive can also be used when
+processing targets to mark a specific target as default.
+
+       dncache-ttl     {forever|disabled|<ttl>}
+
+This directive sets the time-to-live of the dn cache.  This caches
+the target that holds a given dn to speed up target selection
+in case multiple targets would result from an uncached search;
+forever means cache never expires; disabled means no dn caching;
+otherwise a valid ( > 0 ) ttl in seconds is required.
+
+
+
+ - Target specification
+
+Target specification starts with a "uri" directive:
+
+       uri             <protocol>://[<host>[:<port>]]/<naming context>
+
+The "server" directive that was allowed in back-ldap has been discarded
+in back-meta.  The <protocol> part can be anything ldap_initialize(3)
+accepts ({ldap|ldaps|ldapi} and variants); <host> and <port> may be
+omitted, defaulting to whatever is set in /etc/ldap.conf (correct me!?!).
+The <naming context> part is mandatory.  It must end with one of the
+naming contexts defined for the backend, e.g.:
+
+               suffix          "dc=foo,dc=com"
+               uri             "ldap://x.foo.com/dc=x,dc=foo,dc=com"
+
+The <naming context> part doesn't need to be unique across the targets;
+it may also match one of the values of the "suffix" directive.
+
+       default-target  [<target>]
+
+the "default-target" directive can also be used during target 
+specification.  With no arguments it marks the current target as
+the default.  The optional number marks target <target> as the
+default one, starting from 1.  Target <target> must be defined.
+
+       binddn          <administrative dn for ac purposes>
+
+This directive, as in back-ldap, allows to define the dn that is
+used to query the target server for acl checking; it should have
+read access on the target server to attributes used on the proxy
+for acl checking.  There is no risk of giving away such values;
+they are only used to check permissions.
+
+       bindpw          <plaintext password for ac purposes>
+
+This directive sets the password for acl checking in conjunction
+with the above mentioned "binddn" directive.
+
+       rewrite*        ...
+
+       suffixmassage   <virtual naming context> <real naming context>
+
+All the directives starting with "rewrite" refer to the rewrite engine
+that has been added to slapd.  The "suffixmassage" directive was
+introduced in back-ldap to allow suffix massaging while proxying.
+It has been obsoleted by the rewriting tools.  However, both for
+backward compatibility and for ease of configuration when simple
+suffix massage is required, it has been preserved.  It wraps the
+basic rewriting instruction that perform suffix massaging.
+
+Note: this also fixes a flaw in suffix massaging, which operated
+on (case insensitive) DNs instead of normalized DNs,
+so "dc=foo, dc=com" would not match "dc=foo,dc=com".
+
+See the "rewrite" section.
+
+       map             {objectClass|attribute} {<source>|*} [<dest>|*]
+
+objectClass/attribute mapping stuff.  This has been inherited from
+the work made by Mark Valence on the back-ldap backend.
+See the mapping section.
+
+
+
+ - Scenarios
+
+A powerful (and in some sense dangerous) rewrite engine has been added
+to both back-ldap and back-meta.  While the former can gain limited
+beneficial effects from rewriting stuff, the latter can become
+an amazingly powerful tool.
+
+Consider a couple of scenarios first.
+
+1) Two directory servers share two levels of naming context;
+say "dc=a,dc=foo,dc=com" and "dc=b,dc=foo,dc=com".  Then, an
+unambiguous back-meta can be configured as:
+
+       database        meta
+       suffix          "dc=foo,dc=com"
+
+       uri             "ldap://a.foo.com/dc=a,dc=foo,dc=com"
+
+       uri             "ldap://b.foo.com/dc=b,dc=foo,dc=com"
+
+Operations directed to a specific target can be easily resolved
+because there are no ambiguities.  The only operation that may
+resolve to multiple targets is a search with base "dc=foo,dc=com" 
+and scope at least "one", which results in spawning two searches
+to the targets.
+
+2a) Two directory servers don't share any portion of naming context,
+but they'd present as a single DIT.  [Caveat: uniqueness of
+(massaged) entries among the two servers is assumed; integrity 
+checks risk to incurr in excessive overhead.]
+Say we have "dc=bar,dc=org" and "o=Foo,c=US", and we'd like them to
+present as branches of "dc=foo,dc=com", say "dc=a,dc=foo,dc=com"
+and "dc=b,dc=foo,dc=com".  Then we need to configure our back-meta as:
+
+       database        meta
+       suffix          "dc=foo,dc=com"
+
+       uri             "ldap://a.bar.com/dc=a,dc=foo,dc=com"
+       suffixmassage   "dc=a,dc=foo,dc=com" "dc=bar,dc=org"
+       
+       uri             "ldap://b.foo.com/dc=b,dc=foo,dc=com"
+       suffixmassage   "dc=b,dc=foo,dc=com" "o=Foo,c=US"
+
+Again, operations can be resolved without ambiguity, although
+some rewriting is required.  Notice that the virtual naming context
+of each target is a branch of the database's naming context; it
+is rewritten back and forth when operations are performed towards
+the target servers.  What "back and forth" means will be clarified
+later.
+
+When a search with base "dc=foo,dc=com" is attempted, if the 
+scope is "base" it fails with "no such object"; in fact, the
+common root of the two targets (prior to massaging) does not
+exists.  If the scope is "one", both targets are contacted with
+the base replaced by each target's base; the scope is decreased
+to "base".  In general, the scope "one" search is honored,
+and the scope is decreased, only when the incoming base is
+at most one level lower of a target's naming context (prior
+to massaging).
+Finally, if the scope is "sub" the incoming base is replaced
+by each target's unmassaged naming context, and the scope
+is not altered.
+
+2b) Consider the above reported scenario with the two servers
+sharing the same naming context:
+
+       database        meta
+       suffix          "dc=foo,dc=com"
+
+       uri             "ldap://a.bar.com/dc=foo,dc=com"
+       suffixmassage   "dc=foo,dc=com" "dc=bar,dc=org"
+       
+       uri             "ldap://b.foo.com/dc=foo,dc=com"
+       suffixmassage   "dc=foo,dc=com" "o=Foo,c=US"
+       
+All the previous considerations hold, except that now there is
+no way to unambiguously resolve a dn.  In this case, all the
+operations that require an unambiguous target selection will
+fail unless the dn is already cached or a default target has
+been set.
+
+
+
+ - Rewriting
+
+This part of the document is being prepared.  At present you may consult
+the RATIONALE in libraries/librewrite.
+
+
+
+ - Objectclass/attribute mapping
+
+This part of the document is being prepared.  At present you may stick with 
+
+http://www.openldap.org/lists/openldap-devel/200102/msg00006.html
+
+
+
+ - ACL
+Note on ACLs: at present you may add whatever ACL rule you desire
+to back-meta (as well as to back-ldap).  However, the meaning of an ACL
+on a proxy may require some considerations.  Two philosophies may be
+considered:
+
+a) the remote server dictates the permissions; the proxy simply passes
+back what it gets from the remore server.
+
+b) the remote server unveils "everything"; the proxy is responsible
+for protecting data from unauthorized access.
+
+Of course the latter sounds unreasonable, but it is not.  It is possible
+to imagine scenarios in which a remote host discloses data that can
+be considered "public" in an intranet, and a proxy that connects it to
+the internet may impose additional constraints.  To this purpose, the
+proxy should be able to comply with all the ACL matching criteria that
+the server supports.  This has been achieved with regard to all the
+criteria supported by slapd except a secial subtle case (please notify
+me if you can find other exceptions).
+The rule
+
+access to dn="<dn>" attr=<attr>
+       by dnattr=<dnattr> read
+       by * none
+
+cannot be matched IFF:
+- the operation dn (the one that bound) is "<dn>", and
+- the entry whose <attr> is being accessed is again <dn>, and
+- the attribute that determines membership, <dnattr>, has not
+  been required (e.g. in a search)
+
+In fact this ACL is resolved by slapd considering the entry it retrieved
+from the remote server without requiring any further intervention of the
+backend, so, if the <dnattr> attribute has not been fetched, the match
+cannot be accomplished because the attribute is not present, not because
+no value matches the requirement.
+
diff --git a/servers/slapd/back-meta/Makefile.in b/servers/slapd/back-meta/Makefile.in
new file mode 100644 (file)
index 0000000..2b796c3
--- /dev/null
@@ -0,0 +1,26 @@
+# $OpenLDAP$
+
+SRCS   = init.c config.c search.c bind.c unbind.c add.c compare.c \
+               delete.c modify.c modrdn.c group.c attribute.c \
+               conn.c candidates.c dncache.c
+OBJS   = init.lo config.lo search.lo bind.lo unbind.lo add.lo compare.lo \
+               delete.lo modify.lo modrdn.lo group.lo attribute.lo \
+               conn.lo candidates.lo dncache.lo
+
+LDAP_INCDIR= ../../../include       
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-meta"
+BUILD_MOD = @BUILD_META@
+LINKAGE = @BUILD_META_DYNAMIC@
+
+LIBBASE = back_meta
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+       @touch $@
+
diff --git a/servers/slapd/back-meta/TODO b/servers/slapd/back-meta/TODO
new file mode 100644 (file)
index 0000000..11fa8cc
--- /dev/null
@@ -0,0 +1,20 @@
+* Short term:
+
+- add missing functions (FIXED)
+
+- review per-target error handling
+
+- dn cache and cache exploitation to refine the candidate selection (?) (FIXED)
+
+- review the group and attribute stuff (also in back-ldap!) (FIXED, need to
+  test attribute)
+
+- rework compare and bind to attempt to operate on all candidate entries
+  while checking at most one succeedes
+
+- clear previously bound targets when the bind is repeated
+
+* Long term:
+
+- distributed entries
+
diff --git a/servers/slapd/back-meta/add.c b/servers/slapd/back-meta/add.c
new file mode 100644 (file)
index 0000000..4f50305
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ * 
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ * 
+ * 4. This notice may not be removed or altered.
+ * 
+ * 
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ * 
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ * 
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *                
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_add(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               Entry           *e
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       int i, candidate = -1;
+       Attribute *a;
+       LDAPMod **attrs;
+       char *mdn = NULL, *mapped;
+
+       /*
+        * get the current connection
+        */
+       lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE,
+                       e->e_ndn, &candidate );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       /*
+        * Rewrite the add dn, if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "addDn", e->e_dn, conn, &mdn )) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = e->e_dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> addDn: \"%s\" -> \"%s\"\n%s", 
+                               e->e_dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+               
+       case REWRITE_REGEXEC_ERR:
+               return -1;
+       }
+
+       /* Count number of attributes in entry */
+       for ( i = 1, a = e->e_attrs; a; i++, a = a->a_next );
+       
+       /* Create array of LDAPMods for ldap_add() */
+       attrs = ch_malloc( sizeof( LDAPMod * )*i );
+
+       for ( i = 0, a = e->e_attrs; a; a = a->a_next ) {
+               /*
+                * lastmod should always be <off>, so that
+                * creation/modification operational attrs
+                * of the target directory are used, if available
+                */
+#if 0
+               if ( !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_creatorsName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_createTimestamp->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifiersName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val )
+               ) {
+                       continue;
+               }
+#endif
+               
+               mapped = ldap_back_map( &li->targets[ candidate ]->at_map,
+                               a->a_desc->ad_cname->bv_val, 0);
+               if ( mapped == NULL ) {
+                       continue;
+               }
+
+               attrs[ i ] = ch_malloc( sizeof( LDAPMod ) );
+               if ( attrs[ i ] == NULL ) {
+                       continue;
+               }
+               attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
+               attrs[ i ]->mod_type = mapped;
+
+               /*
+                * FIXME: dn-valued attrs should be rewritten
+                * to allow their use in ACLs at the back-ldap
+                * level.
+                */
+               if ( strcmp( a->a_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       ldap_dnattr_rewrite( li->targets[ candidate ]->rwinfo,
+                                       a->a_vals, conn );
+               }
+
+               attrs[ i ]->mod_vals.modv_bvals = a->a_vals;
+               i++;
+       }
+       attrs[ i ] = NULL;
+
+       ldap_add_s( lc->conns[ candidate ]->ld, mdn, attrs );
+       for ( --i; i >= 0; --i ) {
+               free( attrs[ i ] );
+       }
+       free( attrs );
+       if ( mdn != e->e_dn ) {
+               free( mdn );
+       }
+       return meta_back_op_result( lc, op );
+}
+
diff --git a/servers/slapd/back-meta/attribute.c b/servers/slapd/back-meta/attribute.c
new file mode 100644 (file)
index 0000000..46c4623
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ * 
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ * 
+ * 
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ * 
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ * 
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+
+/* return 0 IFF we can retrieve the attributes
+ * of entry with e_ndn
+ */
+
+/*
+ * FIXME: I never testd this function; I know it compiles ... :)
+ */
+int
+meta_back_attribute(
+               Backend                 *be,
+               Connection              *conn,
+               Operation               *op,
+               Entry                   *target,
+               const char              *e_ndn,
+               AttributeDescription    *entry_at,
+               struct berval           ***vals
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;    
+       int rc = 1, i, j, count, is_oc, candidate;
+       Attribute *attr;
+       struct berval **abv, **v;
+       char **vs, *mapped;
+       LDAPMessage     *result, *e;
+       char *gattr[ 2 ];
+       LDAP *ld;
+       char *me_ndn;
+
+       *vals = NULL;
+       if ( target != NULL && strcmp( target->e_ndn, e_ndn ) == 0 ) {
+               /* we already have a copy of the entry */
+               /* attribute and objectclass mapping has already been done */
+               attr = attr_find( target->e_attrs, entry_at );
+               if ( attr == NULL ) {
+                       return 1;
+               }
+
+               for ( count = 0; attr->a_vals[ count ] != NULL; count++ )
+                       ;
+               v = ch_calloc( ( count + 1 ), sizeof( struct berval * ) );
+               if ( v != NULL ) {
+                       for ( j = 0, abv = attr->a_vals; --count >= 0; abv++ ) {
+                               if ( ( *abv )->bv_len > 0 ) {
+                                       v[ j ] = ber_bvdup( *abv );
+                                       if ( v[ j ] == NULL ) {
+                                               break;
+                                       }
+                               }
+                       }
+                       v[ j ] = NULL;
+                       *vals = v;
+                       rc = 0;
+               }
+
+               return rc;
+       } /* else */
+
+       candidate = meta_back_select_unique_candidate( li, e_ndn );
+       if ( candidate == -1 ) {
+               return 1;
+       }
+
+       mapped = ldap_back_map( &li->targets[ candidate ]->at_map,
+                       entry_at->ad_cname->bv_val, 0 );
+       if ( mapped == NULL )
+               return 1;
+
+       rc =  ldap_initialize( &ld, li->targets[ candidate ]->uri );
+       if ( rc != LDAP_SUCCESS ) {
+               return 1;
+       }
+
+       rc = ldap_bind_s( ld, li->targets[ candidate ]->binddn,
+                       li->targets[ candidate ]->bindpw, LDAP_AUTH_SIMPLE );
+       if ( rc != LDAP_SUCCESS) {
+               return 1;
+       }
+
+       gattr[ 0 ] = mapped;
+       gattr[ 1 ] = NULL;
+       if ( ldap_search_ext_s( ld, e_ndn, LDAP_SCOPE_BASE, "(objectclass=*)",
+                               gattr, 0, NULL, NULL, LDAP_NO_LIMIT,
+                               LDAP_NO_LIMIT, &result) == LDAP_SUCCESS) {
+               if ( ( e = ldap_first_entry( ld, result ) ) != NULL ) {
+                       vs = ldap_get_values( ld, e, mapped );
+                       if ( vs != NULL ) {
+                               for ( count = 0; vs[ count ] != NULL;
+                                               count++ ) { }
+                               v = ch_calloc( ( count + 1 ),
+                                               sizeof( struct berval * ) );
+                               if ( v == NULL ) {
+                                       ldap_value_free( vs );
+                               } else {
+                                       is_oc = ( strcasecmp( "objectclass", mapped ) == 0 );
+                                       for ( i = 0, j = 0; i < count; i++ ) {
+                                               if ( !is_oc ) {
+                                                       v[ j ] = ber_bvstr( vs[ i ] );
+                                                       if ( v[ j ] == NULL ) {
+                                                               ch_free( vs[ i ] );
+                                                       } else {
+                                                               j++;
+                                                       }
+                                               } else {
+                                                       mapped = ldap_back_map( &li->targets[ candidate ]->oc_map, vs[ i ], 1 );
+                                                       if ( mapped ) {
+                                                               mapped = ch_strdup( mapped );
+                                                               if ( mapped ) {
+                                                                       v[ j ] = ber_bvstr( mapped );
+                                                                       if ( v[ j ] ) {
+                                                                               j++;
+                                                                       }
+                                                               }
+                                                       }
+                                                       ch_free( vs[ i ] );
+                                               }
+                                       }
+                                       v[ j ] = NULL;
+                                       *vals = v;
+                                       rc = 0;
+                                       ch_free( vs );
+                               }
+                       }
+               }
+               ldap_msgfree( result );
+       }
+       ldap_unbind(ld);
+
+       return(rc);
+}
+
diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h
new file mode 100644 (file)
index 0000000..5d0c433
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *                         
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *                
+ */ 
+
+#ifndef SLAPD_LDAP_H
+#error "include servers/slapd/back-ldap/back-ldap.h before this file!"
+#endif /* SLAPD_LDAP_H */
+
+#ifndef SLAPD_META_H
+#define SLAPD_META_H
+
+#include "external.h"
+
+/* String rewrite library */
+#include "rewrite.h"
+
+LDAP_BEGIN_DECL
+
+struct slap_conn;
+struct slap_op;
+
+struct metasingleconn {
+       int                     candidate;
+#define        META_NOT_CANDIDATE      0
+#define        META_CANDIDATE          1
+       
+       LDAP                    *ld;
+       char                    *bound_dn;
+       int                     bound;
+#define META_UNBOUND           0
+#define META_BOUND             1
+#define META_ANONYMOUS         2
+};
+
+struct metaconn {
+       struct slap_conn        *conn;
+       struct rewrite_info     *rwinfo;
+       
+       /*
+        * means that the connection is bound; 
+        * of course only one target actually is ...
+        */
+       int             bound_target;
+       /* supersedes the connection stuff */
+       struct metasingleconn **conns;
+};
+
+struct metatarget {
+       char                    *uri;
+       char                    *suffix;/* normalized suffix */
+       char                    *binddn;
+       char                    *bindpw;
+
+       struct rewrite_info     *rwinfo;
+
+       struct ldapmap          oc_map;
+       struct ldapmap          at_map;
+};
+
+struct metadncache {
+       ldap_pvt_thread_mutex_t mutex;
+       Avlnode                 *tree;
+
+#define META_DNCACHE_DISABLED   0
+#define META_DNCACHE_FOREVER    -1
+       long int                ttl;  /* seconds; 0: no cache, -1: no expiry */
+};
+
+struct metainfo {
+       int                     ntargets;
+       int                     defaulttarget;
+#define META_DEFAULT_TARGET_NONE       -1
+       struct metatarget       **targets;
+
+       struct metadncache      cache;
+       
+       ldap_pvt_thread_mutex_t conn_mutex;
+       Avlnode                 *conntree;
+};
+
+extern int
+meta_back_do_single_bind(
+               Operation               *op,
+               struct metainfo         *li,
+               struct metaconn         *lc,
+               const char              *dn,
+               const char              *ndn,
+               struct berval           *cred,
+               int                     method,
+               int                     candidate
+);
+
+
+#define META_OP_ALLOW_MULTIPLE         0x00
+#define META_OP_REQUIRE_SINGLE         0x01
+extern struct metaconn *
+meta_back_getconn(
+               struct                  metainfo *li,
+               struct                  slap_conn *conn,
+               struct                  slap_op *op,
+               int                     op_type,
+               const                   char *dn,
+               int                     *candidate
+);
+
+extern int
+meta_back_dobind(
+               struct metaconn         *lc,
+               Operation               *op
+);
+
+extern int
+meta_back_op_result(
+               struct metaconn         *lc,
+               Operation               *op
+);
+
+extern int
+back_meta_LTX_init_module(
+               int                     argc,
+               char                    *argv[]
+);
+
+extern int
+meta_back_conn_cmp(
+               const void              *c1,
+               const void              *c2
+);
+
+extern int
+meta_back_conn_dup(
+               void                    *c1,
+               void                    *c2
+);
+
+/*
+ * Candidate stuff
+ */
+extern int
+meta_back_is_candidate(
+               const char              *nsuffix,
+               const char              *ndn,
+               int                     ndnlen
+);
+
+extern int
+meta_back_count_candidates(
+               struct metainfo         *li,
+               const char              *ndn
+);
+
+extern int
+meta_back_is_candidate_unique(
+               struct metainfo         *li,
+               const char              *ndn
+);
+
+extern int
+meta_back_select_unique_candidate(
+               struct metainfo         *li,
+               const char              *ndn
+);
+
+extern int
+meta_clear_unused_candidates(
+               struct metainfo         *li,
+               struct metaconn         *lc,
+               int                     candidate,
+               int                     reallyclean
+);
+
+extern int
+meta_clear_one_candidate(
+               struct metasingleconn   *lc,
+               int                     reallyclean
+);
+
+/*
+ * Dn cache stuff (experimental)
+ */
+extern int
+meta_dncache_cmp(
+               const void              *c1,
+               const void              *c2
+);
+
+extern int
+meta_dncache_dup(
+               void                    *c1,
+               void                    *c2
+);
+
+extern int
+meta_dncache_get_target(
+               struct metadncache      *cache,
+               const char              *ndn
+);
+
+extern int
+meta_dncache_update_entry(
+               struct metadncache      *cache,
+               const char              *ndn,
+               int                     target
+);
+
+extern int
+meta_dncache_delete_entry(
+               struct metadncache      *cache,
+               const char              *ndn
+);
+
+extern void
+meta_dncache_free(
+               void *entry
+);
+
+LDAP_END_DECL
+
+#endif /* SLAPD_META_H */
+
diff --git a/servers/slapd/back-meta/bind.c b/servers/slapd/back-meta/bind.c
new file mode 100644 (file)
index 0000000..219e8b9
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_bind(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               const char      *dn,
+               const char      *ndn,
+               int             method,
+               struct berval   *cred,
+               char            **edn
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+
+       int rc = -1, i, gotit = 0, ndnlen, err = LDAP_SUCCESS;
+
+#ifdef NEW_LOGGING
+       LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
+                               "meta_back_bind: dn: %s.\n", dn ));
+#else /* !NEW_LOGGING */
+       Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s", dn, "", "" );
+#endif /* !NEW_LOGGING */
+
+       *edn = NULL;
+
+       lc = meta_back_getconn( li, conn, op, META_OP_ALLOW_MULTIPLE,
+                       ndn, NULL );
+       if ( !lc ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "backend", LDAP_LEVEL_NOTICE,
+                               "meta_back_bind: no target for dn %s.\n", dn ));
+#else /* !NEW_LOGGING */
+               Debug( LDAP_DEBUG_ANY,
+                               "meta_back_bind: no target for dn %s.\n%s%s",
+                               dn, "", "");
+#endif /* !NEW_LOGGING */
+               return -1;
+       }
+
+       /*
+        * Each target is scanned ...
+        */
+       ndnlen = strlen( ndn );
+       for ( i = 0; i < li->ntargets; i++ ) {
+               int lerr;
+
+               /*
+                * Skip non-candidates
+                */
+               if ( lc->conns[ i ]->candidate != META_CANDIDATE ) {
+                       continue;
+               }
+
+               if ( gotit == 0 ) {
+                       gotit = 1;
+               } else {
+                       /*
+                        * A bind operation is expected to have
+                        * ONE CANDIDATE ONLY!
+                        */
+#ifdef NEW_LOGGING
+                       LDAP_LOG(( "backend", LDAP_LEVEL_WARNING,
+"==>meta_back_bind: more that one candidate is attempting to bind ...%s%s%s\n" ));
+#else /* !NEW_LOGGING */
+                       Debug( LDAP_DEBUG_ANY,
+"==>meta_back_bind: more that one candidate is attempting to bind ...%s%s%s\n", 
+                               "", "", "" );
+#endif /* !NEW_LOGGING */
+               }
+
+
+               lerr = meta_back_do_single_bind( op, li, lc, dn, ndn, cred,
+                               method, i );
+               if ( lerr != LDAP_SUCCESS ) {
+                       err = lerr;
+                       ( void )meta_clear_one_candidate( lc->conns[ i ], 1 );
+               } else {
+                       rc = LDAP_SUCCESS;
+               }
+       }
+
+       if ( rc != LDAP_SUCCESS && err != LDAP_SUCCESS ) {
+               
+               /*
+                * deal with bind failure ...
+                */
+               err = ldap_back_map_result( err );
+               send_ldap_result( conn, op, err, NULL, "", NULL, NULL );
+       }
+
+       return LDAP_SUCCESS;
+}
+
+/*
+ * meta_back_do_single_bind
+ *
+ * attempts to perform a bind with creds
+ */
+int
+meta_back_do_single_bind(
+               Operation               *op,
+               struct metainfo         *li,
+               struct metaconn         *lc,
+               const char              *dn,
+               const char              *ndn,
+               struct berval           *cred,
+               int                     method,
+               int                     candidate
+)
+{
+       char *mdn = NULL;
+       int rc;
+       
+       /*
+        * Rewrite the bind dn if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "bindDn", dn, lc->conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                               "rw> bindDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( lc->conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform",
+                               NULL, NULL );
+               /* continues to next case */
+
+       case REWRITE_REGEXEC_ERR:
+               return -1;
+       }
+
+       rc = ldap_bind_s( lc->conns[ candidate ]->ld, mdn,
+                       cred->bv_val, method );
+       if ( rc != LDAP_SUCCESS ) {
+               rc = ldap_back_map_result( rc );
+       } else {
+               lc->conns[ candidate ]->bound_dn = ch_strdup( dn );
+               lc->conns[ candidate ]->bound = META_BOUND;
+               lc->bound_target = candidate;
+
+               if ( li->cache.ttl != META_DNCACHE_DISABLED
+                               && ndn[ 0 ] != '\0' ) {
+                       ( void )meta_dncache_update_entry( &li->cache,
+                                       ch_strdup( ndn ), candidate );
+               }
+       }
+       
+       if ( mdn != dn ) {
+               free( mdn );
+       }
+
+       return rc;
+}
+
+/*
+ * meta_back_dobind
+ */
+int
+meta_back_dobind( struct metaconn *lc, Operation *op )
+{
+       struct metasingleconn **lsc;
+       int bound = 0, i;
+
+       for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) {
+               int rc;
+
+               /*
+                * Not a candidate or something wrong with this target ...
+                */
+               if ( lsc[ 0 ]->ld == NULL ) {
+                       continue;
+               }
+
+               /*
+                * If the target is already bound it is skipped
+                */
+               if ( lsc[ 0 ]->bound == META_BOUND && lc->bound_target == i ) {
+                       ++bound;
+                       continue;
+               }
+
+               /*
+                * Otherwise an anonymous bind is performed
+                * (note: if the target was already bound, the anonymous
+                * bind clears the previous bind).
+                */
+               rc = ldap_bind_s( lsc[ 0 ]->ld, lsc[ 0 ]->bound_dn,
+                               NULL, LDAP_AUTH_SIMPLE );
+               if ( rc != LDAP_SUCCESS ) {
+                       
+                       /*
+                        * This way, the first bind error would be fatal ...
+                        */
+                       Debug( LDAP_DEBUG_ANY,
+       "==>meta_back_dobind: (anonymous) bind as \"%s\" failed"
+       " with error \"%s\"\n%s",
+                               lsc[ 0 ]->bound_dn,
+                               ldap_err2string( rc ), "" );
+
+                       /*
+                        * null cred bind should always succeed
+                        * as anonymous, so a failure means
+                        * the target is no longer candidate possibly
+                        * due to technical reasons (remote host down?)
+                        *
+                        * so better clear the handle
+                        */
+                       ( void )meta_clear_one_candidate( lsc[ 0 ], 1 );
+                       continue;
+               } /* else */
+               
+               lsc[ 0 ]->bound = META_ANONYMOUS;
+               ++bound;
+       }
+
+       return( bound > 0 );
+}
+
+/*
+ * FIXME: error return must be handled in a cleaner way ...
+ */
+int
+meta_back_op_result( struct metaconn *lc, Operation *op )
+{
+       int i, err = LDAP_SUCCESS;
+       char *msg = NULL;
+       char *match = NULL;
+       struct metasingleconn **lsc;
+
+       for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) {
+               ldap_get_option( lsc[ 0 ]->ld, LDAP_OPT_ERROR_NUMBER, &err );
+               if ( err != LDAP_SUCCESS ) {
+                       /*
+                        * better check the type of error. In some cases
+                        * (search ?) it might be better to return a
+                        * success if at least one of the targets gave
+                        * positive result ...
+                        */
+                       ldap_get_option( lsc[ 0 ]->ld,
+                                       LDAP_OPT_ERROR_STRING, &msg );
+                       ldap_get_option( lsc[ 0 ]->ld,
+                                       LDAP_OPT_MATCHED_DN, &match );
+                       err = ldap_back_map_result( err );
+
+                       /*
+                        * FIXME: need to rewrite "match"
+                        */
+                       send_ldap_result( lc->conn, op, err, match, msg,
+                                       NULL, NULL );
+                       
+                       Debug(LDAP_DEBUG_ANY,
+"==> meta_back_op_result: target <%d> sending msg \"%s\" (matched \"%s\")\n", 
+                               i,
+                               ( msg ? msg : "" ),
+                               ( match ? match : "" ) );
+
+                       /* better test the pointers before freeing? */
+                       if ( match ) {
+                               free( match );
+                       }
+                       if ( msg ) {
+                               free( msg );
+                       }
+                       return -1;
+               }
+       }
+
+       return ( err == LDAP_SUCCESS ) ? 0 : -1;
+}
+
diff --git a/servers/slapd/back-meta/candidates.c b/servers/slapd/back-meta/candidates.c
new file mode 100644 (file)
index 0000000..fe08ced
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *                
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * The meta-directory has one suffix, called <suffix>.
+ * It handles a pool of target servers, each with a branch suffix
+ * of the form <branch X>,<suffix>
+ *
+ * When the meta-directory receives a request with a dn that belongs
+ * to a branch, the corresponding target is invoked. When the dn
+ * does not belong to a specific branch, all the targets that
+ * are compatible with the dn are selected as candidates, and
+ * the request is spawned to all the candidate targets
+ *
+ * A request is characterized by a dn. The following cases are handled:
+ *     - the dn is the suffix: <dn> == <suffix>,
+ *             all the targets are candidates (search ...)
+ *     - the dn is a branch suffix: <dn> == <branch X>,<suffix>, or
+ *     - the dn is a subtree of a branch suffix:
+ *             <dn> == <rdn>,<branch X>,<suffix>,
+ *             the target is the only candidate.
+ *
+ * A possible extension will include the handling of multiple suffixes
+ */
+
+/*
+ * returns 1 if suffix is candidate for dn, otherwise 0
+ *
+ * Note: this function should never be called if dn is the <suffix>.
+ */
+int 
+meta_back_is_candidate(
+               const char      *nsuffix,
+               const char      *ndn,
+               int             ndnlen
+)
+{
+       int len = strlen( nsuffix );
+
+       if ( len > ndnlen ) {
+               /*
+                * suffix longer than dn
+                */
+               if ( ! DN_SEPARATOR( nsuffix[ ( len - ndnlen ) - 1 ] ) ) {
+                       /*
+                        * not a separator begins the possible common part
+                        */
+                       return META_NOT_CANDIDATE;
+               }
+
+               if ( strcmp( &nsuffix[ len - ndnlen] , ndn ) == 0 ) {
+                       /*
+                        * Got it!
+                        */
+                       return META_CANDIDATE;
+               }
+       }
+
+       if ( len < ndnlen && ! DN_SEPARATOR( ndn[ ( ndnlen - len ) - 1 ] ) ) {
+               /*
+                * not a separator begins the possible common part
+                */
+               return META_NOT_CANDIDATE;
+       }
+
+       if ( strcmp( nsuffix, &ndn[ ndnlen - len ] ) == 0 ) {
+               /*
+                * Got it!
+                */
+               return META_CANDIDATE;
+       }
+
+       return META_NOT_CANDIDATE;
+}
+
+/*
+ * meta_back_count_candidates
+ *
+ * returns a count of the possible candidate targets
+ * Note: dn MUST be normalized
+ */
+
+int
+meta_back_count_candidates(
+               struct metainfo         *li,
+               const char              *ndn
+)
+{
+       int i, cnt = 0, ndnlen = strlen( ndn );
+
+       /*
+        * I know assertions should not check run-time values;
+        * at present I didn't find a place for such checks
+        * after config.c
+        */
+       assert( li->targets != NULL );
+       assert( li->ntargets != 0 );
+
+       for ( i = 0; i < li->ntargets; ++i ) {
+               if ( meta_back_is_candidate( li->targets[ i ]->suffix,
+                                       ndn, ndnlen ) ) {
+                       ++cnt;
+               }
+       }
+
+       return cnt;
+}
+
+/*
+ * meta_back_is_candidate_unique
+ *
+ * checks whether a candidate is unique
+ * Note: dn MUST be normalized
+ */
+int
+meta_back_is_candidate_unique(
+               struct metainfo         *li,
+               const char              *ndn
+)
+{
+       return ( meta_back_count_candidates( li, ndn ) == 1 );
+}
+
+/*
+ * meta_back_select_unique_candidate
+ *
+ * returns the index of the candidate in case it is unique, otherwise -1
+ * Note: dn MUST be normalized.
+ * Note: if defined, the default candidate is returned in case of no match.
+ */
+int
+meta_back_select_unique_candidate(
+               struct metainfo         *li,
+               const char              *ndn
+)
+{
+       int i, ndnlen;
+       
+       switch ( meta_back_count_candidates( li, ndn ) ) {
+       case 1:
+               break;
+       case 0:
+       default:
+               return ( li->defaulttarget == META_DEFAULT_TARGET_NONE
+                               ? -1 : li->defaulttarget );
+       }
+
+       ndnlen = strlen( ndn );
+       for ( i = 0; i < li->ntargets; ++i ) {
+               if ( meta_back_is_candidate( li->targets[ i ]->suffix,
+                                       ndn, ndnlen ) ) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+/*
+ * meta_clear_unused_candidates
+ *
+ * clears all candidates except candidate
+ */
+int
+meta_clear_unused_candidates(
+               struct metainfo         *li,
+               struct metaconn         *lc,
+               int                     candidate,
+               int                     reallyclean
+)
+{
+       int i;
+       
+       for ( i = 0; i < li->ntargets; ++i ) {
+               if ( i == candidate ) {
+                       continue;
+               }
+               meta_clear_one_candidate( lc->conns[ i ], reallyclean );
+       }
+
+       return 0;
+}
+
+/*
+ * meta_clear_one_candidate
+ *
+ * clears the selected candidate
+ */
+int
+meta_clear_one_candidate(
+               struct metasingleconn   *lsc,
+               int                     reallyclean
+)
+{
+       lsc->candidate = META_NOT_CANDIDATE;
+
+       if ( !reallyclean ) {
+               return 0;
+       }
+
+       if ( lsc->ld ) {
+               ldap_unbind( lsc->ld );
+               lsc->ld = NULL;
+       }
+
+       if ( lsc->bound_dn != NULL ) {
+               free( lsc->bound_dn );
+               lsc->bound_dn = NULL;
+       }
+
+       return 0;
+}
+
diff --git a/servers/slapd/back-meta/compare.c b/servers/slapd/back-meta/compare.c
new file mode 100644 (file)
index 0000000..9ba3ac2
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ * 
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_compare(
+               Backend                 *be,
+               Connection              *conn,
+               Operation               *op,
+               const char              *dn,
+               const char              *ndn,
+               AttributeAssertion      *ava
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       struct metasingleconn **lsc;
+       char *match = NULL, *err = NULL, *mmatch = NULL;
+       int candidates = 0, last = 0, i, count, rc, cres, rres;
+       int *msgid;
+
+       lc = meta_back_getconn( li, conn, op, META_OP_ALLOW_MULTIPLE,
+                       ndn, NULL );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       msgid = ch_calloc( sizeof( int ), li->ntargets );
+       if ( msgid == NULL ) {
+               return -1;
+       }
+
+       /*
+        * start an asynchronous compare for each candidate target
+        */
+       for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) {
+               char *mdn = NULL;
+               char *mapped_attr = ava->aa_desc->ad_cname->bv_val;
+               char *mapped_value = ava->aa_value->bv_val;
+
+               if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
+                       continue;
+               }
+
+               /*
+                * Rewrite the compare dn, if needed
+                */
+               switch ( rewrite_session( li->targets[ i ]->rwinfo,
+                                       "compareDn", dn, conn, &mdn ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mdn == NULL ) {
+                               mdn = ( char * )dn;
+                       }
+                       Debug( LDAP_DEBUG_ARGS,
+                                       "rw> compareDn: \"%s\" -> \"%s\"\n%s",
+                                       dn, mdn, "" );
+                       break;
+               
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       
+               case REWRITE_REGEXEC_ERR:
+                       return -1;
+               }
+
+               /*
+                * if attr is objectClass, try to remap the value
+                */
+               if ( ava->aa_desc->ad_type->sat_oid 
+                       == slap_schema.si_ad_objectClass->ad_type->sat_oid ) {
+                       mapped_value = ldap_back_map( &li->targets[ i ]->oc_map,
+                                       ava->aa_value->bv_val, 0 );
+
+                       if ( mapped_value == NULL ) {
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               continue;
+                       }
+               /*
+                * else try to remap the attribute
+                */
+               } else {
+                       mapped_attr = ldap_back_map( &li->targets[ i ]->at_map,
+                               ava->aa_desc->ad_cname->bv_val, 0 );
+                       if ( mapped_attr == NULL ) {
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               continue;
+                       }
+               }
+               
+               /*
+                * the compare op is spawned across the targets and the first
+                * that returns determines the result; a constraint on unicity
+                * of the result ought to be enforced
+                */
+               msgid[ i ] = ldap_compare( lc->conns[ i ]->ld, mdn,
+                               mapped_attr, mapped_value );
+               if ( msgid[ i ] == -1 ) {
+                       lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                       continue;
+               }
+
+               if ( mdn != dn ) {
+                       free( mdn );
+               }
+               if ( mapped_attr != ava->aa_desc->ad_cname->bv_val ) {
+                       free( mapped_attr );
+               }
+               if ( mapped_value != ava->aa_value->bv_val ) {
+                       free( mapped_value );
+               }
+
+               ++candidates;
+       }
+
+       /*
+        * wait for replies
+        */
+       for ( rc = 0, count = 0; candidates > 0; ) {
+
+               /*
+                * FIXME: should we check for abandon?
+                */
+               for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; lsc++, i++ ) {
+                       int lrc;
+                       LDAPMessage *res = NULL;
+
+                       if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
+                               continue;
+                       }
+
+                       lrc = ldap_result( lsc[ 0 ]->ld, msgid[ i ],
+                                       0, NULL, &res );
+
+                       if ( lrc == 0 ) {
+                               /*
+                                * FIXME: should we yield?
+                                */
+                               if ( res ) {
+                                       ldap_msgfree( res );
+                               }
+                               continue;
+                       } else if ( lrc == LDAP_RES_COMPARE ) {
+                               if ( count > 0 ) {
+                                       rres = LDAP_OPERATIONS_ERROR;
+                                       rc = -1;
+                                       goto finish;
+                               }
+                               
+                               cres = ldap_result2error( lsc[ 0 ]->ld,
+                                               res, 1 );
+                               switch ( cres ) {
+                               case LDAP_COMPARE_TRUE:
+                               case LDAP_COMPARE_FALSE:
+
+                                       /*
+                                        * true or flase, got it;
+                                        * sending to cache ...
+                                        */
+                                       if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
+                                               ( void )meta_dncache_update_entry( &li->cache, ch_strdup( ndn ), i );
+                                       }
+
+                                       count++;
+                                       rc = 0;
+                                       break;
+
+                               default:
+                                       rres = ldap_back_map_result( cres );
+
+                                       if ( err != NULL ) {
+                                               free( err );
+                                       }
+                                       ldap_get_option( lsc[ 0 ]->ld,
+                                               LDAP_OPT_ERROR_STRING, &err );
+
+                                       if ( match != NULL ) {
+                                               free( match );
+                                       }
+                                       ldap_get_option( lsc[ 0 ]->ld,
+                                               LDAP_OPT_MATCHED_DN, &match );
+                                       
+                                       last = i;
+                                       break;
+                               }
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               --candidates;
+                       } else {
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               --candidates;
+                               if ( res ) {
+                                       ldap_msgfree( res );
+                               }
+                               break;
+                       }
+               }
+       }
+
+finish:
+
+       /*
+        * Rewrite the matched portion of the search base, if required
+        * 
+        * FIXME: only the last one gets caught!
+        */
+       if ( count == 1 ) {
+               if ( match != NULL ) {
+                       free( match );
+                       match = NULL;
+               }
+               
+               /*
+                * the result of the compare is assigned to the res code
+                * that will be returned
+                */
+               rres = cres;
+               
+       } else if ( match != NULL ) {
+               
+               /*
+                * At least one compare failed with matched portion,
+                * and none was successful
+                */
+               switch ( rewrite_session( li->targets[ last ]->rwinfo,
+                                       "matchedDn", match, conn, &mmatch ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mmatch == NULL ) {
+                               mmatch = ( char * )match;
+                       }
+                       Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:"
+                                       " \"%s\" -> \"%s\"\n%s",
+                                       match, mmatch, "" );
+                       break;
+                       
+               
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       /* continue to the next case */
+                       
+               case REWRITE_REGEXEC_ERR:
+                       rc = -1;
+                       break;
+               }
+       }       
+       send_ldap_result( conn, op, rres, mmatch, err, NULL, NULL );
+
+       if ( match != NULL ) {
+               if ( mmatch != match ) {
+                       free( mmatch );
+               }
+               free( match );
+       }
+
+       if ( msgid ) {
+               free( msgid );
+       }
+       
+       return rc;
+}
+
diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c
new file mode 100644 (file)
index 0000000..7c449b2
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+extern int
+suffix_massage_config(
+               struct rewrite_info *info,
+               int argc,
+               char **argv
+);
+
+static struct metatarget *
+new_target( void )
+{
+       struct metatarget *lt;
+        struct ldapmapping *mapping;
+
+       lt = ch_calloc( sizeof( struct metatarget ), 1 );
+       if ( lt == NULL ) {
+               return NULL;
+       }
+
+       lt->rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+       if ( lt->rwinfo == NULL ) {
+               free( lt );
+                return NULL;
+       }
+
+       mapping = ch_calloc( 2, sizeof( struct ldapmapping ) );
+        if ( mapping == NULL ) {
+               free( lt );
+               return NULL;
+       }
+       mapping->src = ch_strdup( "objectClass" );
+       mapping->dst = ch_strdup( "objectClass" );
+       mapping[ 1 ].src = mapping->src;
+       mapping[ 1 ].dst = mapping->dst;
+
+       avl_insert( &lt->at_map.map, ( caddr_t )mapping, 
+                       mapping_cmp, mapping_dup );
+       avl_insert( &lt->at_map.remap, ( caddr_t )&mapping[ 1 ],
+                       mapping_cmp, mapping_dup );
+
+       return lt;
+}
+
+int
+meta_back_db_config(
+               BackendDB       *be,
+               const char      *fname,
+               int             lineno,
+               int             argc,
+               char            **argv
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+
+       if ( li == NULL ) {
+               fprintf( stderr, 
+       "%s: line %d: meta backend info is null!\n",
+                   fname, lineno );
+               return 1;
+       }
+
+       /* URI of server to query */
+       if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
+               int j, i = li->ntargets;
+               LDAPURLDesc *ludp;
+               char *last;
+               
+               if ( argc != 2 ) {
+                       fprintf( stderr,
+       "%s: line %d: missing address"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+               
+               ++li->ntargets;
+
+               li->targets = ch_realloc( li->targets, 
+                       sizeof( struct metatarget *)*li->ntargets );
+               if ( li->targets == NULL ) {
+                       fprintf( stderr,
+       "%s: line %d: out of memory while storing server name"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               if ( ( li->targets[ i ] = new_target() ) == NULL ) {
+                       fprintf( stderr,
+       "%s: line %d: unable to init server"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               /*
+                * uri MUST be legal!
+                */
+               if ( ldap_url_parse( argv[ 1 ], &ludp ) != LDAP_SUCCESS ) {
+                       fprintf( stderr,
+       "%s: line %d: unable to parse URI"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               /*
+                * uri MUST have the <dn> part!
+                */
+               if ( ludp->lud_dn == NULL || ludp->lud_dn[ 0 ] == '\0' ) {
+                       fprintf( stderr,
+       "%s: line %d: missing <naming context> "
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               /*
+                * copies and stores uri and suffix
+                */
+               li->targets[ i ]->suffix = ch_strdup( ludp->lud_dn );
+               li->targets[ i ]->uri = ch_strdup( argv[ 1 ] );
+               last = strstr( li->targets[ i ]->uri,
+                               li->targets[ i ]->suffix );
+               assert( last != NULL );
+               last[ 0 ] = '\0'; /* wasting memory ... */
+               
+               /*
+                * Need to store the suffix in normalized form
+                */
+               (void) dn_normalize( li->targets[ i ]->suffix );
+               
+               /*
+                * uri MUST be a branch of suffix!
+                */
+#if 0 /* too strict a constraint */
+               if ( select_backend( li->targets[ i ]->suffix, 0 ) != be ) {
+                       fprintf( stderr,
+       "%s: line %d: <naming context> of URI does not refer to current backend"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+#else
+               /*
+                * uri MUST be a branch of a suffix!
+                */
+               if ( select_backend( li->targets[ i ]->suffix, 0 ) == NULL ) {
+                       fprintf( stderr,
+       "%s: line %d: <naming context> of URI does not resolve to a backend"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+#endif
+
+#if 0
+               /*
+                * uri MUST not be used by other URIs!
+                *
+                * FIXME: this limitation may be removed,
+                * or worked out, at least, in some manner
+                */
+               for ( j = 0; j < i-1; j++ ) {
+                       if ( strcmp( li->targets[ i ]->suffix,
+                                       li->targets[ j ]->suffix ) == 0 ) {
+                               fprintf( stderr,
+       "%s: line %d: naming context \"%s\" already used"
+       " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
+                                       fname, lineno, last+1 );
+                               return 1;
+                       }
+               }
+#endif
+               
+               ldap_free_urldesc( ludp );
+
+               Debug( LDAP_DEBUG_CONFIG,
+       "==>meta_back_db_config: URI \"%s\", suffix \"%s\"%s\n",
+                       li->targets[ i ]->uri, li->targets[ i ]->suffix, "" );
+
+       /* default target directive */
+       } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
+               int i = li->ntargets-1;
+               
+               if ( argc == 1 ) {
+                       if ( i < 0 ) {
+                               fprintf( stderr,
+       "%s: line %d: \"default-target\" alone need be"
+               " inside a \"uri\" directive\n",
+                                       fname, lineno );
+                               return 1;
+                       }
+                       li->defaulttarget = i;
+               } else {
+                       if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) {
+                               if ( i >= 0 ) {
+                                       fprintf( stderr,
+       "%s: line %d: \"default-target none\""
+               " should go before uri definitions\n",
+                                               fname, lineno );
+                               }
+                               li->defaulttarget = META_DEFAULT_TARGET_NONE;
+                       } else {
+                               int n = atoi( argv[ 1 ] );
+                               if ( n < 1 || n >= i ) {
+                                       fprintf( stderr,
+       "%s: line %d: illegal target number %d\n",
+                                               fname, lineno, n );
+                                       return 1;
+                               }
+                               li->defaulttarget = n-1;
+                       }
+               }
+               
+       /* ttl of dn cache */
+       } else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) {
+               if ( argc != 2 ) {
+                       fprintf( stderr,
+       "%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+               
+               if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
+                       li->cache.ttl = META_DNCACHE_FOREVER;
+               } else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) {
+                       li->cache.ttl = META_DNCACHE_DISABLED;
+               } else {
+                       li->cache.ttl = atol( argv[ 1 ] );
+               }
+
+       /* name to use for meta_back_group */
+       } else if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) {
+               int i = li->ntargets-1;
+
+               if ( i < 0 ) {
+                       fprintf( stderr,
+       "%s: line %d: need \"uri\" directive first\n",
+                               fname, lineno );
+               }
+               
+               if ( argc != 2 ) {
+                       fprintf( stderr,
+       "%s: line %d: missing name in \"binddn <name>\" line\n",
+                               fname, lineno );
+                       return 1;
+               }
+               li->targets[ i ]->binddn = ch_strdup( argv[ 1 ] );
+
+       /* password to use for meta_back_group */
+       } else if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) {
+               int i = li->ntargets-1;
+
+               if ( i < 0 ) {
+                       fprintf( stderr,
+       "%s: line %d: need \"uri\" directive first\n",
+                               fname, lineno );
+               }
+               
+               if ( argc != 2 ) {
+                       fprintf( stderr,
+       "%s: line %d: missing password in \"bindpw <password>\" line\n",
+                           fname, lineno );
+                       return 1;
+               }
+               li->targets[ i ]->bindpw = ch_strdup( argv[ 1 ] );
+       
+       /* dn massaging */
+       } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
+               BackendDB *tmp_be;
+               int i = li->ntargets-1;
+
+               if ( i < 0 ) {
+                       fprintf( stderr,
+       "%s: line %d: need \"uri\" directive first\n",
+                               fname, lineno );
+                       return 1;
+               }
+               
+               /*
+                * syntax:
+                * 
+                *      suffixmassage <suffix> <massaged suffix>
+                *
+                * the <suffix> field must be defined as a valid suffix
+                * (or suffixAlias?) for the current database;
+                * the <massaged suffix> shouldn't have already been
+                * defined as a valid suffix or suffixAlias for the 
+                * current server
+                */
+               if ( argc != 3 ) {
+                       fprintf( stderr,
+       "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
+                               fname, lineno );
+                       return 1;
+               }
+               
+               tmp_be = select_backend( argv[ 1 ], 0 );
+               if ( tmp_be != NULL && tmp_be != be ) {
+                       fprintf( stderr, 
+       "%s: line %d: suffix already in use by another backend in"
+       " \"suffixMassage <suffix> <massaged suffix>\"\n",
+                               fname, lineno );
+                       return 1;                                               
+               }
+
+               tmp_be = select_backend( argv[ 2 ], 0 );
+               if ( tmp_be != NULL ) {
+                       fprintf( stderr,
+       "%s: line %d: massaged suffix already in use by another backend in" 
+       " \"suffixMassage <suffix> <massaged suffix>\"\n",
+                                fname, lineno );
+                        return 1;
+               }
+               
+               /*
+                * The suffix massaging is emulated by means of the
+                * rewrite capabilities
+                * FIXME: no extra rewrite capabilities should be added
+                * to the database
+                */
+               return suffix_massage_config( li->targets[ i ]->rwinfo,
+                               argc, argv );
+               
+       /* rewrite stuff ... */
+       } else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
+               int i = li->ntargets-1;
+
+               if ( i < 0 ) {
+                       fprintf( stderr,
+       "%s: line %d: need \"uri\" directive first\n",
+                               fname, lineno );
+               }
+               
+               return rewrite_parse( li->targets[ i ]->rwinfo, fname, lineno,
+                               argc, argv );
+
+       /* objectclass/attribute mapping */
+       } else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) {
+               struct ldapmap *map;
+               struct ldapmapping *mapping;
+               char *src, *dst;
+               int i = li->ntargets-1;
+
+               if ( i < 0 ) {
+                       fprintf( stderr,
+       "%s: line %d: need \"uri\" directive first\n",
+                               fname, lineno );
+               }
+               
+
+               if ( argc < 3 || argc > 4 ) {
+                       fprintf( stderr,
+       "%s: line %d: syntax is \"map {objectclass | attribute} {<source> | *} [<dest> | *]\"\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               if ( strcasecmp( argv[ 1 ], "objectClass" ) == 0 ) {
+                       map = &li->targets[ i ]->oc_map;
+               } else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) {
+                       map = &li->targets[ i ]->at_map;
+               } else {
+                       fprintf( stderr,
+       "%s: line %d: syntax is \"map {objectclass | attribute} {<source> | *} [<dest> | *]\"\n",
+                               fname, lineno );
+                       return 1;
+               }
+
+               if ( strcasecmp( argv[ 2 ], "*" ) != 0 ) {
+                       src = argv[ 2 ];
+                       if ( argc < 4 ) {
+                               dst = "";
+                       } else if ( strcasecmp( argv[ 3 ], "*" ) == 0 ) {
+                               dst = src;
+                       } else {
+                               dst = argv[ 3 ];
+                       }
+               } else {
+                       if ( argc < 4 ) {
+                               map->drop_missing = 1;
+                               return 0;
+                       }
+                       if ( strcasecmp( argv[ 3 ], "*" ) == 0 ) {
+                               map->drop_missing = 0;
+                               return 0;
+                       }
+
+                       src = argv[ 3 ];
+                       dst = src;
+               }
+
+               if ( ( map == &li->targets[ i ]->at_map )
+                       && ( strcasecmp( src, "objectclass" ) == 0
+                               || strcasecmp( dst, "objectclass" ) == 0 ) ) {
+                       fprintf( stderr,
+       "%s: line %d: objectclass attribute cannot be mapped\n",
+                               fname, lineno );
+               }
+
+               mapping = ch_calloc( 2, sizeof( struct ldapmapping ) );
+               if ( mapping == NULL ) {
+                       fprintf( stderr,
+                               "%s: line %d: out of memory\n",
+                               fname, lineno );
+                       return 1;
+               }
+               mapping->src = ch_strdup( src );
+               mapping->dst = ch_strdup( dst );
+               if ( *dst != 0 ) {
+                       mapping[ 1 ].src = mapping->dst;
+                       mapping[ 1 ].dst = mapping->src;
+               } else {
+                       mapping[ 1 ].src = mapping->src;
+                       mapping[ 1 ].dst = mapping->dst;
+               }
+
+               if ( avl_find( map->map, ( caddr_t )mapping,
+                               mapping_cmp ) != NULL
+                       || avl_find( map->remap, ( caddr_t )&mapping[ 1 ],
+                               mapping_cmp ) != NULL) {
+                       fprintf( stderr,
+       "%s: line %d: duplicate mapping found (ignored)\n",
+                               fname, lineno );
+                       return 0;
+               }
+
+               avl_insert( &map->map, ( caddr_t )mapping,
+                                       mapping_cmp, mapping_dup );
+               avl_insert( &map->remap, ( caddr_t )&mapping[ 1 ],
+                                       mapping_cmp, mapping_dup );
+
+       /* anything else */
+       } else {
+               fprintf( stderr,
+       "%s: line %d: unknown directive \"%s\" in meta database definition"
+       " (ignored)\n",
+                   fname, lineno, argv[0] );
+       }
+       return 0;
+}
+
diff --git a/servers/slapd/back-meta/conn.c b/servers/slapd/back-meta/conn.c
new file mode 100644 (file)
index 0000000..cf440de
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * Set PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
+ */
+#define PRINT_CONNTREE 0
+
+/*
+ * meta_back_conn_cmp
+ *
+ * compares two struct metaconn based on the value of the conn pointer;
+ * used by avl stuff
+ */
+int
+meta_back_conn_cmp(
+       const void *c1,
+       const void *c2
+       )
+{
+       struct metaconn *lc1 = ( struct metaconn * )c1;
+        struct metaconn *lc2 = ( struct metaconn * )c2;
+       
+       return ( ( lc1->conn < lc2->conn ) ? -1 :
+                       ( ( lc1->conn > lc2-> conn ) ? 1 : 0 ) );
+}
+
+/*
+ * meta_back_conn_dup
+ *
+ * returns -1 in case a duplicate struct metaconn has been inserted;
+ * used by avl stuff
+ */
+int
+meta_back_conn_dup(
+       void *c1,
+       void *c2
+       )
+{
+       struct metaconn *lc1 = ( struct metaconn * )c1;
+       struct metaconn *lc2 = ( struct metaconn * )c2;
+
+       return( ( lc1->conn == lc2->conn ) ? -1 : 0 );
+}
+
+/*
+ * Debug stuff (got it from libavl)
+ */
+#if PRINT_CONNTREE > 0
+static void
+ravl_print( Avlnode *root, int depth )
+{
+       int     i;
+       
+       if ( root == 0 ) {
+               return;
+       }
+       
+       ravl_print( root->avl_right, depth+1 );
+       
+       for ( i = 0; i < depth; i++ ) {
+               printf( "    " );
+       }
+
+       printf( "c(%d) %d\n", ( ( struct metaconn * )root->avl_data )->conn->c_connid, root->avl_bf );
+       
+       ravl_print( root->avl_left, depth+1 );
+}
+
+static void
+myprint( Avlnode *root )
+{
+       printf( "********\n" );
+       
+       if ( root == 0 ) {
+               printf( "\tNULL\n" );
+       } else {
+               ravl_print( root, 0 );
+       }
+       
+       printf( "********\n" );
+}
+#endif /* PRINT_CONNTREE */
+/*
+ * End of debug stuff
+ */
+
+/*
+ * metaconn_alloc
+ * 
+ * Allocates a connection structure, making room for all the referenced targets
+ */
+static struct metaconn *
+metaconn_alloc( int ntargets )
+{
+       struct metaconn *lc;
+       int i;
+
+       assert( ntargets > 0 );
+
+       lc = ch_calloc( sizeof( struct metaconn ), 1 );
+       if ( lc == NULL ) {
+               return NULL;
+       }
+       
+       /*
+        * make it a null-terminated array ...
+        */
+       lc->conns = ch_calloc( sizeof( struct metasingleconn * ), ntargets+1 );
+       if ( lc->conns == NULL ) {
+               free( lc );
+               return NULL;
+       }
+
+       for ( i = 0; i < ntargets; i++ ) {
+               lc->conns[ i ] =
+                       ch_calloc( sizeof( struct metasingleconn ), 1 );
+               if ( lc->conns[ i ] == NULL ) {
+                       charray_free( ( char ** )lc->conns );
+                       free( lc->conns );
+                       free( lc );
+                       return NULL;
+               }
+       }
+
+       lc->bound_target = -1;
+
+       return lc;
+}
+
+/*
+ * metaconn_free
+ *
+ * clears a metaconn
+ */
+static void
+metaconn_free(
+               struct metaconn *lc
+)
+{
+       if ( !lc ) {
+               return;
+       }
+       
+       if ( lc->conns ) {
+               int i;
+
+               for ( i = 0; lc->conns[ i ] != NULL; ++i ) {
+                       free( lc->conns[ i ] );
+               }
+               charray_free( ( char ** )lc->conns );
+       }
+
+       free( lc );
+}
+
+/*
+ * init_one_conn
+ * 
+ * Initializes one connection
+ */
+static int
+init_one_conn(
+               Connection *conn, 
+               Operation *op, 
+               struct metatarget *lt, 
+               int vers,
+               struct metasingleconn *lsc
+               )
+{
+       int err;
+
+       /*
+        * Already init'ed
+        */
+       if ( lsc->ld != NULL ) {
+               return LDAP_SUCCESS;
+       }
+       
+       /*
+        * Attempts to initialize the connection to the target ds
+        */
+       err = ldap_initialize( &lsc->ld, lt->uri );
+
+       /*
+        * In case of failure, the error is mapped back from client
+        * to server error code
+        */
+       if ( err != LDAP_SUCCESS ) {
+               return ldap_back_map_result( err );
+       }
+       
+       /*
+        * Set LDAP version. This will always succeed: If the client
+        * bound with a particular version, then so can we.
+        */
+       ldap_set_option( lsc->ld, LDAP_OPT_PROTOCOL_VERSION, &vers );
+
+       /*
+        * Sets a cookie for the rewrite session
+        */
+       ( void )rewrite_session_init( lt->rwinfo, conn );
+
+       /*
+        * If the connection dn is not null, an attempt to rewrite it is made
+        */
+       if ( conn->c_cdn != NULL && conn->c_cdn[ 0 ] != '\0' ) {
+               /*
+                * Rewrite the bind dn if needed
+                */
+               lsc->bound_dn = NULL;
+               switch ( rewrite_session( lt->rwinfo, "bindDn",
+                                       conn->c_cdn, conn,
+                                       &lsc->bound_dn ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( lsc->bound_dn == NULL ) {
+                               lsc->bound_dn = ch_strdup( conn->c_cdn );
+                       }
+                       Debug( LDAP_DEBUG_ARGS,
+                                       "rw> bindDn: \"%s\" -> \"%s\"\n%s",
+                                       conn->c_cdn, lsc->bound_dn, "" );
+                       break;
+                       
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op,
+                                       LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       /* continues to the next case */
+                       
+               case REWRITE_REGEXEC_ERR:
+                       return LDAP_OPERATIONS_ERROR;
+               }
+       } else {
+               lsc->bound_dn = NULL;
+       }
+
+       lsc->bound = META_UNBOUND;
+
+       /*
+        * The candidate is activated
+        */
+       lsc->candidate = META_CANDIDATE;
+       return LDAP_SUCCESS;
+}
+
+/*
+ * meta_back_getconn
+ * 
+ * Prepares the connection structure
+ * 
+ * FIXME: This function needs to receive some info on the type of operation
+ * it is invoked by, so that only the correct pool of candidate targets
+ * is initialized in case no connection was available yet.
+ * 
+ * At present a flag that says whether the candidate target must be unique
+ * is passed; eventually an operation agent will be used.
+ */
+struct metaconn *
+meta_back_getconn(
+               struct metainfo *li,
+               Connection *conn,
+               Operation *op,
+               int op_type,
+               const char *ndn,
+               int *candidate
+               )
+{
+       struct metaconn *lc, lc_curr;
+       int vers, cached = -1, i = -1, err = LDAP_SUCCESS;
+       int new_conn = 0;
+
+       /* Searches for a metaconn in the avl tree */
+       lc_curr.conn = conn;
+       ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+       lc = (struct metaconn *)avl_find( li->conntree, 
+               (caddr_t)&lc_curr, meta_back_conn_cmp );
+       ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+
+       /* Looks like we didn't get a bind. Open a new session... */
+       if ( !lc ) {
+               lc = metaconn_alloc( li->ntargets );
+               lc->conn = conn;
+               new_conn = 1;
+       }
+
+       vers = conn->c_protocol;
+
+       /*
+        * looks in cache, if any
+        */
+       if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
+               cached = i = meta_dncache_get_target( &li->cache, ndn );
+       }
+
+       if ( op_type == META_OP_REQUIRE_SINGLE ) {
+
+               /*
+                * tries to get a unique candidate
+                * (takes care of default target 
+                */
+               if ( i < 0 ) {
+                       i = meta_back_select_unique_candidate( li, ndn );
+               }
+
+               /*
+                * if any is found, inits the connection
+                */
+               if ( i < 0 ) {
+                       if ( new_conn ) {
+                               metaconn_free( lc );
+                       }
+
+                       send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT,
+                               NULL, "", NULL, NULL );
+
+                       return NULL;
+               }
+                               
+
+               Debug( LDAP_DEBUG_CACHE,
+       "==>meta_back_getconn: got target %d for ndn=\"%s\" from cache\n%s",
+                               i, ndn, "" );
+                       
+               /*
+                * Clear all other candidates
+                */
+               ( void )meta_clear_unused_candidates( li, lc, i, 0 );
+
+               /*
+                * The target is activated; if needed, it is
+                * also init'd
+                */
+               err = init_one_conn( conn, op, li->targets[ i ],
+                               vers, lc->conns[ i ] );
+               if ( err != LDAP_SUCCESS ) {
+               
+                       /*
+                        * FIXME: in case one target cannot
+                        * be init'd, should the other ones
+                        * be tried?
+                        */
+                       ( void )meta_clear_one_candidate( lc->conns[ i ], 1 );
+                       if ( new_conn ) {
+                               metaconn_free( lc );
+                       }
+
+                       send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,                                      NULL, "internal server error", NULL, NULL );
+
+                       return NULL;
+               }
+
+               if ( candidate ) {
+                       *candidate = i;
+               }
+
+       /*
+        * if no unique candidate ...
+        */
+       } else {
+               int ndnlen = strlen( ndn );     
+               for ( i = 0; i < li->ntargets; i++ ) {
+                       if ( i == cached 
+               || meta_back_is_candidate( li->targets[ i ]->suffix,
+                                       ndn, ndnlen ) ) {
+
+                               /*
+                                * The target is activated; if needed, it is
+                                * also init'd
+                                */
+                               int lerr = init_one_conn( conn, op,
+                                               li->targets[ i ],
+                                               vers, lc->conns[ i ] );
+                               if ( lerr != LDAP_SUCCESS ) {
+                               
+                                       /*
+                                        * FIXME: in case one target cannot
+                                        * be init'd, should the other ones
+                                        * be tried?
+                                        */
+                                       ( void )meta_clear_one_candidate( lc->conns[ i ], 1 );
+                                       err = lerr;
+                                       continue;
+                               }
+                       }
+               }
+       }
+
+       if ( new_conn ) {
+               
+               /*
+                * Inserts the newly created metaconn in the avl tree
+                */
+               ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+               err = avl_insert( &li->conntree, ( caddr_t )lc,
+                               meta_back_conn_cmp, meta_back_conn_dup );
+
+#if PRINT_CONNTREE > 0
+               myprint( li->conntree );
+#endif /* PRINT_CONNTREE */
+               
+               ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+
+               Debug( LDAP_DEBUG_TRACE,
+                       "=>meta_back_getconn: conn %ld inserted\n%s%s",
+                       lc->conn->c_connid, "", "" );
+               
+               /*
+                * Err could be -1 in case a duplicate metaconn is inserted
+                */
+               if ( err != 0 ) {
+                       send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
+                       NULL, "internal server error", NULL, NULL );
+                       metaconn_free( lc );
+                       return NULL;
+               }
+       } else {
+               Debug( LDAP_DEBUG_TRACE,
+                       "=>meta_back_getconn: conn %ld fetched\n%s%s",
+                       lc->conn->c_connid, "", "" );
+       }
+       
+       return lc;
+}
+
diff --git a/servers/slapd/back-meta/data/meta-1.ldif b/servers/slapd/back-meta/data/meta-1.ldif
new file mode 100644 (file)
index 0000000..1f90465
--- /dev/null
@@ -0,0 +1,10 @@
+dn: ou=People, dc=foo, dc=example, dc=com
+objectClass: top
+
+dn: cn=Ando, ou=People, dc=foo, dc=example, dc=com
+objectClass: top
+objectClass: person
+cn: Ando
+sn: Ando
+userPassword: ando
+
diff --git a/servers/slapd/back-meta/data/meta-2.ldif b/servers/slapd/back-meta/data/meta-2.ldif
new file mode 100644 (file)
index 0000000..7c717ee
--- /dev/null
@@ -0,0 +1,10 @@
+dn: ou=People, dc=bar, dc=example, dc=com
+objectClass: top
+
+dn: cn=Ando, ou=People, dc=bar, dc=example, dc=com
+objectClass: top
+objectClass: person
+cn: Ando
+sn: Ando
+userPassword: ando
+
diff --git a/servers/slapd/back-meta/data/meta-3.ldif b/servers/slapd/back-meta/data/meta-3.ldif
new file mode 100644 (file)
index 0000000..1786b24
--- /dev/null
@@ -0,0 +1,11 @@
+dn: ou=Groups, dc=bar, dc=example, dc=com
+objectClass: top
+
+dn: cn=Users, ou=Groups, dc=bar, dc=example, dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: simpleSecurityObject
+cn: Users
+member: cn=Users, ou=Groups, dc=bar, dc=example, dc=com
+member: cn=Ando, ou=People, dc=bar, dc=example, dc=com
+userPassword: users
diff --git a/servers/slapd/back-meta/data/setup.sh b/servers/slapd/back-meta/data/setup.sh
new file mode 100644 (file)
index 0000000..e9a8f3d
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+SRCDIR="../../../.."
+METADBDIR="./meta-db"
+SLAPADD="$SRCDIR/servers/slapd/tools/slapadd -v"
+
+ADDCONF="./slapd-meta-plain.conf"
+#ADDCONF="./slapd-meta-rewrite.conf"
+#ADDCONF="./slapd-ldap-rewrite.conf"
+
+LDAPADDCONF="./slapd-ldap-raw.conf"
+CONF="./slapd.conf"
+LDAPCONF="./slapd-ldap.conf"
+PORT=9876
+#DEBUG=-1
+DEBUG=0
+
+rm -rf $METADBDIR
+rm -f schema ucdata
+ln -s "$SRCDIR/servers/slapd/schema" .
+ln -s "$SRCDIR/libraries/liblunicode" ucdata
+for i in 1 2 3 ; do
+       echo "Feeding directory $i"
+       mkdir -p "$METADBDIR/$i"
+       $SLAPADD -f $ADDCONF -n $i -l meta-$i.ldif
+done
+
+sed "s/@PORT@/$PORT/" $ADDCONF > $CONF
+sed "s/@PORT@/$PORT/" $LDAPADDCONF > $LDAPCONF
+
+echo ""
+echo "After slapd started, try"
+echo ""
+echo "    ldapsearch -x -H ldap://localhost:$PORT -b '' -s base namingContexts"
+echo ""
+echo "and browse the directory using the last base that appears;"
+echo "you may also try to bind as administrator of each subdirectory"
+echo "or as \"cn=Ando, ...\" with password \"ando\": notice what happens"
+echo "to attrs \"sn\" and \"cn\" of some entries based on the ACLs ..."
+echo ""
+
+echo "Starting slapd on port $PORT"
+$SRCDIR/servers/slapd/slapd -f $CONF -h "ldap://localhost:$PORT/" -d $DEBUG
+echo "Waiting 2 secs for everything to shut down ..."
+sleep 2
+
+#exit
+
+rm -rf $METADBDIR
+rm -f schema ucdata $CONF $LDAPCONF
+
diff --git a/servers/slapd/back-meta/data/slapd-ldap-raw.conf b/servers/slapd/back-meta/data/slapd-ldap-raw.conf
new file mode 100644 (file)
index 0000000..d83deca
--- /dev/null
@@ -0,0 +1,15 @@
+#######################################################################
+# ldap database with suffix massage definitions
+#######################################################################
+
+database       ldap
+uri            "ldap://localhost:@PORT@/"
+suffix         "o=FB, c=US"
+suffixmassage  "o=FB, c=US" "ou=Groups, dc=bar, dc=example, dc=com"
+lastmod                off
+
+access to dn.regex="[^,]+,o=FB,c=US" attr=cn
+       by group.exact="cn=Users,o=FB,c=US" read
+       by group.exact="cn=Users,ou=Groups,dc=bar,dc=example,dc=com" read
+       by * none
+
diff --git a/servers/slapd/back-meta/data/slapd-ldap-rewrite.conf b/servers/slapd/back-meta/data/slapd-ldap-rewrite.conf
new file mode 100644 (file)
index 0000000..23b779a
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# master slapd config -- for testing of ldap metadirectory rewrite
+#
+ucdata-path    ./ucdata
+include                ./schema/core.schema
+include                ./schema/cosine.schema
+include                ./schema/inetorgperson.schema
+#
+schemacheck    off
+#
+pidfile                ./meta-db/slapd.pid
+argsfile       ./meta-db/slapd.args
+
+access to attr=userPassword 
+       by anonymous auth
+       by self write
+
+access to dn.regex="[^,]+,ou=People,dc=[^,]+,o=Foo Bar,c=US" attr=sn
+       by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read
+       by * none
+
+access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn
+       by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read
+       by * none
+
+#access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn
+#      by dnattr=member read
+#      by * none
+
+access to * 
+       by * read
+
+#######################################################################
+# ldbm database definitions
+#######################################################################
+
+include                ./slapd-ldbm.conf
+
+#######################################################################
+# ldap database with suffix massage definitions
+#######################################################################
+
+include                ./slapd-ldap.conf
+
diff --git a/servers/slapd/back-meta/data/slapd-ldbm.conf b/servers/slapd/back-meta/data/slapd-ldbm.conf
new file mode 100644 (file)
index 0000000..8ca3145
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# slapd config -- for testing of ldap metadirectory
+#
+
+#######################################################################
+# ldbm database definitions
+#######################################################################
+
+database       ldbm
+suffix         "ou=People, dc=foo, dc=example, dc=com"
+rootdn         "cn=Root, ou=People, dc=foo, dc=example, dc=com"
+rootpw         ldap
+directory      ./meta-db/1
+lastmod                on
+index          objectClass     pres,eq
+
+database       ldbm
+suffix         "ou=People, dc=bar, dc=example, dc=com"
+rootdn         "cn=Root, ou=People, dc=bar, dc=example, dc=com"
+rootpw         ldap
+directory      ./meta-db/2
+index          objectClass     pres,eq
+
+database       ldbm
+suffix         "ou=Groups, dc=bar, dc=example, dc=com"
+rootdn         "cn=Root, ou=Groups, dc=bar, dc=example, dc=com"
+rootpw         ldap
+directory      ./meta-db/3
+index          objectClass     pres,eq
+
diff --git a/servers/slapd/back-meta/data/slapd-meta-plain.conf b/servers/slapd/back-meta/data/slapd-meta-plain.conf
new file mode 100644 (file)
index 0000000..06f27c4
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# master slapd config -- for testing of ldap metadirectory
+#
+ucdata-path    ./ucdata
+include                ./schema/core.schema
+include                ./schema/cosine.schema
+include                ./schema/inetorgperson.schema
+#
+schemacheck    off
+#
+pidfile                ./meta-db/slapd.pid
+argsfile       ./meta-db/slapd.args
+
+access to attr=userPassword
+       by anonymous auth
+       by self write
+
+access to * 
+       by * read
+                       
+#######################################################################
+# ldbm database definitions
+#######################################################################
+
+include                ./slapd-ldbm.conf
+
+#######################################################################
+# meta database definitions
+#######################################################################
+
+database       meta
+suffix         "dc=example, dc=com"
+dncache-ttl    forever
+uri            "ldap://localhost:@PORT@/ou=People, dc=foo, dc=example, dc=com"
+uri            "ldap://localhost:@PORT@/ou=People, dc=bar, dc=example, dc=com"
+uri            "ldap://localhost:@PORT@/ou=Groups, dc=bar, dc=example, dc=com"
+
+#######################################################################
+# ldap database with suffix massage definitions
+#######################################################################
+
+include                ./slapd-ldap.conf
+
diff --git a/servers/slapd/back-meta/data/slapd-meta-rewrite.conf b/servers/slapd/back-meta/data/slapd-meta-rewrite.conf
new file mode 100644 (file)
index 0000000..e0ffb2e
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# master slapd config -- for testing of ldap metadirectory rewrite
+#
+ucdata-path    ./ucdata
+include                ./schema/core.schema
+include                ./schema/cosine.schema
+include                ./schema/inetorgperson.schema
+#
+schemacheck    off
+#
+pidfile                ./meta-db/slapd.pid
+argsfile       ./meta-db/slapd.args
+
+access to attr=userPassword 
+       by anonymous auth
+       by self write
+
+access to dn.regex="[^,]+,ou=People,dc=[^,]+,o=Foo Bar,c=US" attr=sn
+       by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read
+       by * none
+
+access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn
+       by group.exact="cn=Users,ou=Groups,dc=bar,o=Foo Bar,c=US" read
+       by * none
+
+#access to dn.regex="[^,]+,ou=Groups,dc=[^,]+,o=Foo Bar,c=US" attr=cn
+#      by dnattr=member read
+#      by * none
+
+access to * 
+       by * read
+
+#######################################################################
+# ldbm database definitions
+#######################################################################
+
+include                ./slapd-ldbm.conf
+
+#######################################################################
+# meta database definitions
+#######################################################################
+
+database       meta
+suffix         "o=Foo Bar, c=US"
+dncache-ttl    forever
+lastmod                off
+
+uri            "ldap://localhost:@PORT@/ou=People, dc=foo, o=Foo Bar, c=US"
+rewriteEngine  on
+rewriteContext default
+rewriteRule    "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com"
+rewriteContext searchResult
+rewriteRule    "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US"
+rewriteContext searchFilter
+rewriteRule    "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3"
+
+uri            "ldap://localhost:@PORT@/ou=People, dc=bar, o=Foo Bar, c=US"
+rewriteEngine  on
+rewriteContext default
+rewriteRule    "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com"
+rewriteContext searchResult
+rewriteRule    "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US"
+rewriteContext searchFilter
+rewriteRule    "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3"
+default-target
+map            attribute givenName sn
+
+uri            "ldap://localhost:@PORT@/ou=Groups, dc=bar, o=Foo Bar, c=US"
+rewriteEngine  on
+rewriteContext default
+rewriteRule    "(.*)o=Foo Bar,[ ]?c=US" "%1dc=example, dc=com"
+rewriteContext searchResult
+rewriteRule    "(.*)dc=example,[ ]?dc=com" "%1o=Foo Bar, c=US"
+rewriteContext searchFilter
+rewriteRule    "(.*)member=([^)]+),o=Foo Bar,[ ]?c=US(.*)" "%1member=%2,dc=example,dc=com%3"
+
+#######################################################################
+# ldap database with suffix massage definitions
+#######################################################################
+
+include                ./slapd-ldap.conf
+
diff --git a/servers/slapd/back-meta/delete.c b/servers/slapd/back-meta/delete.c
new file mode 100644 (file)
index 0000000..1acde0b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_delete(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               const char      *dn,
+               const char      *ndn
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       int candidate = -1;
+
+       char *mdn = NULL;
+
+       lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE,
+                       ndn, &candidate );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       /*
+        * Rewrite the compare dn, if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "deleteDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> deleteDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
+               return( -1 );
+       }
+       
+       ldap_delete_s( lc->conns[ candidate ]->ld, mdn );
+
+       if ( mdn != dn ) {
+               free( mdn );
+       }
+       
+       return meta_back_op_result( lc, op );
+}
+
diff --git a/servers/slapd/back-meta/dncache.c b/servers/slapd/back-meta/dncache.c
new file mode 100644 (file)
index 0000000..febb963
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * The dncache, at present, maps an entry to the target that holds it.
+ */
+
+struct metadncacheentry {
+       char *dn;
+       int target;
+
+       time_t lastupdated;
+};
+
+/*
+ * meta_dncache_cmp
+ *
+ * compares two struct metadncacheentry; used by avl stuff
+ * FIXME: modify avl stuff to delete an entry based on cmp
+ * (e.g. when ttl expired?)
+ */
+int
+meta_dncache_cmp(
+               const void *c1,
+               const void *c2
+)
+{
+       struct metadncacheentry *cc1 = ( struct metadncacheentry * )c1;
+       struct metadncacheentry *cc2 = ( struct metadncacheentry * )c2;
+       
+       /*
+        * case sensitive, because the dn MUST be normalized
+        */
+       return strcmp( cc1->dn, cc2->dn );
+}
+
+/*
+ * meta_dncache_dup
+ *
+ * returns -1 in case a duplicate struct metadncacheentry has been inserted;
+ * used by avl stuff
+ */
+int
+meta_dncache_dup(
+               void *c1,
+               void *c2
+)
+{
+       struct metadncacheentry *cc1 = ( struct metadncacheentry * )c1;
+       struct metadncacheentry *cc2 = ( struct metadncacheentry * )c2;
+       
+       /*
+        * case sensitive, because the dn MUST be normalized
+        */
+       return ( strcmp( cc1->dn, cc2->dn ) == 0 ) ? -1 : 0;
+}
+
+/*
+ * meta_dncache_get_target
+ *
+ * returns the target a dn belongs to, or -1 in case the dn is not
+ * in the cache
+ */
+int
+meta_dncache_get_target(
+               struct metadncache      *cache,
+               const char              *ndn
+)
+{
+       struct metadncacheentry tmp_entry, *entry;
+       time_t curr_time;
+       int target = -1;
+
+       tmp_entry.dn = ( char * )ndn;
+       ldap_pvt_thread_mutex_lock( &cache->mutex );
+       entry = ( struct metadncacheentry * )avl_find( cache->tree,
+                       ( caddr_t )&tmp_entry, meta_dncache_cmp );
+
+       if ( entry != NULL ) {
+               
+               /*
+                * if cache->ttl < 0, cache never expires;
+                * if cache->ttl = 0 no cache is used; shouldn't get here
+                * else, cache is used with ttl
+                */
+               if ( cache->ttl < 0 ) { 
+                       target = entry->target;
+               } else {
+
+                       /*
+                        * Need mutex?
+                        */     
+                       curr_time = time( NULL );
+
+                       if ( entry->lastupdated+cache->ttl > curr_time ) {
+                               target = entry->target;
+                       }
+               }
+       }
+       ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+       return target;
+}
+
+/*
+ * meta_dncache_update_entry
+ *
+ * updates target and lastupdated of a struct metadncacheentry if exists,
+ * otherwise it gets created; returns -1 in case of error
+ */
+int
+meta_dncache_update_entry(
+               struct metadncache      *cache,
+               const char              *ndn,
+               int                     target
+)
+{
+       struct metadncacheentry *entry, tmp_entry;
+       time_t curr_time = 0L;
+       int err = 0;
+
+       /*
+        * if cache->ttl < 0, cache never expires;
+        * if cache->ttl = 0 no cache is used; shouldn't get here
+        * else, cache is used with ttl
+        */
+       if ( cache->ttl > 0 ) {
+
+               /*
+                * Need mutex?
+                */
+               curr_time = time( NULL );
+       }
+
+       tmp_entry.dn = ( char * )ndn;
+
+       ldap_pvt_thread_mutex_lock( &cache->mutex );
+       entry = ( struct metadncacheentry * )avl_find( cache->tree,
+                       ( caddr_t )&tmp_entry, meta_dncache_cmp );
+
+       if ( entry != NULL ) {
+               entry->target = target;
+               entry->lastupdated = curr_time;
+       } else {
+               entry = ch_calloc( sizeof( struct metadncacheentry ), 1 );
+               if ( entry == NULL ) {
+                       ldap_pvt_thread_mutex_unlock( &cache->mutex );
+                       return -1;
+               }
+
+               entry->dn = ch_strdup( ndn );
+               if ( entry->dn == NULL ) {
+                       ldap_pvt_thread_mutex_unlock( &cache->mutex );
+                       return -1;
+               }
+               entry->target = target;
+               entry->lastupdated = curr_time;
+
+               err = avl_insert( &cache->tree, ( caddr_t )entry,
+                               meta_dncache_cmp, meta_dncache_dup );
+       }
+       ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+       return err;
+}
+
+/*
+ * meta_dncache_update_entry
+ *
+ * updates target and lastupdated of a struct metadncacheentry if exists,
+ * otherwise it gets created; returns -1 in case of error
+ */
+int
+meta_dncache_delete_entry(
+               struct metadncache      *cache,
+               const char              *ndn
+)
+{
+       struct metadncacheentry *entry, tmp_entry;
+
+       tmp_entry.dn = ( char * )ndn;
+
+       ldap_pvt_thread_mutex_lock( &cache->mutex );
+       entry = avl_delete( &cache->tree, ( caddr_t )&tmp_entry,
+                       meta_dncache_cmp );
+       ldap_pvt_thread_mutex_lock( &cache->mutex );
+
+       if ( entry != NULL ) {
+               meta_dncache_free( ( void * )entry );
+       }
+
+       return 0;
+}
+
+/*
+ * meta_dncache_free
+ *
+ * frees an entry
+ * 
+ */
+void
+meta_dncache_free(
+               void *e
+)
+{
+       struct metadncacheentry *entry = ( struct metadncacheentry * )e;
+
+       free( entry->dn );
+}
+
diff --git a/servers/slapd/back-meta/external.h b/servers/slapd/back-meta/external.h
new file mode 100644 (file)
index 0000000..814b22b
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#ifndef META_EXTERNAL_H
+#define META_EXTERNAL_H
+
+LDAP_BEGIN_DECL
+
+extern int
+meta_back_initialize LDAP_P((
+               BackendInfo *bi
+));
+extern int
+meta_back_open LDAP_P((
+               BackendInfo *bi
+));
+extern int
+meta_back_close LDAP_P((
+               BackendInfo *bi
+));
+extern int
+meta_back_destroy LDAP_P((
+               BackendInfo *bi
+));
+extern int
+meta_back_db_init LDAP_P((
+               BackendDB *bd
+));
+extern int
+meta_back_db_destroy LDAP_P((
+               BackendDB *bd
+));
+extern int
+meta_back_db_config LDAP_P((
+               BackendDB *bd,
+               const char *fname,
+               int lineno,
+               int argc,
+               char **argv
+));
+extern int
+meta_back_bind LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *dn,
+               const char *ndn,
+               int method,
+               struct berval *cred,
+               char** edn
+));
+extern int
+meta_back_conn_destroy LDAP_P((
+               BackendDB *bd,
+               Connection *conn
+));
+extern int
+meta_back_search LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *base,
+               const char *nbase,
+               int scope,
+               int deref,
+               int sizelimit,
+               int timelimit,
+               Filter *filter,
+               const char *filterstr,
+               char **attrs,
+               int attrsonly
+));
+extern int
+meta_back_compare LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *dn,
+               const char *ndn,
+               AttributeAssertion *ava
+));
+extern int
+meta_back_modify LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *dn,
+               const char *ndn,
+               Modifications *ml
+));
+extern int
+meta_back_modrdn LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *dn,
+               const char *ndn,
+               const char *newrdn,
+               int deleteoldrdn,
+               const char *newSuperior
+));
+extern int
+meta_back_add LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               Entry *e
+));
+extern int
+meta_back_delete LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               const char *dn,
+               const char *ndn
+));
+extern int meta_back_abandon LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               int msgid
+));
+extern int meta_back_group LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               Entry *target,
+               const char* gr_ndn,
+               const char* op_ndn,
+               ObjectClass* group_oc,
+               AttributeDescription*
+               group_at
+));
+extern int
+meta_back_attribute LDAP_P((
+               BackendDB *bd,
+               Connection *conn,
+               Operation *op,
+               Entry *target,
+               const char* e_ndn,
+               AttributeDescription* entry_at,
+               struct berval ***vals
+));
+
+LDAP_END_DECL
+
+#endif /* META_EXTERNAL_H */
+
diff --git a/servers/slapd/back-meta/group.c b/servers/slapd/back-meta/group.c
new file mode 100644 (file)
index 0000000..1fe9d6e
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+
+/* return 0 IFF op_dn is a value in group_at (member) attribute
+ * of entry with gr_dn AND that entry has an objectClass
+ * value of group_oc (groupOfNames)
+ */
+int
+meta_back_group(
+               Backend                 *be,
+               Connection              *conn,
+               Operation               *op,
+               Entry                   *target,
+               const char              *gr_ndn,
+               const char              *op_ndn,
+               ObjectClass             *group_oc,
+               AttributeDescription    *group_at
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;    
+       int rc = 1, candidate;
+       Attribute   *attr;
+       struct berval bv;
+
+       AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass;
+       LDAPMessage     *result;
+       char *gattr[ 2 ];
+       char *filter;
+       LDAP *ld;
+       char *mop_ndn, *mgr_ndn;
+
+       char *group_oc_name = NULL;
+       char *group_at_name = group_at->ad_cname->bv_val;
+
+       if ( group_oc->soc_names && group_oc->soc_names[ 0 ] ) {
+               group_oc_name = group_oc->soc_names[ 0 ];
+       } else {
+               group_oc_name = group_oc->soc_oid;
+       }
+
+       if ( target != NULL && strcmp( target->e_ndn, gr_ndn ) == 0 ) {
+               /* we already have a copy of the entry */
+               /* attribute and objectclass mapping has already been done */
+
+               /*
+                * first we need to check if the objectClass attribute
+                * has been retrieved; otherwise we need to repeat the search
+                */
+               attr = attr_find( target->e_attrs, ad_objectClass );
+               if ( attr != NULL ) {
+
+                       /*
+                        * Now we can check for the group objectClass value
+                        */
+                       if ( !is_entry_objectclass( target, group_oc ) ) {
+                               return 1;
+                       }
+
+                       /*
+                        * This part has been reworked: the group attr compare
+                        * fails only if the attribute is PRESENT but the value
+                        * is NOT PRESENT; if the attribute is NOT PRESENT, the
+                        * search must be repeated as well.
+                        * This may happen if a search for an entry has already
+                        * been performed (target is not null) but the group
+                        * attribute has not been required
+                        */
+                       attr = attr_find( target->e_attrs, group_at );
+                       if ( attr != NULL ) {
+                               bv.bv_val = ( char * )op_ndn;
+                               bv.bv_len = strlen( op_ndn );         
+                               rc = value_find( group_at, attr->a_vals, &bv );
+                               if ( rc != LDAP_SUCCESS ) {
+                                       return 1;
+                               }
+                               return 0;
+                       } /* else: repeat the search */
+               } /* else: repeat the search */
+       } /* else: do the search */
+
+       candidate = meta_back_select_unique_candidate( li, gr_ndn );
+       if ( candidate == -1 ) {
+               return 1;
+       }
+
+       /*
+        * Rewrite the op ndn if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo, "bindDn",
+                               op_ndn, conn, &mop_ndn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mop_ndn == NULL ) {
+                       mop_ndn = ( char * )op_ndn;
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                               "rw> bindDn (op ndn in group):"
+                               " \"%s\" -> \"%s\"\n%s",
+                               op_ndn, mop_ndn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               /* continues to next case */
+               
+       case REWRITE_REGEXEC_ERR:
+               return 1;
+       }
+
+       /*
+        * Rewrite the gr ndn if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "searchBase",
+                               gr_ndn, conn, &mgr_ndn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mgr_ndn == NULL ) {
+                       mgr_ndn = ( char * )gr_ndn;
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                               "rw> searchBase (gr ndn in group):"
+                               " \"%s\" -> \"%s\"\n%s",
+                               gr_ndn, mgr_ndn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               /* continues to next case */
+               
+       case REWRITE_REGEXEC_ERR:
+               return 1;
+       }
+       
+       group_oc_name = ldap_back_map( &li->targets[ candidate ]->oc_map,
+                       group_oc_name, 0 );
+       if ( group_oc_name == NULL ) {
+               return 1;
+       }
+       group_at_name = ldap_back_map( &li->targets[ candidate ]->at_map,
+                       group_at_name, 0 );
+       if ( group_at_name == NULL ) {
+               return 1;
+       }
+
+       filter = ch_malloc( sizeof( "(&(objectclass=)(=))" )
+                       + strlen( group_oc_name )
+                       + strlen( group_at_name )
+                       + strlen( mop_ndn ) + 1 );
+       if ( filter == NULL ) {
+               goto cleanup;
+       }
+
+       rc = ldap_initialize( &ld, li->targets[ candidate ]->uri );
+       if ( rc != LDAP_SUCCESS ) {
+               goto cleanup;
+       }
+
+       rc = ldap_bind_s( ld, li->targets[ candidate ]->binddn,
+                       li->targets[ candidate ]->bindpw, LDAP_AUTH_SIMPLE );
+       if ( rc != LDAP_SUCCESS ) {
+               goto cleanup;
+       }
+
+       strcpy( filter, "(&(objectclass=" );
+       strcat( filter, group_oc_name );
+       strcat( filter, ")(" );
+       strcat( filter, group_at_name );
+       strcat( filter, "=" );
+       strcat( filter, mop_ndn );
+       strcat( filter, "))" );
+
+       gattr[ 0 ] = "objectclass";
+       gattr[ 1 ] = NULL;
+       rc = 1;
+       if ( ldap_search_ext_s( ld, mgr_ndn, LDAP_SCOPE_BASE, filter,
+                               gattr, 0, NULL, NULL, LDAP_NO_LIMIT,
+                               LDAP_NO_LIMIT, &result ) == LDAP_SUCCESS ) {
+               if ( ldap_first_entry( ld, result ) != NULL ) {
+                       rc = 0;
+               }
+               ldap_msgfree( result );
+       }
+
+cleanup:
+       if ( ld != NULL ) {
+               ldap_unbind( ld );
+       }
+       if ( filter != NULL ) {
+               ch_free( filter );
+       }
+       if ( mop_ndn != op_ndn ) {
+               free( mop_ndn );
+       }
+       if ( mgr_ndn != gr_ndn ) {
+               free( mgr_ndn );
+       }
+
+       return rc;
+}
+
diff --git a/servers/slapd/back-meta/init.c b/servers/slapd/back-meta/init.c
new file mode 100644 (file)
index 0000000..4e30347
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+#ifdef SLAPD_META_DYNAMIC
+
+int
+back_meta_LTX_init_module( int argc, char *argv[] ) {
+    BackendInfo bi;
+
+    memset( &bi, '\0', sizeof( bi ) );
+    bi.bi_type = "meta";
+    bi.bi_init = meta_back_initialize;
+
+    backend_add( &bi );
+    return 0;
+}
+
+#endif /* SLAPD_META_DYNAMIC */
+
+int
+meta_back_initialize(
+               BackendInfo     *bi
+)
+{
+       bi->bi_open = 0;
+       bi->bi_config = 0;
+       bi->bi_close = 0;
+       bi->bi_destroy = 0;
+
+       bi->bi_db_init = meta_back_db_init;
+       bi->bi_db_config = meta_back_db_config;
+       bi->bi_db_open = 0;
+       bi->bi_db_close = 0;
+       bi->bi_db_destroy = meta_back_db_destroy;
+
+       bi->bi_op_bind = meta_back_bind;
+       bi->bi_op_unbind = 0;
+       bi->bi_op_search = meta_back_search;
+       bi->bi_op_compare = meta_back_compare;
+       bi->bi_op_modify = meta_back_modify;
+       bi->bi_op_modrdn = meta_back_modrdn;
+       bi->bi_op_add = meta_back_add;
+       bi->bi_op_delete = meta_back_delete;
+       bi->bi_op_abandon = 0;
+
+       bi->bi_extended = 0;
+
+       bi->bi_acl_group = meta_back_group;
+       bi->bi_acl_attribute = meta_back_attribute;
+       bi->bi_chk_referrals = 0;
+
+       bi->bi_connection_init = 0;
+       bi->bi_connection_destroy = meta_back_conn_destroy;
+
+       return 0;
+}
+
+int
+meta_back_db_init(
+               Backend *be
+)
+{
+       struct metainfo *li;
+
+       li = ch_calloc( 1, sizeof( struct metainfo ) );
+       if ( li == NULL ) {
+               return -1;
+       }
+       
+       /*
+        * At present the default is no default target;
+        * this may change
+        */
+       li->defaulttarget = META_DEFAULT_TARGET_NONE;
+
+       ldap_pvt_thread_mutex_init( &li->conn_mutex );
+       ldap_pvt_thread_mutex_init( &li->cache.mutex );
+       be->be_private = li;
+
+       return 0;
+}
+
+static void
+conn_free( 
+       struct metaconn *lc
+)
+{
+       struct metasingleconn **lsc;
+
+       for ( lsc = lc->conns; lsc[ 0 ] != NULL; lsc++ ) {
+               if ( lsc[ 0 ]->ld != NULL ) {
+                       ldap_unbind( lsc[ 0 ]->ld );
+               }
+               if ( lsc[ 0 ]->bound_dn ) {
+                       free( lsc[ 0 ]->bound_dn );
+               }
+               free( lsc[ 0 ] );
+       }
+       free( lc->conns );
+       free( lc );
+}
+
+static void
+target_free(
+               struct metatarget *lt
+)
+{
+       if ( lt->uri ) {
+               free( lt->uri );
+       }
+       if ( lt->binddn ) {
+               free( lt->binddn );
+       }
+       if ( lt->bindpw ) {
+               free( lt->bindpw );
+       }
+       if ( lt->rwinfo ) {
+               rewrite_info_delete( lt->rwinfo );
+       }
+       avl_free( lt->oc_map.remap, NULL );
+       avl_free( lt->oc_map.map, ( AVL_FREE )mapping_free );
+       avl_free( lt->at_map.remap, NULL );
+       avl_free( lt->at_map.map, ( AVL_FREE )mapping_free );
+}
+
+int
+meta_back_db_destroy(
+    Backend    *be
+)
+{
+       struct metainfo *li;
+
+       if ( be->be_private ) {
+               int i;
+
+               li = ( struct metainfo * )be->be_private;
+
+               /*
+                * Destroy the connection tree
+                */
+               ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+
+               if ( li->conntree ) {
+                       avl_free( li->conntree,
+                                       ( AVL_FREE )conn_free );
+               }
+
+               ldap_pvt_thread_mutex_unlock( &li->cache.mutex );
+               ldap_pvt_thread_mutex_destroy( &li->cache.mutex );
+
+               /*
+                * Destroy the per-target stuff (assuming there's at
+                * least one ...)
+                */
+               for ( i = 0; i < li->ntargets; i++ ) {
+                       target_free( li->targets[ i ] );
+                       free( li->targets[ i ] );
+               }
+
+               free( li->targets );
+
+               ldap_pvt_thread_mutex_lock( &li->cache.mutex );
+               if ( li->cache.tree ) {
+                       avl_free( li->cache.tree,
+                                       ( AVL_FREE )meta_dncache_free );
+               }
+               
+               ldap_pvt_thread_mutex_unlock( &li->cache.mutex );
+               ldap_pvt_thread_mutex_destroy( &li->cache.mutex );
+
+                                               
+       }
+
+       free( be->be_private );
+       return 0;
+}
+
diff --git a/servers/slapd/back-meta/modify.c b/servers/slapd/back-meta/modify.c
new file mode 100644 (file)
index 0000000..10cda0b
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_modify(
+               Backend *be,
+               Connection      *conn,
+               Operation       *op,
+               const char      *dn,
+               const char      *ndn,
+               Modifications   *modlist
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       LDAPMod **modv;
+       LDAPMod *mods;
+       Modifications *ml;
+       int candidate = -1, i;
+       char *mdn, *mapped;
+
+       lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE,
+                       ndn, &candidate );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       /*
+        * Rewrite the modify dn, if needed
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "modifyDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> modifyDN: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
+               return( -1 );
+       }
+
+       for ( i = 0, ml = modlist; ml; i++ ,ml = ml->sml_next )
+               ;
+
+       mods = ch_malloc( sizeof( LDAPMod )*i );
+       if ( mods == NULL ) {
+               if ( mdn != dn ) {
+                       free( mdn );
+               }
+               return -1;
+       }
+       modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) );
+       if ( modv == NULL ) {
+               free( mods );
+               if ( mdn != dn ) {
+                       free( mdn );
+               }
+               return -1;
+       }
+
+       for ( i = 0, ml = modlist; ml; ml = ml->sml_next ) {
+               /*
+                * lastmod should always be <off>
+                */
+#if 0
+               if ( !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_creatorsName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_createTimestamp->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifiersName->ad_cname->bv_val )
+                       || !strcasecmp( a->a_desc->ad_cname->bv_val,
+                       slap_schema.si_ad_modifyTimestamp->ad_cname->bv_val ) ) {
+                       continue;
+               }
+#endif
+
+               mapped = ldap_back_map( &li->targets[ candidate ]->at_map,
+                               ml->sml_desc->ad_cname->bv_val, 0 );
+               if ( mapped == NULL ) {
+                       continue;
+               }
+
+               modv[ i ] = &mods[ i ];
+               mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
+               mods[ i ].mod_type = mapped;
+
+               /*
+                * FIXME: dn-valued attrs should be rewritten
+                * to allow their use in ACLs at the back-ldap
+                * level.
+                */
+               if ( strcmp( ml->sml_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       ldap_dnattr_rewrite(
+                               li->targets[ candidate ]->rwinfo,
+                               ml->sml_bvalues, conn );
+               }
+                                       
+               mods[ i ].mod_bvalues = ml->sml_bvalues;
+               i++;
+       }
+       modv[ i ] = 0;
+
+       ldap_modify_s( lc->conns[ candidate ]->ld, mdn, modv );
+
+       if ( mdn != dn ) {
+               free( mdn );
+       }
+       free( mods );
+       free( modv );
+       return meta_back_op_result( lc, op );
+}
+
diff --git a/servers/slapd/back-meta/modrdn.c b/servers/slapd/back-meta/modrdn.c
new file mode 100644 (file)
index 0000000..c4929f2
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_modrdn(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               const char      *dn,
+               const char      *ndn,
+               const char      *newrdn,
+               int             deleteoldrdn,
+               const char      *newSuperior
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       int candidate = -1;
+
+       char *mdn = NULL, *mnewSuperior = NULL;
+
+       lc = meta_back_getconn( li, conn, op, META_OP_REQUIRE_SINGLE,
+                       ndn, &candidate );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       if ( newSuperior ) {
+               int nsCandidate, version = LDAP_VERSION3;
+
+               nsCandidate = meta_back_select_unique_candidate( li,
+                               newSuperior );
+
+               if ( nsCandidate != candidate ) {
+                       /*
+                        * FIXME: one possibility is to delete the entry
+                        * from one target and add it to the other;
+                        * unfortunately we'd need write access to both,
+                        * which is nearly impossible; for administration
+                        * needs, the rootdn of the metadirectory could
+                        * be mapped to an administrative account on each
+                        * target (the binddn?); we'll see.
+                        */
+                       /*
+                        * FIXME: is this the correct return code?
+                        */
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       return -1;
+               }
+
+               ldap_set_option( lc->conns[ nsCandidate ]->ld,
+                               LDAP_OPT_PROTOCOL_VERSION, &version );
+               
+               /*
+                * Rewrite the new superior, if defined and required
+                */
+               switch ( rewrite_session( li->targets[ nsCandidate ]->rwinfo,
+                                       "newSuperiorDn",
+                                       newSuperior, conn, &mnewSuperior ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mnewSuperior == NULL ) {
+                               mnewSuperior = ( char * )newSuperior;
+                       }
+                       Debug( LDAP_DEBUG_ARGS, "rw> newSuperiorDn:"
+                                       " \"%s\" -> \"%s\"\n%s",
+                                       newSuperior, mnewSuperior, "" );
+                       break;
+
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+
+               case REWRITE_REGEXEC_ERR:
+                       return( -1 );
+               }
+       }
+
+       /*
+        * Rewrite the modrdn dn, if required
+        */
+       switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
+                               "modrDn", dn, conn, &mdn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( mdn == NULL ) {
+                       mdn = ( char * )dn;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> modrDn: \"%s\" -> \"%s\"\n%s",
+                               dn, mdn, "" );
+               break;
+               
+       case REWRITE_REGEXEC_UNWILLING:
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                               NULL, "Unwilling to perform", NULL, NULL );
+
+       case REWRITE_REGEXEC_ERR:
+               return( -1 );
+       }
+
+       ldap_rename2_s( lc->conns[ candidate ]->ld, mdn, newrdn,
+                       mnewSuperior, deleteoldrdn );
+
+       if ( mdn != dn ) {
+               free( mdn );
+       }
+       if ( mnewSuperior != NULL && mnewSuperior != newSuperior ) {
+               free( mnewSuperior );
+       }
+       
+       return meta_back_op_result( lc, op );
+}
diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c
new file mode 100644 (file)
index 0000000..5286128
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *                
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+#include "ldap_pvt.h"
+
+static void
+meta_send_entry(
+               Backend         *be,
+               Operation       *op,
+               struct metaconn *lc,
+               int             i,
+               LDAPMessage     *e,
+               char            **attrs,
+               int             attrsonly
+);
+
+static int
+is_one_level_rdn(
+               const char *rdn,
+               int len
+);
+
+int
+meta_back_search(
+               Backend         *be,
+               Connection      *conn,
+               Operation       *op,
+               const char      *base,
+               const char      *nbase,
+               int             scope,
+               int             deref,
+               int             size,
+               int             time,
+               Filter          *filter,
+               const char      *filterstr,
+               char            **attrs,
+               int             attrsonly
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc;
+       struct metasingleconn **lsc;
+       struct timeval  tv;
+       LDAPMessage     *res, *e;
+       int     count, rc = 0, *msgid, sres = LDAP_NO_SUCH_OBJECT;
+       char *match = NULL, *err = NULL;
+       char *mbase = NULL, *mfilter = NULL, *mmatch = NULL, 
+               *mapped_filter = NULL, **mapped_attrs = NULL;
+               
+       int i, last = 0, candidates = 0, nbaselen, op_type;
+
+       if ( scope == LDAP_SCOPE_BASE ) {
+               op_type = META_OP_REQUIRE_SINGLE;
+       } else {
+               op_type = META_OP_ALLOW_MULTIPLE;
+       }
+       
+       lc = meta_back_getconn( li, conn, op, op_type, nbase, NULL );
+       if ( !lc || !meta_back_dobind( lc, op ) ) {
+               return -1;
+       }
+
+       /*
+        * Array of message id of each target
+        */
+       msgid = ch_calloc( sizeof( int ), li->ntargets );
+       if ( msgid == NULL ) {
+               return -1;
+       }
+       
+       nbaselen = strlen( nbase );
+
+       /*
+        * Inits searches
+        */
+       for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; ++i, ++lsc ) {
+               char *realbase = ( char * )base;
+               int realscope = scope;
+               int suffixlen;
+               
+               if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
+                       continue;
+               }
+
+               if ( deref != -1 ) {
+                       ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_DEREF,
+                                       ( void * )&deref);
+               }
+               if ( time != -1 ) {
+                       ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_TIMELIMIT,
+                                       ( void * )&time);
+               }
+               if ( size != -1 ) {
+                       ldap_set_option( lsc[ 0 ]->ld, LDAP_OPT_SIZELIMIT,
+                                       ( void * )&size);
+               }
+
+               /*
+                * modifies the base according to the scope, if required
+                */
+               suffixlen = strlen( li->targets[ i ]->suffix );
+               if ( suffixlen > nbaselen ) {
+                       switch ( scope ) {
+                       case LDAP_SCOPE_SUBTREE:
+                               /*
+                                * make the target suffix the new base
+                                */
+                               realbase = li->targets[ i ]->suffix;
+                               break;
+
+                       case LDAP_SCOPE_ONELEVEL:
+                               if ( is_one_level_rdn( li->targets[ i ]->suffix,
+                                               suffixlen-nbaselen-1) ) {
+                                       /*
+                                        * if there is exactly one level,
+                                        * make the target suffix the new
+                                        * base, and make scope "base"
+                                        */
+                                       realbase = li->targets[ i ]->suffix;
+                                       realscope = LDAP_SCOPE_BASE;
+                                       break;
+                               } /* else continue with the next case */
+
+                       case LDAP_SCOPE_BASE:
+                               /*
+                                * this target is no longer candidate
+                                */
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               continue;
+                               // rc = meta_back_op_result(lc, op);
+                               // goto finish;
+                       }
+
+               }
+
+               /*
+                * Rewrite the search base, if required
+                */
+               switch ( rewrite_session( li->targets[ i ]->rwinfo,
+                                       "searchBase",
+                                       realbase, conn, &mbase ) ) {
+               case REWRITE_REGEXEC_OK:
+               if ( mbase == NULL ) {
+                       mbase = realbase;
+               }
+               Debug( LDAP_DEBUG_ARGS, "rw> searchBase: \"%s\" -> \"%s\"\n%s",
+                               base, mbase, "" );
+               break;
+               
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       /* continue to the next case */
+
+               case REWRITE_REGEXEC_ERR:
+                       rc = -1;
+                       goto finish;
+               }
+       
+               /*
+                * Rewrite the search filter, if required
+                */
+               switch ( rewrite_session( li->targets[ i ]->rwinfo,
+                                       "searchFilter",
+                                       filterstr, conn, &mfilter ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mfilter == NULL || mfilter[ 0 ] == '\0') {
+                               if ( mfilter != NULL ) {
+                                       free( mfilter );
+                               }
+                               mfilter = ( char * )filterstr;
+                       }
+                       Debug( LDAP_DEBUG_ARGS,
+                               "rw> searchFilter: \"%s\" -> \"%s\"\n%s",
+                               filterstr, mfilter, "" );
+                       break;
+               
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       /* continue to the next case */
+
+               case REWRITE_REGEXEC_ERR:
+                       rc = -1;
+                       goto finish;
+               }
+
+               /*
+                * Maps attributes in filter
+                */
+               mapped_filter = ldap_back_map_filter( &li->targets[ i ]->at_map,
+                               &li->targets[ i ]->oc_map,
+                               ( char * )mfilter, 0 );
+               if ( mapped_filter == NULL ) {
+                       mapped_filter = ( char * )mfilter;
+               }
+       
+               /*
+                * Maps required attributes
+                */
+               mapped_attrs = ldap_back_map_attrs( &li->targets[ i ]->at_map,
+                               attrs, 0 );
+               if ( mapped_attrs == NULL ) {
+                       mapped_attrs = attrs;
+               }
+
+               /*
+                * Starts the search
+                */
+               msgid[ i ] = ldap_search( lsc[ 0 ]->ld, mbase, realscope,
+                               mapped_filter, mapped_attrs, attrsonly); 
+               if ( msgid[ i ] == -1 ) {
+                       lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                       continue;
+               }
+
+               if ( mapped_attrs != attrs ) {
+                       charray_free( mapped_attrs );
+                       mapped_attrs = NULL;
+               }
+               if ( mapped_filter != mfilter ) {
+                       free( mapped_filter );
+                       mapped_filter = NULL;
+               }
+               if ( mfilter != filterstr ) {
+                       free( mfilter );
+                       mfilter = NULL;
+               }
+               if ( mbase != realbase ) {
+                       free( mbase );
+                       mbase = NULL;
+               }
+
+               ++candidates;
+       }
+
+       /* We pull apart the ber result, stuff it into a slapd entry, and
+        * let send_search_entry stuff it back into ber format. Slow & ugly,
+        * but this is necessary for version matching, and for ACL processing.
+        */
+
+
+       /*
+        * In case there are no candidates, no cycle takes place...
+        */
+       for ( count = 0, rc = 0; candidates > 0; ) {
+               int ab, gotit = 0;
+
+               /* check for abandon */
+               ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
+               ab = op->o_abandon;
+               ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+
+               for ( i = 0, lsc = lc->conns; lsc[ 0 ] != NULL; lsc++, i++ ) {
+                       if ( lsc[ 0 ]->candidate != META_CANDIDATE ) {
+                               continue;
+                       }
+                       
+                       if ( ab ) {
+                               ldap_abandon( lsc[ 0 ]->ld, msgid[ i ] );
+                               rc = 0;
+                               break;
+                       }
+
+                       rc = ldap_result( lsc[ 0 ]->ld, LDAP_RES_ANY,
+                                       0, &tv, &res );
+
+                       if ( rc == 0 ) {
+                               continue;
+                       } else if ( rc == -1 ) {
+                               /* something REALLY bad happened! */
+                               ( void )meta_clear_unused_candidates( li,
+                                               lc, -1, 0 );
+                               send_search_result( conn, op,
+                                               LDAP_OPERATIONS_ERROR,
+                                               "", "", NULL, NULL, count );
+                               
+                               /* anything else needs be done? */
+                               goto finish;
+                       } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
+                               e = ldap_first_entry( lsc[ 0 ]->ld,res );
+                               meta_send_entry(be, op, lc, i, e, attrs,
+                                               attrsonly);
+                               count++;
+                               ldap_msgfree( res );
+                               gotit = 1;
+                       } else {
+                               sres = ldap_result2error( lsc[ 0 ]->ld,
+                                               res, 1 );
+                               sres = ldap_back_map_result( sres );
+                               if ( err != NULL ) {
+                                       free( err );
+                               }
+                               ldap_get_option( lsc[ 0 ]->ld,
+                                               LDAP_OPT_ERROR_STRING, &err );
+                               if ( match != NULL ) {
+                                       free( match );
+                               }
+                               ldap_get_option( lsc[ 0 ]->ld,
+                                               LDAP_OPT_MATCHED_DN, &match );
+
+                               Debug( LDAP_DEBUG_ARGS,
+       "meta_back_search=> [%d] match=\"%s\" err=\"%s\"\n",
+                                       i, match, err );        
+                               
+                               last = i;
+                               rc = 0;
+
+                               /*
+                                * When no candidates are left,
+                                * the outer cycle finishes
+                                */
+                               lsc[ 0 ]->candidate = META_NOT_CANDIDATE;
+                               --candidates;
+                       }
+               }
+
+               if ( ab ) {
+                       goto finish;
+               }
+
+               if ( gotit == 0 ) {
+                       tv.tv_sec = 0;
+                        tv.tv_usec = 100000;
+                        ldap_pvt_thread_yield();
+               } else {
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 0;
+               }
+       }
+
+       if ( rc == -1 ) {
+               /*
+                * FIXME: need a strategy to handle errors
+                */
+               rc = meta_back_op_result( lc, op );
+               goto finish;
+       }
+
+       /*
+        * Rewrite the matched portion of the search base, if required
+        * 
+        * FIXME: only the last one gets caught!
+        */
+       if ( match != NULL ) {
+               switch ( rewrite_session( li->targets[ last ]->rwinfo,
+                                       "matchedDn", match, conn, &mmatch ) ) {
+               case REWRITE_REGEXEC_OK:
+                       if ( mmatch == NULL ) {
+                               mmatch = ( char * )match;
+                       }
+                       Debug( LDAP_DEBUG_ARGS, "rw> matchedDn:"
+                                      " \"%s\" -> \"%s\"\n%s",
+                                      match, mmatch, "" );
+                       break;
+                       
+               case REWRITE_REGEXEC_UNWILLING:
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
+                                       NULL, "Unwilling to perform",
+                                       NULL, NULL );
+                       /* continue to the next case */
+                       
+               case REWRITE_REGEXEC_ERR:
+                       rc = -1;
+                       goto finish;
+               }
+       }
+
+       send_search_result( conn, op, sres,
+               mmatch, err, NULL, NULL, count );
+
+finish:
+       if ( match ) {
+               if ( mmatch != match ) {
+                       free( mmatch );
+               }
+               free(match);
+       }
+       
+       if ( err ) {
+               free( err );
+       }
+       
+       if ( msgid ) {
+               free( msgid );
+       }
+       
+       return rc;
+}
+
+static void
+meta_send_entry(
+               Backend *be,
+               Operation *op,
+               struct metaconn *lc,
+               int target,
+               LDAPMessage *e,
+               char **attrs,
+               int attrsonly
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       char *a, *mapped;
+       Entry ent;
+       BerElement *ber = NULL;
+       Attribute *attr, **attrp;
+       struct berval *dummy = NULL;
+       struct berval *bv;
+       const char *text;
+       char *dn;
+
+       struct metasingleconn *lsc = lc->conns[ target ];
+
+       dn = ldap_get_dn( lsc->ld, e );
+       if ( dn == NULL ) {
+               return;
+       }
+
+       /*
+        * Rewrite the dn of the result, if needed
+        */
+       switch ( rewrite_session( li->targets[ target ]->rwinfo,
+                               "searchResult", dn, lc->conn, &ent.e_dn ) ) {
+       case REWRITE_REGEXEC_OK:
+               if ( ent.e_dn == NULL ) {
+                       ent.e_dn = dn;
+               } else {
+                       Debug( LDAP_DEBUG_ARGS, "rw> searchResult[%d]: \"%s\""
+                                       " -> \"%s\"\n", target, dn, ent.e_dn );
+                       free( dn );
+                       dn = NULL;
+               }
+               break;
+               
+       case REWRITE_REGEXEC_ERR:
+       case REWRITE_REGEXEC_UNWILLING:
+               free( dn );
+               return;
+       }
+
+       ent.e_ndn = ch_strdup( ent.e_dn );
+       ( void )dn_normalize( ent.e_ndn );
+
+       /*
+        * cache dn
+        */
+       if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
+               ( void )meta_dncache_update_entry( &li->cache,
+                                                  ch_strdup( ent.e_ndn ),
+                                                  target );
+       }
+
+       ent.e_id = 0;
+       ent.e_attrs = 0;
+       ent.e_private = 0;
+       attrp = &ent.e_attrs;
+
+       for ( a = ldap_first_attribute( lsc->ld, e, &ber );
+                       a != NULL;
+                       a = ldap_next_attribute( lsc->ld, e, ber ) )
+       {
+               mapped = ldap_back_map( &li->targets[ target ]->at_map, a, 1 );
+               if ( mapped == NULL ) {
+                       continue;
+               }
+               attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
+               if ( attr == NULL ) {
+                       continue;
+               }
+               attr->a_next = 0;
+               attr->a_desc = NULL;
+               if ( slap_str2ad( mapped, &attr->a_desc, &text )
+                               != LDAP_SUCCESS) {
+                       ch_free( attr );
+                       continue;
+               }
+               attr->a_vals = ldap_get_values_len( lsc->ld, e, a );
+               if ( !attr->a_vals ) {
+                       attr->a_vals = &dummy;
+               } else if ( strcasecmp( mapped, "objectClass" ) == 0 ) {
+                       int i, last;
+                       for ( last = 0; attr->a_vals[ last ]; ++last ) ;
+                       for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) {
+                               mapped = ldap_back_map(
+                                               &li->targets[ target]->oc_map,
+                                               bv->bv_val, 1 );
+                               if ( mapped == NULL ) {
+                                       ber_bvfree( attr->a_vals[ i ] );
+                                       attr->a_vals[ i ] = NULL;
+                                       if ( --last < 0 ) {
+                                               break;
+                                       }
+                                       attr->a_vals[ i ] = 
+                                               attr->a_vals[ last ];
+                                       attr->a_vals[ last ] = NULL;
+                                       --i;
+                               } else if ( mapped != bv->bv_val ) {
+                                       ch_free( bv->bv_val );
+                                       bv->bv_val = ch_strdup( mapped );
+                                       bv->bv_len = strlen( mapped );
+                               }
+                       }
+               /*
+                * It is necessary to try to rewrite attributes with
+                * dn syntax because they might be used in ACLs as
+                * members of groups; since ACLs are applied to the
+                * rewritten stuff, no dn-based subecj clause could
+                * be used at the ldap backend side (see
+                * http://www.OpenLDAP.org/faq/data/cache/452.html)
+                * The problem can be overcome by moving the dn-based
+                * ACLs to the target directory server, and letting
+                * everything pass thru the ldap backend.
+                */
+               } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid,
+                                       SLAPD_DN_SYNTAX ) == 0 ) {
+                       int i;
+                       for ( i = 0; ( bv = attr->a_vals[ i ] ); i++ ) {
+                               char *newval;
+
+                               switch ( rewrite_session( li->targets[ target ]->rwinfo,
+                                                       "searchResult", bv->bv_val,
+                                                       lc->conn, &newval )) {
+                               case REWRITE_REGEXEC_OK:
+                                       /* left as is */
+                                       if ( newval == NULL ) {
+                                               break;
+                                       }
+                                       Debug( LDAP_DEBUG_ARGS,
+                               "rw> searchResult on attr=%s: \"%s\" -> \"%s\"\n",
+                                               attr->a_desc->ad_type->sat_cname,
+                                               bv->bv_val, newval );
+
+                                       free( bv->bv_val );
+                                       bv->bv_val = newval;
+                                       bv->bv_len = strlen( newval );
+
+                                       break;
+
+                               case REWRITE_REGEXEC_UNWILLING:
+                                       
+                               case REWRITE_REGEXEC_ERR:
+                                       /*
+                                        * FIXME: better give up,
+                                        * skip the attribute
+                                        * or leave it untouched?
+                                        */
+                                       break;
+                               }
+                       }
+               }
+               *attrp = attr;
+               attrp = &attr->a_next;
+       }
+       send_search_entry( be, lc->conn, op, &ent, attrs, attrsonly, NULL );
+       while ( ent.e_attrs ) {
+               attr = ent.e_attrs;
+               ent.e_attrs = attr->a_next;
+               ad_free( attr->a_desc, 1 );
+               if ( attr->a_vals != &dummy ) {
+                       ber_bvecfree(attr->a_vals);
+               }
+               free( attr );
+       }
+       if ( ber ) {
+               ber_free( ber, 0 );
+       }
+       
+       if ( ent.e_dn ) {
+               free( ent.e_dn );
+       }
+       if ( ent.e_ndn ) {
+               free( ent.e_ndn );
+       }
+}
+
+static int
+is_one_level_rdn(
+               const char *rdn,
+               int len
+)
+{
+       for ( ; len--; ) {
+               if ( LDAP_DNSEPARATOR( rdn[ len ] ) ) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
diff --git a/servers/slapd/back-meta/unbind.c b/servers/slapd/back-meta/unbind.c
new file mode 100644 (file)
index 0000000..71086e5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 1998-2001 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ *
+ * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ * 
+ * This work has been developed to fulfill the requirements
+ * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
+ * to the OpenLDAP Foundation in the hope that it may be useful
+ * to the Open Source community, but WITHOUT ANY WARRANTY.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author and SysNet s.n.c. are not responsible for the consequences
+ *    of use of this software, no matter how awful, even if they arise from 
+ *    flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the documentation.
+ *    SysNet s.n.c. cannot be responsible for the consequences of the
+ *    alterations.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ * This software is based on the backend back-ldap, implemented
+ * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
+ * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
+ * contributors. The contribution of the original software to the present
+ * implementation is acknowledged in this copyright statement.
+ *
+ * A special acknowledgement goes to Howard for the overall architecture
+ * (and for borrowing large pieces of code), and to Mark, who implemented
+ * from scratch the attribute/objectclass mapping.
+ *
+ * The original copyright statement follows.
+ *
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits should appear in the
+ *    documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_conn_destroy(
+               Backend         *be,
+               Connection      *conn
+)
+{
+       struct metainfo *li = ( struct metainfo * )be->be_private;
+       struct metaconn *lc, lc_curr;
+
+       Debug( LDAP_DEBUG_TRACE,
+               "=>meta_back_conn_destroy: fetching conn %ld\n%s%s",
+               conn->c_connid, "", "" );
+       
+       lc_curr.conn = conn;
+       
+       ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+       lc = avl_delete( &li->conntree, ( caddr_t )&lc_curr,
+                       meta_back_conn_cmp );
+       ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+
+       if ( lc ) {
+               int i;
+               
+               Debug( LDAP_DEBUG_TRACE,
+                       "=>meta_back_conn_destroy: destroying conn %ld\n%s%s",
+                       lc->conn->c_connid, "", "" );
+               
+               /*
+                * Cleanup rewrite session
+                */
+               for ( i = 0; i < li->ntargets; ++i ) {
+                       if ( lc->conns[ i ]->ld == NULL ) {
+                               free( lc->conns[ i ] );
+                               continue;
+                       }
+
+                       rewrite_session_delete( li->targets[ i ]->rwinfo, conn );
+                       meta_clear_one_candidate( lc->conns[ i ], 1 );
+                       free( lc->conns[ i ] );
+               }
+
+               free( lc->conns );
+               free( lc );
+       }
+
+       /* no response to unbind */
+
+       return 0;
+}
+
index 19802a61d57b8e52f1a216e960332bf80e289442..1b26ee117f3b41e94f8b01992fc0835398df8a3d 100644 (file)
@@ -30,6 +30,9 @@
 #ifdef SLAPD_LDBM
 #include "back-ldbm/external.h"
 #endif
+#ifdef SLAPD_META
+#include "back-meta/external.h"
+#endif
 #ifdef SLAPD_PASSWD
 #include "back-passwd/external.h"
 #endif
@@ -62,6 +65,9 @@ static BackendInfo binfo[] = {
 #if defined(SLAPD_LDBM) && !defined(SLAPD_LDBM_DYNAMIC)
        {"ldbm",        ldbm_back_initialize},
 #endif
+#if defined(SLAPD_META) && !defined(SLAPD_META_DYNAMIC)
+       {"meta",        meta_back_initialize},
+#endif
 #if defined(SLAPD_PASSWD) && !defined(SLAPD_PASSWD_DYNAMIC)
        {"passwd",      passwd_back_initialize},
 #endif