Hi all,

Here is another snapshot of the library that incorporates LDAP into
apr-util.

This patch is big, but it's an incorporation of a body of new code
which works as a unit.

The build should work correctly both with the default configuration
(LDAP not included) to the configuration --with-ldap. Please test this
for me to be sure.

The header file apr_ldap.h incorporates some high level access to a
number of LDAP functions, like opening connections, checking userids,
and comparing users. Access is also possible to any of the ldap-*()
routines within the LDAP SDK.

The source code is half a vastly modified version of Dave Carrigan's
auth_ldap module, published under the ASF licence. The other half takes
the form of mod_auth_ldap, which is being posted separately to the
new-httpd list.

Currently the code works for me (Linux/PPC) - would be keen to see what
other people's experiences are. It still needs a lot of work - but at
this point feedback will be valuable.

Comments...?

The files are:

include/apr_ldap.h.in
include/private/apu_ldap_cache.h
ldap/apr_ldap.c
ldap/apr_ldap_cache.c
ldap/apr_ldap_cache_mgr.c
ldap/apr_ldap_compat.c
ldap/Makefile.in

Regards,
Graham
-- 
-----------------------------------------
[EMAIL PROTECTED]               "There's a moon
                                        over Bourbon Street
                                                tonight..."
diff -r -u /home/minfrin/src/apache/pristine/apr-util/Makefile.in 
apr-util/Makefile.in
--- /home/minfrin/src/apache/pristine/apr-util/Makefile.in      Thu Aug  2 
00:48:19 2001
+++ apr-util/Makefile.in        Thu Aug  2 01:10:48 2001
@@ -11,7 +11,7 @@
 # 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
 
 CLEAN_TARGETS = $(TARGET_EXPORTS)
Only in apr-util: Makefile.in.orig
diff -r -u /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 Aug 
 2 00:48:21 2001
+++ apr-util/build/apu-conf.m4  Wed Aug  8 23:00:41 2001
@@ -413,3 +413,78 @@
 APR_ADDTO(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 ${apu_has_ldap} != "define"; then
+    ldaplib=$1
+    extralib=$2
+    unset ac_cv_lib_${ldaplib}_ldap_init
+    AC_CHECK_LIB(${ldaplib}, ldap_init, 
+      [
+dnl        APR_ADDTO(CPPFLAGS,[-DAPU_HAS_LDAP])
+        APR_ADDTO(LIBS,[-l${ldaplib} ${extralib}])
+        APR_ADDTO(APRUTIL_EXPORT_LIBS,[-l${ldaplib} ${extralib}])
+        AC_CHECK_LIB(${ldaplib}, ldapssl_install_routines, 
apu_has_ldap_netscape_ssl="define", , ${extralib})
+        AC_CHECK_LIB(${ldaplib}, ldap_start_tls_s, 
apu_has_ldap_starttls="define", , ${extralib})
+        apu_has_ldap="define";
+      ], , ${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}"
+
+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
+      APR_ADDTO(CPPFLAGS, [-I$with_ldap_include])
+    fi
+    if test -n "$with_ldap_lib"; then
+      APR_ADDTO(LDFLAGS, [-L$with_ldap_lib])
+    fi
+
+    apu_has_ldap="undef";
+    apu_has_ldap_netscape_ssl="undef"
+    apu_has_ldap_starttls="undef"
+
+    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")
+      APU_FIND_LDAPLIB("ldapssl41", "-lnspr3 -lplc3 -lplds3")
+      APU_FIND_LDAPLIB("ldapssl40")
+      APU_FIND_LDAPLIB("ldapssl30")
+      APU_FIND_LDAPLIB("ldapssl20")
+      APU_FIND_LDAPLIB("ldap", "-llber")
+    else
+      APU_FIND_LDAPLIB($LDAPLIB)
+    fi
+
+    test ${apu_has_ldap} != "define" && AC_MSG_ERROR(could not find an LDAP 
library)
+    AC_CHECK_LIB(lber, ber_init)
+
+    AC_CHECK_HEADERS(ldap.h, ldap_h=["#include <ldap.h>"])
+    AC_CHECK_HEADERS(lber.h, lber_h=["#include <lber.h>"])
+    AC_CHECK_HEADERS(ldap_ssl.h, ldap_ssl_h=["#include <ldap_ssl.h>"])
+
+AC_SUBST(apu_has_ldap_netscape_ssl)
+AC_SUBST(apu_has_ldap_starttls)
+AC_SUBST(ldap_h)
+AC_SUBST(lber_h)
+AC_SUBST(ldap_ssl_h)
+AC_SUBST(apu_has_ldap)
+
+  ])
+
+])
diff -r -u /home/minfrin/src/apache/pristine/apr-util/configure.in 
apr-util/configure.in
--- /home/minfrin/src/apache/pristine/apr-util/configure.in     Thu Aug  2 
00:48:20 2001
+++ apr-util/configure.in       Wed Aug  8 22:38:32 2001
@@ -50,6 +50,7 @@
 dnl 3. Find Expat
 dnl
 APU_FIND_APR
