Hi all,

This is the initial landing of the LDAP support for apr-util.

It has the following purposes:

- Do the job of finding and linking to a suitable LDAP library
- Add an LDAP search and compare cache for extra performance
- Provide an LDAP connection cache so that LDAP connections can be
reused
- Other good things

The code is based on a port of an auth_ldap module by Dave Carrigan
released under the Apache licence.

The purpose of posting this code here is to get feedback from people on
what else needs to be done to this code before it could be accepted for
inclusion into APR-util.

Stuff that should work:

- If the --with-ldap flag is not specified on the ./configure line, the
LDAP components should not be built, and no errors should crop up. This
part of the library defaults to disabled.   

- If the --with-ldap flag is specified, it should successfully find and
link to the OpenLDAP libraries (v1.2.x and v2.0.x), or the Netscape LDAP
libraries, or the new iPlanet LDAP libraries (untested).

If there is anything within the code that is either broken, or
implemented incorrectly, please let me know so this can be fixed.

The include file apr_ldap.h.in goes in the include/ directory. The rest
of the files go
in a new directory called ldap/

Regards,
Graham
-- 
-----------------------------------------
[EMAIL PROTECTED]                "There's a moon
                                        over Bourbon Street
                                                tonight..."
diff -u -r /home/minfrin/src/apache/pristine/apr-util/Makefile.in 
apr-util/Makefile.in
--- /home/minfrin/src/apache/pristine/apr-util/Makefile.in      Sat Jun 16 
12:28:53 2001
+++ apr-util/Makefile.in        Sun Jul 15 21:34:36 2001
@@ -10,13 +10,14 @@
 # bring in rules.mk for standard functionality
 @INCLUDE_RULES@
 
-SUBDIRS = buckets crypto dbm encoding hooks uri xml misc
+SUBDIRS = buckets crypto dbm encoding hooks ldap uri xml misc
 CLEAN_SUBDIRS = . test build
 
 CLEAN_TARGETS = $(TARGET_EXPORTS)
 DISTCLEAN_TARGETS = config.cache config.log config.status libtool \
        include/private/apu_config.h include/private/apu_private.h \
-       include/private/apu_select_dbm.h include/apu.h export_vars.sh
+       include/private/apu_select_dbm.h include/apu.h \
+        include/apr_ldap.h export_vars.sh
 EXTRACLEAN_TARGETS = configure aclocal.m4 include/private/apu_config.h.in
 
 [EMAIL PROTECTED]@
diff -u -r /home/minfrin/src/apache/pristine/apr-util/build/apu-conf.m4 
apr-util/build/apu-conf.m4
--- /home/minfrin/src/apache/pristine/apr-util/build/apu-conf.m4        Thu Jul 
12 17:25:25 2001
+++ apr-util/build/apu-conf.m4  Thu Jul 26 10:20:34 2001
@@ -410,3 +410,87 @@
 APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $expat_libs"
 dnl ### export the Expat includes?
 ])
+
+
+dnl 
+dnl Find a particular LDAP library
+dnl
+AC_DEFUN(APU_FIND_LDAPLIB,[
+  if test ${apr_have_ldap} != "1"; then
+    ldaplib=$1
+    extralib=$2
+    unset ac_cv_lib_${ldaplib}_ldap_init
+    apr_have_ldap="1"
+    AC_CHECK_LIB(${ldaplib}, ldap_init, 
+      [
+#        LIBS="-I${ldaplib} ${extralib} $LIBS"
+        LIBS="-l${ldaplib} ${extralib} $LIBS"
+        APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS -l${ldaplib} ${extralib}"
+        AC_CHECK_LIB(${ldaplib}, ldapssl_install_routines, 
apr_have_ldapssl_install_routines="1", , ${extralib})
+        AC_CHECK_LIB(${ldaplib}, ldap_start_tls_s, 
apr_have_ldap_start_tls_s="1", , ${extralib})
+      ],
+      apr_have_ldap="0", ${extralib})
+  fi
+])
+
+
+dnl
+dnl APU_FIND_LDAP: figure out where LDAP is located
+dnl
+AC_DEFUN(APU_FIND_LDAP,[
+
+echo $ac_n "${nl}checking for ldap support...${nl}"
+
+apr_have_ldap="0"
+apr_have_ldap_h="0"
+apr_have_lber_h="0"
+apr_have_ldapssl_h="0"
+apr_have_ldapssl_install_routines="0"
+apr_have_ldap_start_tls_s="0"
+
+AC_ARG_WITH(ldap-include,  --with-ldap-include=path     path to ldap include 
files with trailing slash)
+AC_ARG_WITH(ldap-lib,  --with-ldap-lib=path     path to ldap lib file)
+AC_ARG_WITH(ldap,  --with-ldap=library   ldap library to use,
+  [
+    if test -n "$with_ldap_include"; then
+      CPPFLAGS="-I$with_ldap_include $CPPFLAGS"
+    fi
+    if test -n "$with_ldap_lib"; then
+      LDFLAGS="-L$with_ldap_lib $LDFLAGS"
+    fi
+
+    LIBLDAP="$withval"
+    if test "$LIBLDAP" = "yes"; then
+dnl The iPlanet C SDK 5.0 is as yet untested... 
+      APU_FIND_LDAPLIB("ldap50", "-lnspr4 -lplc4 -lplds4 -liutil50 -llber50 
-lldif50 -lnss3 -lprldap50 -lssl3 -lssldap50")   # Generic
+      APU_FIND_LDAPLIB("ldapssl41", "-lnspr3 -lplc3 -lplds3")   # Generic
+      APU_FIND_LDAPLIB("ldapssl41", " -lnspr3 -lplc3 -lplds3 -lsocket -lnsl 
-lmt")  # Solaris
+      APU_FIND_LDAPLIB("ldapssl41", " -lnspr3 -lplc3 -lplds3 -lpthread") # 
Linux
+      APU_FIND_LDAPLIB("ldapssl40")
+      APU_FIND_LDAPLIB("ldapssl40", "-lpthread")
+      APU_FIND_LDAPLIB("ldapssl30")
+      APU_FIND_LDAPLIB("ldapssl30", "-lpthread")
+      APU_FIND_LDAPLIB("ldapssl20")
+      APU_FIND_LDAPLIB("ldapssl20", "-lpthread")
+      APU_FIND_LDAPLIB("ldap", "-llber")
+    else
+      AC_CHECK_LIB($LIBLDAP,main,apr_have_ldap="1",apr_have_ldap="0",-llber)
+    fi
+
+    test $apr_have_ldap != "1" && AC_MSG_ERROR(could not find an LDAP library)
+    AC_CHECK_LIB(lber, ber_init, , AC_CHECK_LIB(lber, ber_init), -lnsl)
+
+    AC_CHECK_HEADERS(ldap.h, apr_have_ldap_h="1", apr_have_ldap_h="0")
+    AC_CHECK_HEADERS(lber.h, apr_have_lber_h="1", apr_have_lber_h="0")
+    AC_CHECK_HEADERS(ldap_ssl.h, apr_have_ldapssl_h="1", 
apr_have_ldapssl_h="0")
+
+    AC_SUBST(apr_have_ldap)
+    AC_SUBST(apr_have_ldap_h)
+    AC_SUBST(apr_have_lber_h)
+    AC_SUBST(apr_have_ldapssl_h)
+    AC_SUBST(with_ldap_include)
+    AC_SUBST(apr_have_ldapssl_install_routines)
+    AC_SUBST(apr_have_ldap_start_tls_s)
+  ])
+
+])
diff -u -r /home/minfrin/src/apache/pristine/apr-util/configure.in 
apr-util/configure.in
--- /home/minfrin/src/apache/pristine/apr-util/configure.in     Thu Jul 12 
17:25:20 2001
+++ apr-util/configure.in       Thu Jul 26 09:56:11 2001
@@ -42,6 +42,7 @@
 dnl 2. Determine what DBM backend type to use.
 dnl 3. Find Expat
 dnl
+APU_FIND_LDAP
 APU_FIND_APR
 APU_CHECK_DBM
 APU_FIND_EXPAT
@@ -51,7 +52,7 @@
 dnl
 dnl prep libtool
 dnl
-echo "performing libtool configuration..."
+echo "${nl}performing libtool configuration..."
 
 AC_CANONICAL_SYSTEM
 LDFLAGS=""
@@ -100,6 +101,7 @@
       lib_target=''
 fi
 
+
 AC_SUBST(lt_compile)
 AC_SUBST(link)
 AC_SUBST(so_ext)
@@ -136,11 +138,12 @@
 
 dnl
 dnl everthing is done. 
-MAKEFILES=" Makefile build/Makefile buckets/Makefile crypto/Makefile 
dbm/Makefile dbm/sdbm/Makefile encoding/Makefile hooks/Makefile uri/Makefile 
xml/Makefile misc/Makefile $test_Makefile"
+MAKEFILES=" Makefile build/Makefile buckets/Makefile crypto/Makefile 
dbm/Makefile dbm/sdbm/Makefile encoding/Makefile hooks/Makefile ldap/Makefile 
uri/Makefile xml/Makefile misc/Makefile $test_Makefile"
 AC_OUTPUT([
        export_vars.sh
        build/rules.mk
        include/private/apu_select_dbm.h
+        include/apr_ldap.h
         include/apu.h
         $MAKEFILES
        ])
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * apr_ldap.h is generated from apr_ldap.h.in by configure -- do not edit 
apr_ldap.h
 */

#ifndef APR_LDAP_H
#define APR_LDAP_H

/*
 * This switches LDAP support on or off.
 */
#define APR_HAVE_LDAP_H       @apr_have_ldap_h@
#define APR_HAVE_LBER_H       @apr_have_lber_h@
#define APR_HAVE_LDAPSSL_H    @apr_have_ldapssl_h@
#define APR_HAVE_NETSCAPE_SSL @apr_have_ldapssl_install_routines@
#if !APR_HAVE_NETSCAPE_SSL
#undef APR_HAVE_NETSCAPE_SSL
#endif
#define APR_HAVE_START_TLS    @apr_have_ldap_start_tls_s@
#if !APR_HAVE_START_TLS
#undef APR_HAVE_START_TLS
#endif
#define APR_HAVE_LDAP         @apr_have_ldap@
#if !APR_HAVE_LDAP
#undef APR_HAVE_LDAP
#endif

/* this whole thing disappears if LDAP is not enabled */
#ifdef APR_HAVE_LDAP


/* LDAP header files */
#if APR_HAVE_LDAP_H
#include <@[EMAIL PROTECTED]>
#endif
#if APR_HAVE_LBER_H
#include <@[EMAIL PROTECTED]>
#endif
#if APR_HAVE_LDAPSSL_H
#include <@[EMAIL PROTECTED]>
#endif

/* APR header files */
#include <apr_lock.h>
#include <apr_tables.h>
#include <apr_time.h>


/* Define some errors that are mysteriously gone from OpenLDAP 2.x */
#ifndef LDAP_URL_ERR_NOTLDAP
#define LDAP_URL_ERR_NOTLDAP LDAP_URL_ERR_BADSCHEME
#endif

#ifndef LDAP_URL_ERR_NODN
#define LDAP_URL_ERR_NODN LDAP_URL_ERR_BADURL
#endif


/* XXXXXXXXXXXXXX */
#include <stdarg.h>
#include <sys/types.h>
#include <time.h>











/*
 * Compatibility
 */

#if LDAP_VERSION_MAX <= 2
int ldap_search_ext_s(LDAP *ldap, char *base, int scope, char *filter,
                      char **attrs, int attrsonly, void *servertrls, void 
*clientctrls,
                      void *timeout, int sizelimit, LDAPMessage **res);
void ldap_memfree(void *p);

/* The const_cast is used to get around the fact that some of the LDAPv2 
prototypes
 * have non-const parameters, while the same ones in LDAPv3 are const. If 
compiling
 * with LDAPv2, the const_cast casts away the constness, but won't under LDAPv3
 */
#define const_cast(x) ((char *)(x))
#else
#define const_cast(x) (x)
#endif /* LDAP_VERSION_MAX */



/*
 * Connections
 */

/* 
 * Values in this enum help us keep track of how this connection is
 * currently bound to the server 
 */
typedef enum {
    bind_none,                  /* Not bound */
    bind_system,                        /* Bound using config bind creds */
    bind_user                   /* Bound using user-supplied creds */
} bindings;

/* Values that the deref member can have */
typedef enum {
    never=LDAP_DEREF_NEVER, 
    searching=LDAP_DEREF_SEARCHING, 
    finding=LDAP_DEREF_FINDING, 
    always=LDAP_DEREF_ALWAYS
} deref_options;

/* Structure representing an LDAP connection */
typedef struct apr_ldap_connection_t {
    LDAP *ldap;
    apr_lock_t *lock;
    bindings boundas;

    char *host;                 /* Name of the LDAP server (or space separated 
list) */
    int port;                   /* Port of the LDAP server */
    deref_options deref;                /* how to handle alias dereferening */

    char *binddn;                       /* DN to bind to server (can be NULL) */
    char *bindpw;                       /* Password to bind to server (can be 
NULL) */

#ifdef APR_HAVE_NETSCAPE_SSL
    int netscapessl;            /* True if use SSL connection */
    char *certtdb;              /* Path to CA database */
#endif

#ifdef APR_HAVE_STARTTLS
    int starttls;                 /* True if StartTLS is enabled */
    int withtls;                        /* True if StartTLS on this connection 
*/
#endif

  struct apr_ldap_connection_t *next;
} apr_ldap_connection_t;



/*
 * LDAP Cache Manager
 */

#ifdef WITH_SHARED_LDAP_CACHE
#include <apr_shmem.h>
#endif

typedef struct apr_cache_node_t {
    void *payload;              /* Pointer to the payload */
    time_t add_time;            /* Time node was added to cache */
    struct apr_cache_node_t *next;
} apr_cache_node_t;

typedef struct apr_ald_cache_t {
    unsigned long size;         /* Size of cache array */
    unsigned long maxentries;   /* Maximum number of cache entries */
    unsigned long numentries;   /* Current number of cache entries */
    unsigned long fullmark;     /* Used to keep track of when cache becomes 3/4 
full */
    time_t marktime;            /* Time that the cache became 3/4 full */
    unsigned long (*hash)(void *);  /* Func to hash the payload */
    int (*compare)(void *, void *); /* Func to compare two payloads */
    void * (*copy)(void *);     /* Func to alloc mem and copy payload to new 
mem */
    void (*free)(void *);               /* Func to free mem used by the payload 
*/
    apr_cache_node_t **nodes;

    unsigned long numpurges;    /* No. of times the cache has been purged */
    double avg_purgetime;               /* Average time to purge the cache */
    time_t last_purge;          /* Time of the last purge */
    unsigned long npurged;      /* Number of elements purged in last purge. 
This is not
                                   obvious: it won't be 3/4 the size of the 
cache if 
                                   there were a lot of expired entries. */

    unsigned long fetches;      /* Number of fetches */
    unsigned long hits;         /* Number of cache hits */
    unsigned long inserts;      /* Number of inserts */
    unsigned long removes;      /* Number of removes */
} apr_ald_cache_t;

/* LDAP cache state information */ 
typedef struct apr_ldap_state_t {
    apr_pool_t *pool;           /* pool from which this state is allocated */

    long search_cache_ttl;      /* TTL for search cache */
    long search_cache_size;     /* Size of search cache */
    long compare_cache_ttl;     /* TTL for compare cache */
    long compare_cache_size;    /* Size of compare cache */

    struct apr_ldap_connection_t *connections;
#ifdef WITH_SSL
    int have_certdb;
#endif
} apr_ldap_state_t;

/*#define EXTERN */
#ifdef WITH_SHARED_LDAP_CACHE
EXTERN AP_MM *apr_ldap_mm;
#endif
/*EXTERN apr_ald_cache_t *apr_ldap_cache;*/
apr_ald_cache_t *apr_ldap_cache;

#ifndef WIN32
#define ALD_MM_FILE_MODE ( S_IRUSR|S_IWUSR )
#else
#define ALD_MM_FILE_MODE ( _S_IREAD|_S_IWRITE )
#endif


/*
 * LDAP Cache
 */

/*
 * Maintain a cache of LDAP URLs that the server handles. Each node in
 * the cache contains the search cache for that URL, and a compare cache
 * for the URL. The compare cash is populated when doing require group
 * compares.
 */
typedef struct apr_url_node_t {
    char *url;
    apr_ald_cache_t *search_cache;
    apr_ald_cache_t *compare_cache;
    apr_ald_cache_t *dn_compare_cache;
} apr_url_node_t;

/*
 * We cache every successful search and bind operation, using the username 
 * as the key. Each node in the cache contains the returned DN, plus the 
 * password used to bind.
 */
typedef struct apr_search_node_t {
    char *username;             /* Cache key */
    char *dn;                   /* DN returned from search */
    char *bindpw;               /* The most recently used bind password; 
                                   NULL if the bind failed */
    apr_time_t lastbind;        /* Time of last successful bind */
} apr_search_node_t;

/*
 * We cache every successful compare operation, using the DN, attrib, and
 * value as the key. 
 */
typedef struct apr_compare_node_t {
    char *dn;                   /* DN, attrib and value combine to be the key */
    char *attrib;                       
    char *value;
    apr_time_t lastcompare;
} apr_compare_node_t;

/*
 * We cache every successful compare dn operation, using the dn in the require
 * statement and the dn fetched based on the client-provided username.
 */
typedef struct apr_dn_compare_node_t {
    char *reqdn;                /* The DN in the require dn statement */
    char *dn;                   /* The DN found in the search */
} apr_dn_compare_node_t;















/* apr_ldap.c */

void apr_ldap_close_connection(apr_ldap_connection_t *ldc);
int apr_ldap_open_connection(apr_ldap_connection_t *ldc);
void apr_ldap_find_connection(apr_ldap_connection_t *new, apr_ldap_connection_t 
**list);

/* apr_ldap_cache.c */
unsigned long apr_ldap_url_node_hash(void *n);
int apr_ldap_url_node_compare(void *a, void *b);
void *apr_ldap_url_node_copy(void *c);
void apr_ldap_url_node_free(void *n);
unsigned long apr_ldap_search_node_hash(void *n);
int apr_ldap_search_node_compare(void *a, void *b);
void *apr_ldap_search_node_copy(void *c);
void apr_ldap_search_node_free(void *n);
unsigned long apr_ldap_compare_node_hash(void *n);
int apr_ldap_compare_node_compare(void *a, void *b);
void *apr_ldap_compare_node_copy(void *c);
void apr_ldap_compare_node_free(void *n);
unsigned long apr_ldap_dn_compare_node_hash(void *n);
int apr_ldap_dn_compare_node_compare(void *a, void *b);
void *apr_ldap_dn_compare_node_copy(void *c);
void apr_ldap_dn_compare_node_free(void *n);
void apr_ldap_cache_init(apr_pool_t *p);
int apr_ldap_cache_compare(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                           char *url, const char *dn, const char *attrib, const 
char *value);
int apr_ldap_cache_comparedn(apr_ldap_state_t *st, apr_ldap_connection_t *ldc, 
                             char *url, const char *dn, const char *reqdn, 
                             int compare_dn_on_server);


/* apr_ldap_cache_mgr.c */

void apr_ald_free(void *ptr);
void *apr_ald_alloc(int size);
char *apr_ald_strdup(char *s);
unsigned long apr_ald_hash_string(int nstr, ...);
void apr_ald_cache_purge(apr_ald_cache_t *cache);
apr_url_node_t *apr_ald_create_caches(apr_ldap_state_t *s, char *url);
apr_ald_cache_t *apr_ald_create_cache(unsigned long maxentries,
                                unsigned long (*hashfunc)(void *), 
                                int (*comparefunc)(void *, void *),
                                void * (*copyfunc)(void *),
                                void (*freefunc)(void *));
void apr_ald_destroy_cache(apr_ald_cache_t *cache);
void *apr_ald_cache_fetch(apr_ald_cache_t *cache, void *payload);
void apr_ald_cache_insert(apr_ald_cache_t *cache, void *payload);
void apr_ald_cache_remove(apr_ald_cache_t *cache, void *payload);
void apr_ald_cache_display_stats(apr_pool_t *p, apr_ald_cache_t *cache,
                                 char *name, char **stats);


#endif /* APR_HAVE_LDAP */
#endif /* APR_LDAP_H */
TARGETS = apr_ldap.lo apr_ldap_cache.lo apr_ldap_cache_mgr.lo apr_ldap_compat.lo

# bring in rules.mk for standard functionality
@INCLUDE_RULES@
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * apr_ldap.c: LDAP things
 * 
 * Original code from auth_ldap module for Apache v1.3:
 * Copyright 1998, 1999 Enbridge Pipelines Inc. 
 * Copyright 1999-2001 Dave Carrigan
 */

#include <apr_ldap.h>

#ifdef APR_HAVE_LDAP


/*
 * Closes an LDAP connection by unbinding. Sets the boundas value for the
 * http connection config record and clears the bound dn string in the
 * global connection record. The next time connect_to_server is called, the
 * connection will be recreated.
 *
 * If the log parameter is set, adds a debug entry to the log that the
 * server was down and it's reconnecting.
 *
 * The lock for the LDAPConnection should already be acquired.
 */
void apr_ldap_close_connection(apr_ldap_connection_t *ldc)
{

    if (ldc->ldap) {

        /* freeing connection to LDAP server */
        ldap_unbind_s(ldc->ldap);
        ldc->ldap = NULL;
        ldc->boundas = bind_none;

    }

}


/*
 * Connect to the LDAP server and binds. Does not connect if already
 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
 *
 * Returns LDAP_SUCCESS on success; and an error code on failure
 * XXX FIXME: Make these APR error codes, not LDAP error codes
 *
 * The lock for the LDAP connection should already be acquired.
 */
int apr_ldap_open_connection(apr_ldap_connection_t *ldc)
{
    int result = 0;
    int failures = 0;


start_over:
    if (failures++ > 10) {
        /* too many failures - leave */
        return result;
    }

    if (!ldc->ldap) {
        ldc->boundas = bind_none;

        /* opening connection to LDAP server */
        if ((ldc->ldap = ldap_init(ldc->host, ldc->port)) == NULL) {
            /* couldn't connect */
            /* XXX FIXME: when ldap_init returns NULL, the real error
             * message is a system error in errno. This should be
             * changed when LDAP errors become APR errors to automatically
             * return the relevant APR system error where appropriate
             */
            return -1;
        }

        /* Set the alias dereferencing option */
#if LDAP_VERSION_MAX == 2
        ldc->ldap->ld_deref = ldc->deref;
#else
        result = ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
        if (result != LDAP_SUCCESS) {
            /* setting LDAP dereference option failed */
            /* we ignore this error */
        }
#endif /* LDAP_VERSION_MAX */

#ifdef APR_HAVE_NETSCAPE_SSL
        if (ldc->netscapessl) {
            if (!ldc->certdb) {
                /* secure LDAP requested, but no CA cert defined */
                return -1;
            } else {
                result = ldapssl_install_routines(ldc->ldap);
                if (result != LDAP_SUCCESS) {
                    /* SSL initialisation failed */
                    return result;
                }
                result = ldap_set_option(ldc->ldap, LDAP_OPT_SSL, LDAP_OPT_ON);
                if (result != LDAP_SUCCESS) {
                    /* SSL option failed */
                    return result;
                }
            }
        }
#endif

#ifdef APR_HAVE_STARTTLS
        if (ldc->starttls) {
            int version = LDAP_VERSION3;

            /* Also we have to set the connection to use protocol version 3,
             * since we're using TLS. */
            if (result = ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION,
                                         &version) != LDAP_SUCCESS) {
                /* setting LDAP version failed - ignore error */
            }

            /* 
             * In auth_ldap_find_connection, we compare ldc->withtls to
             * sec->starttls to see if we have a cache match. On the off
             * chance that apache's config processing rotines set starttls to
             * some other true value besides 1, we set it to 1 here to ensure
             * that the comparison succeeds.
             */
            ldc->starttls = 1;

            result = ldap_start_tls_s(ldc->ldap, NULL, NULL);
            ldc->withtls = 1;
            if (result != LDAP_SUCCESS) {
                /* start TLS failed */
                return result;
            }
        } else {
            ldc->withtls = 0;
        }
#endif /* APR_HAVE_STARTTLS */
    }

    /* 
     * At this point the LDAP connection is guaranteed alive. If boundas says
     * that we're bound as system, we can just return.
     */
    if (ldc->boundas == bind_system) {
        return LDAP_SUCCESS;
    }

    /* 
     * Now bind with the username/password provided by the
     * configuration. It will be an anonymous bind if no u/p was
     * provided. 
     */
    if ((result = ldap_simple_bind_s(ldc->ldap, ldc->binddn, ldc->bindpw))
        == LDAP_SERVER_DOWN) {
        /* couldn't connect - try again */
        apr_ldap_close_connection(ldc);
        goto start_over;
    }

    if (result != LDAP_SUCCESS) {
        /* LDAP fatal error occured */
        apr_ldap_close_connection(ldc);
        return result;
    }

    /* note how we are bound */
    ldc->boundas = bind_system;

    return LDAP_SUCCESS;
}


/*
 * Find an existing ldap connection struct that matches the
 * provided ldap connection struct. Add the provided struct to the
 * list if none are found.
 *
 * The host searched for is passed in a ldc structure. This
 * ldc will become the new connection if a connection does
 * not already exist.
 *
 * The lock for the LDAP connection should already be acquired.
 */
void apr_ldap_find_connection(apr_ldap_connection_t *new, apr_ldap_connection_t 
**list)
{
    struct apr_ldap_connection_t *l, *p;        /* To traverse the linked list 
*/

    /* Find if this connection exists in the linked list yet */
    for (l=*list,p=NULL; l; l=l->next) {
        if (l->port == new->port
            && strcmp(l->host, new->host) == 0
#ifdef APR_HAVE_STARTTLS
            && l->withtls == new->starttls
#endif
            )
            break;
        p = l;
    }

    if (l) {

        /* 
         * Existing connection. Now determine if we need to rebind because
         * the last time this connection was used, it bound as a different
         * dn than this time.
         */
        if ((new->binddn && !l->binddn) ||
            (!new->binddn && l->binddn) ||
            (new->binddn && l->binddn && strcmp(new->binddn, l->binddn) != 0))
            l->boundas = bind_none;
        else
            l->boundas = bind_system;
    }
    else {

        /* 
         * Add the new connection entry to the linked list. Note that we
         * don't actually establish an LDAP connection yet; that happens
         * the first time authentication is requested.
         */
        new->ldap = NULL;
        new->next = NULL;
        if (p) {
            p->next = new;
        }
        else {
            *list = new;
        }
        new->boundas = bind_none;

    }

}

/* ------------------------------------------------------------------ */

/*
 * Compares two DNs to see if they're equal. The only way to do this correctly 
is to 
 * search for the dn and then do ldap_get_dn() on the result. This should match 
the 
 * initial dn, since it would have been also retrieved with ldap_get_dn(). This 
is
 * expensive, so if the configuration value compare_dn_on_server is
 * false, just does an ordinary strcmp.
 *
 * The lock for the ldap cache should already be acquired.
 */
int apr_ldap_cache_comparedn(apr_ldap_state_t *st, apr_ldap_connection_t *ldc, 
                             char *url, const char *dn, const char *reqdn, 
                             int compare_dn_on_server)
{
    int result = 0;
////////// FIXME...
    apr_url_node_t *curl; 
    apr_url_node_t curnode;
    apr_dn_compare_node_t *node;
    apr_dn_compare_node_t newnode;
    int failures = 0;
    LDAPMessage *res, *entry;
    char *searchdn;

    /* get cache entry (or create one) */
    curnode.url = url;
    curl = apr_ald_cache_fetch(apr_ldap_cache, &curnode);
    if (curl == NULL) {
        curl = apr_ald_create_caches(st, url);
    }

    /* a simple compare? */
    if (!compare_dn_on_server) {
        if (strcmp(dn, reqdn)) {
            return LDAP_COMPARE_FALSE;
        }
        else {
            return LDAP_COMPARE_TRUE;
        }
    }

    /* no - it's a server side compare */

    /* is it in the compare cache? */
    newnode.reqdn = (char *)reqdn;
    node = apr_ald_cache_fetch(curl->dn_compare_cache, &newnode);
    if (node != NULL) {
        /* If it's in the cache, it's good */
        return LDAP_COMPARE_TRUE;
    }

start_over:
    if (failures++ > 10) {
        /* too many failures */
        return result;
    }

    /* make a server connection */
    if (LDAP_SUCCESS != (result = apr_ldap_open_connection(ldc))) {
        /* connect to server failed */
        return result;
    }

    /* search for reqdn */
    if ((result = ldap_search_ext_s(ldc->ldap, const_cast(reqdn), 
LDAP_SCOPE_BASE, 
                                    "(objectclass=*)", NULL, 1, 
                                    NULL, NULL, NULL, -1, &res)) == 
LDAP_SERVER_DOWN) {
        apr_ldap_close_connection(ldc);
        goto start_over;
    }
    if (result != LDAP_SUCCESS) {
        /* search for reqdn failed - no match */
        return result;
    }

    entry = ldap_first_entry(ldc->ldap, res);
    searchdn = ldap_get_dn(ldc->ldap, entry);

    ldap_msgfree(res);
    if (strcmp(dn, searchdn) != 0) {
        /* compare unsuccessful */
        result = LDAP_COMPARE_FALSE;
    }
    else {
        /* compare successful - add to the compare cache */
        newnode.reqdn = (char *)reqdn;
        newnode.dn = (char *)dn;
        apr_ald_cache_insert(curl->dn_compare_cache, &newnode);
        result = LDAP_COMPARE_TRUE;
    }
    ldap_memfree(searchdn);
    return result;

}

/*
 * Does an generic ldap_compare operation. It accepts a cache that it will use
 * to lookup the compare in the cache. We cache two kinds of compares 
 * (require group compares) and (require user compares). Each compare has a 
different
 * cache node: require group includes the DN; require user does not because the
 * require user cache is owned by the 
 *
 * The lock for the ldap cache should already be acquired.
 */
int apr_ldap_cache_compare(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                           char *url, const char *dn, const char *attrib, const 
char *value)
{
    int result = 0;
    apr_url_node_t *curl; 
    apr_url_node_t curnode;
    apr_compare_node_t *compare_nodep;
    apr_compare_node_t the_compare_node;
    apr_time_t curtime;
    int failures = 0;

    /* get cache entry (or create one) */
    curnode.url = url;
    curl = apr_ald_cache_fetch(apr_ldap_cache, &curnode);
    if (curl == NULL) {
        curl = apr_ald_create_caches(st, url);
    }

    /* make a comparison to the cache */
    curtime = apr_time_now();

    the_compare_node.dn = (char *)dn;
    the_compare_node.attrib = (char *)attrib;
    the_compare_node.value = (char *)value;

    compare_nodep = apr_ald_cache_fetch(curl->compare_cache, &the_compare_node);

    if (compare_nodep != NULL) {
        /* found it... */
        if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
            /* ...but it is too old */
            apr_ald_cache_remove(curl->compare_cache, compare_nodep);
        }
        else {
            /* ...and it is good */
            return LDAP_COMPARE_TRUE;
        }
    }

start_over:
    if (failures++ > 10) {
        /* too many failures */
        return result;
    }
    if (LDAP_SUCCESS != (result = apr_ldap_open_connection(ldc))) {
        /* connect failed */
        return result;
    }

    if ((result = ldap_compare_s(ldc->ldap, const_cast(dn), 
                                 const_cast(attrib), const_cast(value)))
        == LDAP_SERVER_DOWN) { 
        /* connection failed - try again */
        apr_ldap_close_connection(ldc);
        goto start_over;
    }
  
    if (result == LDAP_COMPARE_TRUE) {
        /* compare succeeded; caching result */
        the_compare_node.lastcompare = curtime;
        apr_ald_cache_insert(curl->compare_cache, &the_compare_node);
    }
    return result;
}