+APU_FIND_LDAP
 APU_CHECK_DBM
 APU_FIND_EXPAT
 
@@ -104,10 +105,11 @@
 
 dnl
 dnl everthing is done. 
-MAKEFILES="Makefile buckets/Makefile crypto/Makefile dbm/Makefile 
dbm/sdbm/Makefile encoding/Makefile hooks/Makefile uri/Makefile xml/Makefile 
misc/Makefile $test_Makefile"
+MAKEFILES="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
     include/private/apu_select_dbm.h
+    include/apr_ldap.h
     include/apu.h
     $MAKEFILES
        ])
Only in apr-util/include: apr_ldap.h.in
Only in apr-util/include/private: apu_ldap_cache.h
Only in apr-util: 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.h is generated from apr_ldap.h.in by configure -- do not edit 
apr_ldap.h
 */

#ifndef APU_LDAP_H
#define APU_LDAP_H

/*
 * This switches LDAP support on or off.
 */

/* this will be defined if LDAP support was compiled into apr-util */
[EMAIL PROTECTED]@      APU_HAS_LDAP

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

/* LDAP secure capabilities */
[EMAIL PROTECTED]@      APU_HAS_LDAP_NETSCAPE_SSL
[EMAIL PROTECTED]@      APU_HAS_LDAP_STARTTLS

/* LDAP header files */
@ldap_h@
@lber_h@
@ldap_ssl_h@

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


/*
 * LDAP 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 */

/* 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


/*
 * LDAP Connections
 */

/* 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;                   /* Lock to indicate this connection is 
in use */
    int bound;                          /* Flag to indicate whether this 
connection is bound yet */

    const 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 */

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

    int netscapessl;                    /* True if use Netscape SSL connection 
*/
    const char *certtdb;                        /* Path to Netscape CA database 
*/

    int starttls;                       /* True if StartTLS is enabled */
    int withtls;                        /* True if StartTLS on this connection 
*/

    const char *reason;                 /* Reason for an error failure */

    struct apr_ldap_connection_t *next;
} apr_ldap_connection_t;

/* LDAP cache state information */ 
typedef struct apr_ldap_state_t {
    apr_pool_t *pool;           /* pool from which this state is allocated */
    apr_lock_t *mutex;          /* mutex lock for the connection list */

    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 APU_HAS_LDAP_NETSCAPE_SSL
    int have_certdb;
#endif
} apr_ldap_state_t;


/**
 * Open a connection to an LDAP server
 * @param ldc A structure containing the expanded details of the server
 *            to connect to. The handle to the LDAP connection is returned
 *            as ldc->ldap.
 * @tip This function connects to the LDAP server and binds. It does not
 *      connect if already connected (ldc->ldap != NULL). Does not bind
 *      if already bound.
 * @return If successful LDAP_SUCCESS is returned.
 * @deffunc int apr_ldap_connection_open(apr_ldap_connection_t *ldc)
 */