#endif /* APR_HAVE_LDAP */
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * apr_ldap_cache.c: LDAP cache things
 * 
 * Original code from auth_ldap module for Apache v1.3:
 * Copyright 1998, 1999 Enbridge Pipelines Inc. 
 * Copyright 1999-2001 Dave Carrigan
 */

#include <apr_ldap.h>

#ifdef APR_HAVE_LDAP




/* ------------------------------------------------------------------ */

unsigned long apr_ldap_url_node_hash(void *n)
{
    apr_url_node_t *node = (apr_url_node_t *)n;
    return apr_ald_hash_string(1, node->url);
}

int apr_ldap_url_node_compare(void *a, void *b)
{
    apr_url_node_t *na = (apr_url_node_t *)a;
    apr_url_node_t *nb = (apr_url_node_t *)b;

    return(strcmp(na->url, nb->url) == 0);
}

void *apr_ldap_url_node_copy(void *c)
{
    apr_url_node_t *n = (apr_url_node_t *)c;
    apr_url_node_t *node = (apr_url_node_t 
*)apr_ald_alloc(sizeof(apr_url_node_t));

    node->url = apr_ald_strdup(n->url);
    node->search_cache = n->search_cache;
    node->compare_cache = n->compare_cache;
    node->dn_compare_cache = n->dn_compare_cache;
    return node;
}

void apr_ldap_url_node_free(void *n)
{
    apr_url_node_t *node = (apr_url_node_t *)n;

    apr_ald_free(node->url);
    apr_ald_destroy_cache(node->search_cache);
    apr_ald_destroy_cache(node->compare_cache);
    apr_ald_destroy_cache(node->dn_compare_cache);
    apr_ald_free(node);
}

/* ------------------------------------------------------------------ */

/* Cache functions for search nodes */
unsigned long apr_ldap_search_node_hash(void *n)
{
    apr_search_node_t *node = (apr_search_node_t *)n;
    return apr_ald_hash_string(1, ((apr_search_node_t *)(node))->username);
}

int apr_ldap_search_node_compare(void *a, void *b)
{
    return(strcmp(((apr_search_node_t *)a)->username,
                  ((apr_search_node_t *)b)->username) == 0);
}

void *apr_ldap_search_node_copy(void *c)
{
    apr_search_node_t *node = (apr_search_node_t *)c;
    apr_search_node_t *newnode = apr_ald_alloc(sizeof(apr_search_node_t));
    newnode->username = apr_ald_strdup(node->username);
    newnode->dn = apr_ald_strdup(node->dn);
    newnode->bindpw = apr_ald_strdup(node->bindpw);
    newnode->lastbind = node->lastbind;
    return (void *)newnode;
}