int apr_ldap_connection_open(apr_ldap_connection_t *ldc);

/**
 * Close a connection to an LDAP server
 * @param ldc A structure containing the expanded details of the server
              that was connected.
 * @tip This function unbinds from the LDAP server, and clears ldc->ldap.
 *      It is possible to rebind to this server again using the same ldc
 *      structure, using apr_ldap_open_connection().
 * @deffunc apr_ldap_close_connection(apr_ldap_connection_t *ldc)
 */
void apr_ldap_connection_close(apr_ldap_connection_t *ldc);

/**
 * Find a connection in a list of connections
 * @param new A structure containing the details of the connection to search
 *            for in the linked list. If the connection is not found in the 
linked
 *            list, it will be added to the linked list.
 * @param st A structure containing the LDAP library state. The connection 
linked
 *           list is stored within this structure. If a new connection is 
created
 *           in the linked list, it is allocated from the pool within st->pool.
 * @tip Once a connection is found and returned, the "in use" flag will be set 
to
 *      lock that particular connection, so that another thread does not try and
 *      use this connection while it is busy. Once you are finished with a 
connection,
 *      apr_ldap_connection_close() must be called to mark this connection as no
 *      longer "in use".
 * @deffunc apr_ldap_connection_t *apr_ldap_connection_find(apr_ldap_state_t 
*st, const char *host, int port,
 *                                                          const char *binddn, 
const char *bindpw, deref_options deref,
 *                                                          int netscapessl, 
int starttls)
 */
apr_ldap_connection_t *apr_ldap_connection_find(apr_ldap_state_t *st, const 
char *host, int port,
                                                const char *binddn, const char 
*bindpw, deref_options deref,
                                                int netscapessl, int starttls);


/**
 * Compare two DNs for sameness
 * @param st A structure containing the LDAP library state.
 * @param ldc The LDAP connection being used.
 * @param url The URL of the LDAP connection - used for deciding which cache to 
use.
 * @param dn The first DN to compare.
 * @param reqdn The DN to compare the first DN to.
 * @param compare_dn_on_server Flag to determine whether the DNs should be 
checked using
 *                             LDAP calls or with a direct string comparision. 
A direct
 *                             string comparison is faster, but not as accurate 
- false
 *                             negative comparisons are possible.
 * @tip Two DNs can be equal and still fail a string comparison. Eg 
"dc=example,dc=com"
 *      and "dc=example, dc=com". Use the compare_dn_on_server unless there are 
serious
 *      performance issues.
 * @deffunc int apr_ldap_cache_comparedn(apr_ldap_state_t *st, 
apr_ldap_connection_t *ldc,
 *                                       const char *url, const char *dn, const 
char *reqdn,
 *                                       int compare_dn_on_server)
 */
int apr_ldap_cache_comparedn(apr_ldap_state_t *st, apr_ldap_connection_t *ldc, 
                             const char *url, const char *dn, const char 
*reqdn, 
                             int compare_dn_on_server);

/**
 * A generic LDAP compare function
 * @param st A structure containing the LDAP library state.
 * @param ldc The LDAP connection being used.
 * @param url The URL of the LDAP connection - used for deciding which cache to 
use.
 * @param dn The DN of the object in which we do the compare.
 * @param attrib The attribute within the object we are comparing for.
 * @param value The value of the attribute we are trying to compare for. 
 * @tip Use this function to determine whether an attribute/value pair exists 
within an
 *      object. Typically this would be used to determine LDAP group membership.
 * @deffunc int apr_ldap_cache_compare(apr_ldap_state_t *st, 
apr_ldap_connection_t *ldc,
 *                                     const char *url, const char *dn, const 
char *attrib, const char *value)
 */
int apr_ldap_cache_compare(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                           const char *url, const char *dn, const char *attrib, 
const char *value);

/**
 * Checks a username/password combination by binding to the LDAP server
 * @param st A structure containing the LDAP library state.
 * @param ldc The LDAP connection being used.
 * @param url The URL of the LDAP connection - used for deciding which cache to 
use.
 * @param basedn The Base DN to search for the user in.
 * @param scope LDAP scope of the search.
 * @param filter The user to search for in the form of an LDAP filter. This 
filter must return
 *               exactly one user for the check to be successful.
 * @param bindpw The user password to bind as.
 * @param binddn The DN of the user will be returned in this variable.
 * @tip The filter supplied will be searched for. If a single entry is 
returned, an attempt
 *      is made to bind as that user. If this bind succeeds, the user is not 
validated.
 * @deffunc int apr_ldap_cache_checkuserid(apr_ldap_state_t *st, 
apr_ldap_connection_t *ldc, char *url, const char *basedn, int scope, char 
*filter, char *bindpw, char **binddn)
 */
int apr_ldap_cache_checkuserid(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                               const char *url, const char *basedn, int scope, 
                               const char *filter, const char *bindpw, const 
char **binddn);

/* from apr_ldap_cache.c */

/**
 * Init the LDAP cache
 * @param p The pool to use to initialise the cache
 * @deffunc void apr_ldap_cache_init(apr_pool_t *p)
 */
void apr_ldap_cache_init(apr_pool_t *p);


#endif /* APU_HAS_LDAP */
#endif /* APU_LDAP_H */

/* ====================================================================
 * 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/>.
 */


#ifndef APU_LDAP_CACHE_H
#define APU_LDAP_CACHE_H

/*
 * This switches LDAP support on or off.
 */

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


/*
 * LDAP Cache Manager
 */

#ifdef APU_HAS_LDAP_SHARED_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;

/*#define EXTERN */
#ifdef APU_HAS_LDAP_SHARED_CACHE
EXTERN AP_MM *apr_ldap_mm;
#endif
/*EXTERN apr_ald_cache_t *apr_ldap_cache;*/
apr_ald_cache_t *apr_ldap_cache;
apr_lock_t *apr_ldap_cache_lock;