void apr_ldap_search_node_free(void *n)
{
    apr_search_node_t *node = (apr_search_node_t *)n;
    apr_ald_free(node->username);
    apr_ald_free(node->dn);
    apr_ald_free(node->bindpw);
    apr_ald_free(node);
}

/* ------------------------------------------------------------------ */

unsigned long apr_ldap_compare_node_hash(void *n)
{
    apr_compare_node_t *node = (apr_compare_node_t *)n;
    return apr_ald_hash_string(3, node->dn, node->attrib, node->value);
}

int apr_ldap_compare_node_compare(void *a, void *b)
{
    apr_compare_node_t *na = (apr_compare_node_t *)a;
    apr_compare_node_t *nb = (apr_compare_node_t *)b;
    return (strcmp(na->dn, nb->dn) == 0 &&
            strcmp(na->attrib, nb->attrib) == 0 &&
            strcmp(na->value, nb->value) == 0);
}

void *apr_ldap_compare_node_copy(void *c)
{
    apr_compare_node_t *n = (apr_compare_node_t *)c;
    apr_compare_node_t *node = (apr_compare_node_t 
*)apr_ald_alloc(sizeof(apr_compare_node_t));
    node->dn = apr_ald_strdup(n->dn);
    node->attrib = apr_ald_strdup(n->attrib);
    node->value = apr_ald_strdup(n->value);
    node->lastcompare = n->lastcompare;
    return node;
}