#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 {
    const 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 {
    const char *username;               /* Cache key */
    const char *dn;                     /* DN returned from search */
    const 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 {
    const char *dn;                     /* DN, attrib and value combine to be 
the key */
    const char *attrib;                 
    const 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 {
    const char *reqdn;          /* The DN in the require dn statement */
    const char *dn;                     /* The DN found in the search */
} apr_dn_compare_node_t;


/*
 * Function prototypes for LDAP cache
 */

/* 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);


/* apr_ldap_cache_mgr.c */

void apr_ald_free(const void *ptr);
void *apr_ald_alloc(int size);
const char *apr_ald_strdup(const 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, const 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 /* APU_HAS_LDAP */
#endif /* APU_LDAP_CACHE_H */

[EMAIL PROTECTED]@
INCLUDES=-I$(top_builddir)/include -I$(top_builddir)/../apr/include

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>
#include <private/apu_ldap_cache.h>
#include <apr_lock.h>
#include <apr_strings.h>

#ifdef APU_HAS_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 apr_ldap_connection_open() 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.
 *
 */
void apr_ldap_connection_close(apr_ldap_connection_t *ldc)
{

    /*
     * QUESTION:
     *
     * Is it safe leaving bound connections floating around between the
     * different modules? Keeping the user bound is a performance boost,
     * but it is also a potential security problem - maybe.
     *
     * For now we unbind the user when we finish with a connection, but
     * we don't have to...
     */

    /* unbinding from the LDAP server */
    if (ldc->ldap) {
        ldap_unbind_s(ldc->ldap);
        ldc->bound = 0;
    }

    /* mark our connection as available for reuse */
    apr_lock_release(ldc->lock);

}


/*
 * 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
 *
 */
int apr_ldap_connection_open(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->bound = 0;

        /* 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
             */
            ldc->reason = "ldap_init() failed";
            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 APU_HAS_LDAP_NETSCAPE_SSL
        if (ldc->netscapessl) {
            if (!ldc->certdb) {
                /* secure LDAP requested, but no CA cert defined */
                ldc->reason = "secure LDAP requested, but no CA cert defined";
                return -1;
            } else {
                result = ldapssl_install_routines(ldc->ldap);
                if (result != LDAP_SUCCESS) {
                    /* SSL initialisation failed */
                    ldc->reason = "ldapssl_install_routines() failed";
                    return result;
                }
                result = ldap_set_option(ldc->ldap, LDAP_OPT_SSL, LDAP_OPT_ON);
                if (result != LDAP_SUCCESS) {
                    /* SSL option failed */
                    ldc->reason = "ldap_set_option() failed trying to set 
LDAP_OPT_SSL";
                    return result;
                }
            }
        }
#endif /* APU_HAS_LDAP_NETSCAPE_SSL */

#ifdef APU_HAS_LDAP_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 apr_ldap_connection_find, 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);
            if (result != LDAP_SUCCESS) {
                /* start TLS failed */
                ldc->withtls = 0;
                ldc->reason = "ldap_start_tls_s() failed";
                return result;
            }
            ldc->withtls = 1;
        } else {
            ldc->withtls = 0;
        }
#endif /* APU_HAS_LDAP_STARTTLS */
    }

    /* 
     * At this point the LDAP connection is guaranteed alive. If bound says
     * that we're bound already, we can just return.
     */
    if (ldc->bound) {
        ldc->reason = "LDAP connection open successful (already bound)";
        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 */
        ldc->reason = "ldap_simple_bind_s() failed with server down";
        goto start_over;
    }

    if (result != LDAP_SUCCESS) {
        /* LDAP fatal error occured */
        ldc->reason = "ldap_simple_bind_s() failed";
        return result;
    }

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

    ldc->reason = "LDAP connection open successful";
    return LDAP_SUCCESS;
}


/*
 * Find an existing ldap connection struct that matches the
 * provided ldap connection parameters.
 *
 * If not found in the cache, a new ldc structure will be allocated from 
st->pool
 * and returned to the caller. If found in the cache, a pointer to the existing
 * ldc structure will be returned.
 */
apr_ldap_connection_t *apr_ldap_connection_find(apr_ldap_state_t *st, const 
char *host, int port,
                                                const char *binddn, const char 
*bindpw, deref_options deref,
                                                int netscapessl, int starttls)
{
    struct apr_ldap_connection_t *l, *p;        /* To traverse the linked list 
*/

    /* mutex lock this function */
    if (!st->mutex) {
        apr_lock_create(&st->mutex, APR_MUTEX, APR_INTRAPROCESS, NULL, 
st->pool);
    }
    apr_lock_acquire(st->mutex);

    /* Search for an exact connection match in the list that is not
     * being used.
     */
    for (l=st->connections,p=NULL; l; l=l->next) {
        if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
            && l->port == port
            && strcmp(l->host, host) == 0
            && ( (!l->binddn && !binddn) || (l->binddn && binddn && 
!strcmp(l->binddn, binddn)) )
            && ( (!l->bindpw && !bindpw) || (l->bindpw && bindpw && 
!strcmp(l->bindpw, bindpw)) )
            && l->deref == deref
#ifdef APU_HAS_LDAP_NETSCAPE_SSL
            && l->netscapessl == netscapessl
#endif
#ifdef APU_HAS_LDAP_STARTTLS
            && l->withtls == starttls
#endif
            )
            break;
        p = l;
    }

    /* If nothing found, search again, but we don't care about the
     * binddn and bindpw this time.
     */
    if (!l) {
        for (l=st->connections,p=NULL; l; l=l->next) {
            if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
                && l->port == port
                && strcmp(l->host, host) == 0
                && l->deref == deref
#ifdef APU_HAS_LDAP_NETSCAPE_SSL
                && l->netscapessl == netscapessl
#endif
#ifdef APU_HAS_LDAP_STARTTLS
                && l->withtls == starttls
#endif
                ) {
                /* the bind credentials have changed */
                l->bound = 0;
                l->binddn = apr_pstrdup(st->pool, binddn);
                l->bindpw = apr_pstrdup(st->pool, bindpw);
                break;
            }
            p = l;
        }
    }

/* artificially disable cache */
l = NULL;

    /* If no connection what found after the second search, we
     * must create one.
     */
    if (!l) {

        /* 
         * 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.
         */
        /* create the details to the pool in st */
        l = apr_pcalloc(st->pool, sizeof(apr_ldap_connection_t));
        apr_lock_create(&l->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool);
        apr_lock_acquire(l->lock);
        l->bound = 0;
        l->host = apr_pstrdup(st->pool, host);
        l->port = port;
        l->deref = deref;
        l->binddn = apr_pstrdup(st->pool, binddn);
        l->bindpw = apr_pstrdup(st->pool, bindpw);
        l->netscapessl = netscapessl;
        l->starttls = starttls;
        l->withtls = 0;

        if (p) {
            p->next = l;
        }
        else {
            st->connections = l;
        }
    }

    apr_lock_release(st->mutex);
    return l;
}

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

/*
 * 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, 
                             const char *url, const char *dn, const char 
*reqdn, 
                             int compare_dn_on_server)
{
    int result = 0;
    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;

    /* read lock this function */
    if (!apr_ldap_cache_lock) {
        apr_lock_create(&apr_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, 
NULL, st->pool);
    }

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

    /* a simple compare? */
    if (!compare_dn_on_server) {
        /* unlock this read lock */
        if (strcmp(dn, reqdn)) {
            ldc->reason = "DN Comparison FALSE (direct strcmp())";
            return LDAP_COMPARE_FALSE;
        }
        else {
            ldc->reason = "DN Comparison TRUE (direct strcmp())";
            return LDAP_COMPARE_TRUE;
        }
    }

    /* no - it's a server side compare */
    apr_lock_acquire_rw(apr_ldap_cache_lock, APR_READER);

    /* 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 */
        /* unlock this read lock */
        apr_lock_release(apr_ldap_cache_lock);
        ldc->reason = "DN Comparison TRUE (cached)";
        return LDAP_COMPARE_TRUE;
    }

    /* unlock this read lock */
    apr_lock_release(apr_ldap_cache_lock);

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

    /* make a server connection */
    if (LDAP_SUCCESS != (result = apr_ldap_connection_open(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_connection_close(ldc);
        ldc->reason = "DN Comparison ldap_search_ext_s() failed with server 
down";
        goto start_over;
    }
    if (result != LDAP_SUCCESS) {
        /* search for reqdn failed - no match */
        ldc->reason = "DN Comparison ldap_search_ext_s() failed";
        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 */
        ldc->reason = "DN Comparison FALSE (checked on server)";
        result = LDAP_COMPARE_FALSE;
    }
    else {
        /* compare successful - add to the compare cache */
        apr_lock_acquire_rw(apr_ldap_cache_lock, APR_READER);
        newnode.reqdn = (char *)reqdn;
        newnode.dn = (char *)dn;
        apr_ald_cache_insert(curl->dn_compare_cache, &newnode);
        apr_lock_release(apr_ldap_cache_lock);
        ldc->reason = "DN Comparison TRUE (checked on server)";
        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 
 *
 */
int apr_ldap_cache_compare(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                           const 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;

    /* read lock this function */
    if (!apr_ldap_cache_lock) {
        apr_lock_create(&apr_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, 
NULL, st->pool);
    }

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

    /* make a comparison to the cache */
    apr_lock_acquire_rw(apr_ldap_cache_lock, APR_READER);
    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 */
            /* unlock this read lock */
            apr_lock_release(apr_ldap_cache_lock);
            ldc->reason = "Comparison successful (cached)";
            return LDAP_COMPARE_TRUE;
        }
    }
    /* unlock this read lock */
    apr_lock_release(apr_ldap_cache_lock);
    

start_over:
    if (failures++ > 10) {
        /* too many failures */
        return result;
    }
    if (LDAP_SUCCESS != (result = apr_ldap_connection_open(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_connection_close(ldc);
        ldc->reason = "ldap_compare_s() failed with server down";
        goto start_over;
    }
  
    if (result == LDAP_COMPARE_TRUE) {
        /* compare succeeded; caching result */
        apr_lock_acquire_rw(apr_ldap_cache_lock, APR_WRITER);
        the_compare_node.lastcompare = curtime;
        apr_ald_cache_insert(curl->compare_cache, &the_compare_node);
        apr_lock_release(apr_ldap_cache_lock);
    }
    ldc->reason = "Comparison complete";
    return result;
}

int apr_ldap_cache_checkuserid(apr_ldap_state_t *st, apr_ldap_connection_t *ldc,
                               const char *url, const char *basedn, int scope, 
                               const char *filter, const char *bindpw, const 
char **binddn)
{
    int result = 0;
    LDAPMessage *res, *entry;
    char *dn;
    int count;
    int failures = 0;
    apr_url_node_t *curl;               /* Cached URL node */
    apr_url_node_t curnode;
    apr_search_node_t *search_nodep;    /* Cached search node */
    apr_search_node_t the_search_node;
    apr_time_t curtime;


    /* read lock this function */
    if (!apr_ldap_cache_lock) {
        apr_lock_create(&apr_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, 
NULL, st->pool);
    }

    /* Get the cache node for this url */
    apr_lock_acquire_rw(apr_ldap_cache_lock, APR_WRITER);
    curnode.url = url;
    curl = (apr_url_node_t *)apr_ald_cache_fetch(apr_ldap_cache, &curnode);
    if (curl == NULL) {
        curl = apr_ald_create_caches(st, url);
    }
    apr_lock_release(apr_ldap_cache_lock);

    apr_lock_acquire_rw(apr_ldap_cache_lock, APR_READER);
    the_search_node.username = filter;
    search_nodep = apr_ald_cache_fetch(curl->search_cache, &the_search_node);
    if (search_nodep != NULL && search_nodep->bindpw) {

        /* found entry in search cache... */
        curtime = apr_time_now();

        /*
         * Remove this item from the cache if its expired, or if the 
         * sent password doesn't match the storepassword.
         */
        if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
            /* ...but entry is too old */
            apr_ald_cache_remove(curl->search_cache, search_nodep);
        }
        else if (strcmp(search_nodep->bindpw, bindpw) != 0) {
            /* ...but cached password doesn't match sent password */
            apr_ald_cache_remove(curl->search_cache, search_nodep);
        }
        else {
            /* ...and entry is valid */
            *binddn = search_nodep->dn;
            apr_lock_release(apr_ldap_cache_lock);
            ldc->reason = "Authentication successful (cached)";
            return LDAP_SUCCESS;
        }
    }
    /* unlock this read lock */
    apr_lock_release(apr_ldap_cache_lock);


    /*  
     * At this point, there is no valid cached search, so lets do the search.
     */

    /*
     * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns 
here.
     */
start_over:
    if (failures++ > 10) {
        return result;
    }
    if (LDAP_SUCCESS != (result = apr_ldap_connection_open(ldc))) {
        return result;
    }

    /* try do the search */
    if ((result = ldap_search_ext_s(ldc->ldap,
                                    basedn, scope, 
                                    filter, NULL, 1, 
                                    NULL, NULL, NULL, -1, &res)) == 
LDAP_SERVER_DOWN) {
        ldc->reason = "ldap_search_ext_s() for user failed with server down";
        goto start_over;
    }

    /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
    if (result != LDAP_SUCCESS) {
        ldc->reason = "ldap_search_ext_s() for user failed";
        return result;
    }

    /* 
     * We should have found exactly one entry; to find a different
     * number is an error.
     */
    count = ldap_count_entries(ldc->ldap, res);
    if (count != 1) {
        ldap_msgfree(res);
        ldc->reason = "User is not unique (search found two or more matches)";
        return LDAP_NO_SUCH_OBJECT;
    }

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

    /* Grab the dn, copy it into the pool, and free it again */
    dn = ldap_get_dn(ldc->ldap, entry);
    *binddn = apr_pstrdup(st->pool, dn);
    ldap_memfree(dn);

    /* 
     * A bind to the server with an empty password always succeeds, so
     * we check to ensure that the password is not empty. This implies
     * that users who actually do have empty passwords will never be
     * able to authenticate with this module. I don't see this as a big
     * problem.
     */
    if (strlen(bindpw) <= 0) {
        ldap_msgfree(res);
        ldc->reason = "Empty password not allowed";
        return LDAP_INVALID_CREDENTIALS;
    }

    /* 
     * Attempt to bind with the retrieved dn and the password. If the bind
     * fails, it means that the password is wrong (the dn obviously
     * exists, since we just retrieved it)
     */
    if ((result = 
         ldap_simple_bind_s(ldc->ldap, *binddn, bindpw)) == 
         LDAP_SERVER_DOWN) {
        ldc->reason = "ldap_simple_bind_s() to check user credentials failed 
with server down";
        goto start_over;
    }

    /* failure? if so - return */
    if (result != LDAP_SUCCESS) {
        ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
        return result;
    }

    ldap_msgfree(res);

    /*          
     * Add the new username to the search cache.
     */
    apr_lock_acquire_rw(apr_ldap_cache_lock, APR_WRITER);
    the_search_node.username = filter;
    the_search_node.dn = *binddn;
    the_search_node.bindpw = bindpw;
    the_search_node.lastbind = apr_time_now();
    apr_ald_cache_insert(curl->search_cache, &the_search_node);
    apr_lock_release(apr_ldap_cache_lock);

    ldc->reason = "Authentication successful";
    return LDAP_SUCCESS;
}

#endif /* APU_HAS_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>
#include <private/apu_ldap_cache.h>

#ifdef APU_HAS_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 APU_HAS_LDAP_SHARED_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 APU_HAS_LDAP_SHARED_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 /* APU_HAS_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 <private/apu_ldap_cache.h>
#include <apr_strings.h>

#ifdef APU_HAS_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(const void *ptr)
{
#ifdef APU_HAS_LDAP_SHARED_CACHE
    if (auth_ldap_mm != NULL) {
        apr_mm_free(apr_ldap_mm, ptr);
    } else {
        free((void *)ptr);
    }
#else
    free((void *)ptr);
#endif
}

void *apr_ald_alloc(int size)
{
#ifdef APU_HAS_LDAP_SHARED_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
}

const char *apr_ald_strdup(const char *s)
{
#ifdef APU_HAS_LDAP_SHARED_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, const 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) {
        char str_ctime[APR_CTIME_LEN];

        apr_ctime(str_ctime, cache->last_purge);
        buf = apr_psprintf(p,
                           "%s"
                           "<td align='right'>%lu</td>\n"
                           "<td align='right' nowrap>%s</td>\n", 
                           buf,
                           cache->numpurges,
                           str_ctime);
    }
    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 /* APU_HAS_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>
#include <private/apu_ldap_cache.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