void apr_ldap_compare_node_free(void *n)
{
    apr_compare_node_t *node = (apr_compare_node_t *)n;
    apr_ald_free(node->dn);
    apr_ald_free(node->attrib);
    apr_ald_free(node->value);
    apr_ald_free(node);
}

/* ------------------------------------------------------------------ */

unsigned long apr_ldap_dn_compare_node_hash(void *n)
{
    return apr_ald_hash_string(1, ((apr_dn_compare_node_t *)n)->reqdn);
}

int apr_ldap_dn_compare_node_compare(void *a, void *b)
{
    return (strcmp(((apr_dn_compare_node_t *)a)->reqdn,
                   ((apr_dn_compare_node_t *)b)->reqdn) == 0);
}

void *apr_ldap_dn_compare_node_copy(void *c)
{
    apr_dn_compare_node_t *n = (apr_dn_compare_node_t *)c;
    apr_dn_compare_node_t *node = (apr_dn_compare_node_t 
*)apr_ald_alloc(sizeof(apr_dn_compare_node_t));
    node->reqdn = apr_ald_strdup(n->reqdn);
    node->dn = apr_ald_strdup(n->dn);
    return node;
}

void apr_ldap_dn_compare_node_free(void *n)
{
    apr_dn_compare_node_t *node = (apr_dn_compare_node_t *)n;
    apr_ald_free(node->reqdn);
    apr_ald_free(node->dn);
    apr_ald_free(node);
}


/* ------------------------------------------------------------------ */
apr_status_t apr_ldap_cache_child_kill(void *data);
apr_status_t apr_ldap_cache_module_kill(void *data);

apr_status_t apr_ldap_cache_module_kill(void *data)
{
#ifdef WITH_SHARED_LDAP_CACHE
    if (apr_ldap_mm != NULL) {
        apr_mm_destroy(apr_ldap_mm);
        apr_ldap_mm = NULL;
    }
#endif
    return APR_SUCCESS;
}

apr_status_t apr_ldap_cache_child_kill(void *data)
{
    /* Nothing to do */
    return APR_SUCCESS;
}

void apr_ldap_cache_init(apr_pool_t *p)
{
    apr_pool_cleanup_register(p, NULL, apr_ldap_cache_module_kill, 
apr_ldap_cache_child_kill);

#ifdef WITH_SHARED_LDAP_CACHE
    if (apr_mm_useable()) {
        extern AP_MM *apr_ldap_mm;
        apr_ldap_mm = apr_mm_create(0, "/tmp/ldap_cache");
        if (apr_ldap_mm != NULL) {
            apr_mm_permission(apr_ldap_mm, ALD_MM_FILE_MODE, ap_user_id, -1);
        }
    }
#endif
    apr_ldap_cache = apr_ald_create_cache(50,
                                     apr_ldap_url_node_hash,
                                     apr_ldap_url_node_compare,
                                     apr_ldap_url_node_copy,
                                     apr_ldap_url_node_free);
}


#endif /* APR_HAVE_LDAP */
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * apr_ldap_cache_mgr.c: LDAP cache manager things
 * 
 * Original code from auth_ldap module for Apache v1.3:
 * Copyright 1998, 1999 Enbridge Pipelines Inc. 
 * Copyright 1999-2001 Dave Carrigan
 */

#include <apr_ldap.h>
#include <apr_strings.h>

#ifdef APR_HAVE_LDAP

/* only here until strdup is gone */
#include <string.h>

/* here till malloc is gone */
#include <stdlib.h>

static const int primes[] =
{
  11,
  19,
  37,
  73,
  109,
  163,
  251,
  367,
  557,
  823,
  1237,
  1861,
  2777,
  4177,
  6247,
  9371,
  14057,
  21089,
  31627,
  47431,
  71143,
  106721,
  160073,
  240101,
  360163,
  540217,
  810343,
  1215497,
  1823231,
  2734867,
  4102283,
  6153409,
  9230113,
  13845163,
  0
};

void apr_ald_free(void *ptr)
{
#ifdef WITH_SHARED_LDAP_CACHE
    if (auth_ldap_mm != NULL) {
        apr_mm_free(apr_ldap_mm, ptr);
    } else {
        free(ptr);
    }
#else
    free(ptr);
#endif
}

void *apr_ald_alloc(int size)
{
#ifdef WITH_SHARED_LDAP_CACHE
    if (apr_ldap_mm != NULL) {
        return (void *)apr_mm_malloc(apr_ldap_mm, size);
    } else {
        return (void *)malloc(size);
    }
#else
    return (void *)malloc(size);
#endif
}

char *apr_ald_strdup(char *s)
{
#ifdef WITH_SHARED_LDAP_CACHE
    if (apr_ldap_mm != NULL) {
        return apr_mm_strdup(apr_ldap_mm, s);
    } else {
        return strdup(s);
    }
#else
    return strdup(s);
#endif
}


/*
 * Computes the hash on a set of strings. The first argument is the number
 * of strings to hash, the rest of the args are strings. 
 * Algorithm taken from glibc.
 */
unsigned long apr_ald_hash_string(int nstr, ...)
{
    int i;
    va_list args;
    unsigned long h=0, g;
    char *str, *p;
  
    va_start(args, nstr);
    for (i=0; i < nstr; ++i) {
        str = va_arg(args, char *);
        for (p = str; *p; ++p) {
            h = ( h << 4 ) + *p;
            if ( ( g = h & 0xf0000000 ) ) {
                h = h ^ (g >> 24);
                h = h ^ g;
            }
        }
    }
    va_end(args);

    return h;
}


/*
  Purges a cache that has gotten full. We keep track of the time that we
  added the entry that made the cache 3/4 full, then delete all entries
  that were added before that time. It's pretty simplistic, but time to
  purge is only O(n), which is more important.
*/
void apr_ald_cache_purge(apr_ald_cache_t *cache)
{
    int i;
    apr_cache_node_t *p, *q;
    apr_time_t t;
  
    cache->last_purge = apr_time_now();
    cache->npurged = 0;
    cache->numpurges++;

    for (i=0; i < cache->size; ++i) {
        p = cache->nodes[i];
        while (p != NULL) {
            if (p->add_time < cache->marktime) {
                q = p->next;
                (*cache->free)(p->payload);
                apr_ald_free(p);
                cache->numentries--;
                cache->npurged++;
                p = q;
            }
            else {
                p = p->next;
            }
        }
    }

    t = apr_time_now();
    cache->avg_purgetime = 
         ((t - cache->last_purge) + (cache->avg_purgetime * 
(cache->numpurges-1))) / 
         cache->numpurges;
}


/*
 * create caches
 */
apr_url_node_t *apr_ald_create_caches(apr_ldap_state_t *st, char *url)
{
    apr_url_node_t *curl;

    /* inserting URL into URL cache */

    curl = (apr_url_node_t *)apr_pcalloc(st->pool, sizeof(apr_url_node_t));
    curl->url = url;
    curl->search_cache = apr_ald_create_cache(st->search_cache_size,
                                          apr_ldap_search_node_hash,
                                          apr_ldap_search_node_compare,
                                          apr_ldap_search_node_copy,
                                          apr_ldap_search_node_free);
    curl->compare_cache = apr_ald_create_cache(st->compare_cache_size,
                                           apr_ldap_compare_node_hash,
                                           apr_ldap_compare_node_compare,
                                           apr_ldap_compare_node_copy,
                                           apr_ldap_compare_node_free);
    curl->dn_compare_cache = apr_ald_create_cache(st->compare_cache_size,
                                              apr_ldap_dn_compare_node_hash,
                                              apr_ldap_dn_compare_node_compare,
                                              apr_ldap_dn_compare_node_copy,
                                              apr_ldap_dn_compare_node_free);

    apr_ald_cache_insert(apr_ldap_cache, curl);


    return curl;
}


apr_ald_cache_t *apr_ald_create_cache(unsigned long maxentries,
                                unsigned long (*hashfunc)(void *), 
                                int (*comparefunc)(void *, void *),
                                void * (*copyfunc)(void *),
                                void (*freefunc)(void *))
{
    apr_ald_cache_t *cache;
    int i;

    if (maxentries <= 0)
        return NULL;

    cache = (apr_ald_cache_t *)apr_ald_alloc(sizeof(apr_ald_cache_t));
    if (cache == NULL)
        return NULL;

    cache->maxentries = maxentries;
    cache->numentries = 0;
    cache->size = maxentries / 3;
    if (cache->size < 64) cache->size = 64;
        for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
            cache->size = primes[i]? primes[i] : primes[i-1];

    cache->nodes = (apr_cache_node_t **)apr_ald_alloc(cache->size * 
sizeof(apr_cache_node_t *));
    for (i=0; i < cache->size; ++i)
        cache->nodes[i] = NULL;

    cache->hash = hashfunc;
    cache->compare = comparefunc;
    cache->copy = copyfunc;
    cache->free = freefunc;

    cache->fullmark = cache->maxentries / 4 * 3;
    cache->marktime = 0;
    cache->avg_purgetime = 0.0;
    cache->numpurges = 0;
    cache->last_purge = 0;
    cache->npurged = 0;

    cache->fetches = 0;
    cache->hits = 0;
    cache->inserts = 0;
    cache->removes = 0;

    return cache;
}

void apr_ald_destroy_cache(apr_ald_cache_t *cache)
{
    int i;
    apr_cache_node_t *p, *q;

    if (cache == NULL)
        return;

    for (i = 0; i < cache->size; ++i) {
        p = cache->nodes[i];
        q = NULL;
        while (p != NULL) {
            q = p->next;
           (*cache->free)(p->payload);
           apr_ald_free(p);
           p = q;
        }
    }
    apr_ald_free(cache->nodes);
}

void *apr_ald_cache_fetch(apr_ald_cache_t *cache, void *payload)
{
    int hashval;
    apr_cache_node_t *p;

    if (cache == NULL)
        return NULL;

    cache->fetches++;

    hashval = (*cache->hash)(payload) % cache->size;
    for (p = cache->nodes[hashval]; 
         p && !(*cache->compare)(p->payload, payload);
    p = p->next) ;

    if (p != NULL) {
        cache->hits++;
        return p->payload;
    }
    else {
        return NULL;
    }
}

/*
 * Insert an item into the cache. 
 * *** Does not catch duplicates!!! ***
 */
void apr_ald_cache_insert(apr_ald_cache_t *cache, void *payload)
{
    int hashval;
    apr_cache_node_t *node;

    if (cache == NULL || payload == NULL)
        return;

    cache->inserts++;
    hashval = (*cache->hash)(payload) % cache->size;
    node = (apr_cache_node_t *)apr_ald_alloc(sizeof(apr_cache_node_t));
    node->add_time = apr_time_now();
    node->payload = (*cache->copy)(payload);
    node->next = cache->nodes[hashval];
    cache->nodes[hashval] = node;
    if (++cache->numentries == cache->fullmark) 
        cache->marktime=apr_time_now();
    if (cache->numentries >= cache->maxentries)
        apr_ald_cache_purge(cache);
}

void apr_ald_cache_remove(apr_ald_cache_t *cache, void *payload)
{
    int hashval;
    apr_cache_node_t *p, *q;
  
    if (cache == NULL)
        return;

    cache->removes++;
    hashval = (*cache->hash)(payload) % cache->size;
    for (p = cache->nodes[hashval], q=NULL;
         p && !(*cache->compare)(p->payload, payload);
         p = p->next) {
         q = p;
    }

    /* If p is null, it means that we couldn't find the node, so just return */
    if (p == NULL)
        return;

    if (q == NULL) {
        /* We found the node, and it's the first in the list */
        cache->nodes[hashval] = p->next;
    }
    else {
        /* We found the node and it's not the first in the list */
        q->next = p->next;
    }
    (*cache->free)(p->payload);
    apr_ald_free(p);
    cache->numentries--;
}

void apr_ald_cache_display_stats(apr_pool_t *p, apr_ald_cache_t *cache, char 
*name, char **stats)
{
    int i;
    int totchainlen = 0;
    int nchains = 0;
    double chainlen;
    apr_cache_node_t *n;
    char *buf;

    if (cache == NULL)
        return;

    for (i=0; i < cache->size; ++i) {
        if (cache->nodes[i] != NULL) {
            nchains++;
            for (n = cache->nodes[i]; n != NULL; n = n->next)
                totchainlen++;
        }
    }
    chainlen = nchains? (double)totchainlen / (double)nchains : 0;

    buf = apr_psprintf(p, 
             "<tr valign='top'>"
             "<td nowrap>%s</td>"
             "<td align='right' nowrap>%lu (%.0f%% full)</td>"
             "<td align='right'>%.1f</td>"
             "<td align='right'>%lu/%lu</td>"
             "<td align='right'>%.0f%%</td>"
             "<td align='right'>%lu/%lu</td>",
             name,
             cache->numentries, 
             (double)cache->numentries / (double)cache->maxentries * 100.0,
             chainlen,
             cache->hits,            
             cache->fetches,
             (cache->fetches > 0 ? (double)(cache->hits) / 
(double)(cache->fetches) * 100.0 : 100.0),
             cache->inserts,
             cache->removes);

    if (cache->numpurges) {
        buf = apr_psprintf(p,
                           "%s"
                           "<td align='right'>%lu</td>\n"
                           "<td align='right' nowrap>%s</td>\n", 
                           buf,
                           cache->numpurges,
                           ctime(&cache->last_purge));
    }
    else {
        buf = apr_psprintf(p, 
                           "%s<td colspan='2' align='center'>(none)</td>\n",
                           buf);
    }

    buf = apr_psprintf(p, "%s<td align='right'>%.2g</td>\n</tr>", buf, 
cache->avg_purgetime);

    *stats = buf;
}

#endif /* APR_HAVE_LDAP */
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * apr_ldap_compat.c: LDAP v2/v3 compatibility things
 * 
 * Original code from auth_ldap module for Apache v1.3:
 * Copyright 1998, 1999 Enbridge Pipelines Inc. 
 * Copyright 1999-2001 Dave Carrigan
 */

#include <apr_ldap.h>

#ifdef APR_HAVE_LDAP


/* 
 * Compatibility for LDAPv2 libraries. Differences between LDAPv2 and 
 * LDAPv3, as they affect this module
 * 
 *  LDAPv3 uses ldap_search_ext_s; LDAPv2 uses only basic ldap_search_s
 *
 *  LDAPv3 uses ldap_memfree; LDAPv2 just uses free().
 *
 * In this section, we just implement the LDAPv3 SDK functions that are 
 * missing in LDAPv2. 
 * 
 */
#if LDAP_VERSION_MAX == 2

/*
 * LDAPv2 doesn't support extended search. Since auth_ldap doesn't use
 * it anyway, we just translate the extended search into a normal search.
 */
int
ldap_search_ext_s(LDAP *ldap, char *base, int scope, char *filter,
                  char **attrs, int attrsonly, void *servertrls, void 
*clientctrls,
                  void *timeout, int sizelimit, LDAPMessage **res)
{
  return ldap_search_s(ldap, base, scope, filter, attrs, attrsonly, res);
}

void
ldap_memfree(void *p)
{
  free(p);
}

#endif /* if LDAP_VERSION_MAX */

#endif /* APR_HAVE_LDAP */

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to