1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * util_ldap_cache_mgr.c: LDAP cache manager things
19 *
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
23 */
24
25#include "httpd.h"
26#include "util_ldap.h"
27#include "util_ldap_cache.h"
28#include <apr_strings.h>
29
30APLOG_USE_MODULE(ldap);
31
32#if APR_HAS_LDAP
33
34/* only here until strdup is gone */
35#include <string.h>
36
37/* here till malloc is gone */
38#include <stdlib.h>
39
40static const unsigned long primes[] =
41{
42  11,
43  19,
44  37,
45  73,
46  109,
47  163,
48  251,
49  367,
50  557,
51  823,
52  1237,
53  1861,
54  2777,
55  4177,
56  6247,
57  9371,
58  14057,
59  21089,
60  31627,
61  47431,
62  71143,
63  106721,
64  160073,
65  240101,
66  360163,
67  540217,
68  810343,
69  1215497,
70  1823231,
71  2734867,
72  4102283,
73  6153409,
74  9230113,
75  13845163,
76  0
77};
78
79void util_ald_free(util_ald_cache_t *cache, const void *ptr)
80{
81#if APR_HAS_SHARED_MEMORY
82    if (cache->rmm_addr) {
83        if (ptr)
84            /* Free in shared memory */
85            apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr));
86    }
87    else {
88        if (ptr)
89            /* Cache shm is not used */
90            free((void *)ptr);
91    }
92#else
93    if (ptr)
94        free((void *)ptr);
95#endif
96}
97
98void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size)
99{
100    if (0 == size)
101        return NULL;
102#if APR_HAS_SHARED_MEMORY
103    if (cache->rmm_addr) {
104        /* allocate from shared memory */
105        apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, size);
106        return block ? (void *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
107    }
108    else {
109        /* Cache shm is not used */
110        return (void *)calloc(sizeof(char), size);
111    }
112#else
113    return (void *)calloc(sizeof(char), size);
114#endif
115}
116
117const char *util_ald_strdup(util_ald_cache_t *cache, const char *s)
118{
119#if APR_HAS_SHARED_MEMORY
120    if (cache->rmm_addr) {
121        /* allocate from shared memory */
122        apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, strlen(s)+1);
123        char *buf = block ? (char *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
124        if (buf) {
125            strcpy(buf, s);
126            return buf;
127        }
128        else {
129            return NULL;
130        }
131    }
132    else {
133        /* Cache shm is not used */
134        return strdup(s);
135    }
136#else
137    return strdup(s);
138#endif
139}
140
141/*
142 * Duplicate a subgroupList from one compare entry to another.
143 * Returns: ptr to a new copy of the subgroupList or NULL if allocation failed.
144 */
145util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in)
146{
147    int i = 0;
148    util_compare_subgroup_t *sgl_out = NULL;
149
150    if (!sgl_in) {
151        return NULL;
152    }
153
154    sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t));
155    if (sgl_out) {
156        sgl_out->subgroupDNs = util_ald_alloc(cache, sizeof(char *) * sgl_in->len);
157        if (sgl_out->subgroupDNs) {
158            for (i = 0; i < sgl_in->len; i++) {
159                sgl_out->subgroupDNs[i] = util_ald_strdup(cache, sgl_in->subgroupDNs[i]);
160                if (!sgl_out->subgroupDNs[i]) {
161                    /* We ran out of SHM, delete the strings we allocated for the SGL */
162                    for (i = (i - 1); i >= 0; i--) {
163                            util_ald_free(cache, sgl_out->subgroupDNs[i]);
164                    }
165                    util_ald_free(cache, sgl_out->subgroupDNs);
166                    util_ald_free(cache, sgl_out);
167                    sgl_out =  NULL;
168                    break;
169                }
170            }
171            /* We were able to allocate new strings for all the subgroups */
172            if (sgl_out != NULL) {
173                sgl_out->len = sgl_in->len;
174            }
175        }
176    }
177
178    return sgl_out;
179}
180
181/*
182 * Delete an entire subgroupList.
183 */
184void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl)
185{
186    int i = 0;
187    if (sgl == NULL || *sgl == NULL) {
188        return;
189    }
190
191    for (i = 0; i < (*sgl)->len; i++) {
192        util_ald_free(cache, (*sgl)->subgroupDNs[i]);
193    }
194    util_ald_free(cache, *sgl);
195}
196
197/*
198 * Computes the hash on a set of strings. The first argument is the number
199 * of strings to hash, the rest of the args are strings.
200 * Algorithm taken from glibc.
201 */
202unsigned long util_ald_hash_string(int nstr, ...)
203{
204    int i;
205    va_list args;
206    unsigned long h=0, g;
207    char *str, *p;
208
209    va_start(args, nstr);
210    for (i=0; i < nstr; ++i) {
211        str = va_arg(args, char *);
212        for (p = str; *p; ++p) {
213            h = ( h << 4 ) + *p;
214            if ( ( g = h & 0xf0000000 ) ) {
215                h = h ^ (g >> 24);
216                h = h ^ g;
217            }
218        }
219    }
220    va_end(args);
221
222    return h;
223}
224
225
226/*
227  Purges a cache that has gotten full. We keep track of the time that we
228  added the entry that made the cache 3/4 full, then delete all entries
229  that were added before that time. It's pretty simplistic, but time to
230  purge is only O(n), which is more important.
231*/
232void util_ald_cache_purge(util_ald_cache_t *cache)
233{
234    unsigned long i;
235    util_cache_node_t *p, *q, **pp;
236    apr_time_t t;
237
238    if (!cache)
239        return;
240
241    cache->last_purge = apr_time_now();
242    cache->npurged = 0;
243    cache->numpurges++;
244
245    for (i=0; i < cache->size; ++i) {
246        pp = cache->nodes + i;
247        p = *pp;
248        while (p != NULL) {
249            if (p->add_time < cache->marktime) {
250                q = p->next;
251                (*cache->free)(cache, p->payload);
252                util_ald_free(cache, p);
253                cache->numentries--;
254                cache->npurged++;
255                p = *pp = q;
256            }
257            else {
258                pp = &(p->next);
259                p = *pp;
260            }
261        }
262    }
263
264    t = apr_time_now();
265    cache->avg_purgetime =
266         ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
267         cache->numpurges;
268}
269
270
271/*
272 * create caches
273 */
274util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
275{
276    util_url_node_t curl, *newcurl = NULL;
277    util_ald_cache_t *search_cache;
278    util_ald_cache_t *compare_cache;
279    util_ald_cache_t *dn_compare_cache;
280
281    /* create the three caches */
282    search_cache = util_ald_create_cache(st,
283                      st->search_cache_size,
284                      util_ldap_search_node_hash,
285                      util_ldap_search_node_compare,
286                      util_ldap_search_node_copy,
287                      util_ldap_search_node_free,
288                      util_ldap_search_node_display);
289    compare_cache = util_ald_create_cache(st,
290                      st->compare_cache_size,
291                      util_ldap_compare_node_hash,
292                      util_ldap_compare_node_compare,
293                      util_ldap_compare_node_copy,
294                      util_ldap_compare_node_free,
295                      util_ldap_compare_node_display);
296    dn_compare_cache = util_ald_create_cache(st,
297                      st->compare_cache_size,
298                      util_ldap_dn_compare_node_hash,
299                      util_ldap_dn_compare_node_compare,
300                      util_ldap_dn_compare_node_copy,
301                      util_ldap_dn_compare_node_free,
302                      util_ldap_dn_compare_node_display);
303
304    /* check that all the caches initialised successfully */
305    if (search_cache && compare_cache && dn_compare_cache) {
306
307        /* The contents of this structure will be duplicated in shared
308           memory during the insert.  So use stack memory rather than
309           pool memory to avoid a memory leak. */
310        memset (&curl, 0, sizeof(util_url_node_t));
311        curl.url = url;
312        curl.search_cache = search_cache;
313        curl.compare_cache = compare_cache;
314        curl.dn_compare_cache = dn_compare_cache;
315
316        newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
317
318    }
319
320    return newcurl;
321}
322
323
324util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
325                                long cache_size,
326                                unsigned long (*hashfunc)(void *),
327                                int (*comparefunc)(void *, void *),
328                                void * (*copyfunc)(util_ald_cache_t *cache, void *),
329                                void (*freefunc)(util_ald_cache_t *cache, void *),
330                                void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
331{
332    util_ald_cache_t *cache;
333    unsigned long i;
334#if APR_HAS_SHARED_MEMORY
335    apr_rmm_off_t block;
336#endif
337
338    if (cache_size <= 0)
339        return NULL;
340
341#if APR_HAS_SHARED_MEMORY
342    if (!st->cache_rmm) {
343        cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
344    }
345    else {
346        block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
347        cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
348    }
349#else
350    cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
351#endif
352    if (!cache)
353        return NULL;
354
355#if APR_HAS_SHARED_MEMORY
356    cache->rmm_addr = st->cache_rmm;
357    cache->shm_addr = st->cache_shm;
358#endif
359    cache->maxentries = cache_size;
360    cache->numentries = 0;
361    cache->size = cache_size / 3;
362    if (cache->size < 64) cache->size = 64;
363        for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
364            cache->size = primes[i]? primes[i] : primes[i-1];
365
366    cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
367    if (!cache->nodes) {
368        /* This frees cache in the right way even if !APR_HAS_SHARED_MEMORY or !st->cache_rmm */
369        util_ald_free(cache, cache);
370        return NULL;
371    }
372
373    for (i=0; i < cache->size; ++i)
374        cache->nodes[i] = NULL;
375
376    cache->hash = hashfunc;
377    cache->compare = comparefunc;
378    cache->copy = copyfunc;
379    cache->free = freefunc;
380    cache->display = displayfunc;
381
382    cache->fullmark = cache->maxentries / 4 * 3;
383    cache->marktime = 0;
384    cache->avg_purgetime = 0.0;
385    cache->numpurges = 0;
386    cache->last_purge = 0;
387    cache->npurged = 0;
388
389    cache->fetches = 0;
390    cache->hits = 0;
391    cache->inserts = 0;
392    cache->removes = 0;
393
394    return cache;
395}
396
397void util_ald_destroy_cache(util_ald_cache_t *cache)
398{
399    unsigned long i;
400    util_cache_node_t *p, *q;
401
402    if (cache == NULL)
403        return;
404
405    for (i = 0; i < cache->size; ++i) {
406        p = cache->nodes[i];
407        q = NULL;
408        while (p != NULL) {
409            q = p->next;
410           (*cache->free)(cache, p->payload);
411           util_ald_free(cache, p);
412           p = q;
413        }
414    }
415    util_ald_free(cache, cache->nodes);
416    util_ald_free(cache, cache);
417}
418
419void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
420{
421    unsigned long hashval;
422    util_cache_node_t *p;
423
424    if (cache == NULL)
425        return NULL;
426
427    cache->fetches++;
428
429    hashval = (*cache->hash)(payload) % cache->size;
430
431    for (p = cache->nodes[hashval];
432         p && !(*cache->compare)(p->payload, payload);
433         p = p->next) ;
434
435    if (p != NULL) {
436        cache->hits++;
437        return p->payload;
438    }
439    else {
440        return NULL;
441    }
442}
443
444/*
445 * Insert an item into the cache.
446 * *** Does not catch duplicates!!! ***
447 */
448void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
449{
450    unsigned long hashval;
451    void *tmp_payload;
452    util_cache_node_t *node;
453
454    /* sanity check */
455    if (cache == NULL || payload == NULL) {
456        return NULL;
457    }
458
459    /* check if we are full - if so, try purge */
460    if (cache->numentries >= cache->maxentries) {
461        util_ald_cache_purge(cache);
462        if (cache->numentries >= cache->maxentries) {
463            /* if the purge was not effective, we leave now to avoid an overflow */
464            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01323)
465                         "Purge of LDAP cache failed");
466            return NULL;
467        }
468    }
469
470    node = (util_cache_node_t *)util_ald_alloc(cache,
471                                               sizeof(util_cache_node_t));
472    if (node == NULL) {
473        /*
474         * XXX: The cache management should be rewritten to work
475         * properly when LDAPSharedCacheSize is too small.
476         */
477        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01324)
478                     "LDAPSharedCacheSize is too small. Increase it or "
479                     "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
480        if (cache->numentries < cache->fullmark) {
481            /*
482             * We have not even reached fullmark, trigger a complete purge.
483             * This is still better than not being able to add new entries
484             * at all.
485             */
486            cache->marktime = apr_time_now();
487        }
488        util_ald_cache_purge(cache);
489        node = (util_cache_node_t *)util_ald_alloc(cache,
490                                                   sizeof(util_cache_node_t));
491        if (node == NULL) {
492            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01325)
493                         "Could not allocate memory for LDAP cache entry");
494            return NULL;
495        }
496    }
497
498    /* Take a copy of the payload before proceeeding. */
499    tmp_payload = (*cache->copy)(cache, payload);
500    if (tmp_payload == NULL) {
501        /*
502         * XXX: The cache management should be rewritten to work
503         * properly when LDAPSharedCacheSize is too small.
504         */
505        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01326)
506                     "LDAPSharedCacheSize is too small. Increase it or "
507                     "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
508        if (cache->numentries < cache->fullmark) {
509            /*
510             * We have not even reached fullmark, trigger a complete purge.
511             * This is still better than not being able to add new entries
512             * at all.
513             */
514            cache->marktime = apr_time_now();
515        }
516        util_ald_cache_purge(cache);
517        tmp_payload = (*cache->copy)(cache, payload);
518        if (tmp_payload == NULL) {
519            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01327)
520                         "Could not allocate memory for LDAP cache value");
521            util_ald_free(cache, node);
522            return NULL;
523        }
524    }
525    payload = tmp_payload;
526
527    /* populate the entry */
528    cache->inserts++;
529    hashval = (*cache->hash)(payload) % cache->size;
530    node->add_time = apr_time_now();
531    node->payload = payload;
532    node->next = cache->nodes[hashval];
533    cache->nodes[hashval] = node;
534
535    /* if we reach the full mark, note the time we did so
536     * for the benefit of the purge function
537     */
538    if (++cache->numentries == cache->fullmark) {
539        cache->marktime=apr_time_now();
540    }
541
542    return node->payload;
543}
544
545void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
546{
547    unsigned long hashval;
548    util_cache_node_t *p, *q;
549
550    if (cache == NULL)
551        return;
552
553    cache->removes++;
554    hashval = (*cache->hash)(payload) % cache->size;
555    for (p = cache->nodes[hashval], q=NULL;
556         p && !(*cache->compare)(p->payload, payload);
557         p = p->next) {
558         q = p;
559    }
560
561    /* If p is null, it means that we couldn't find the node, so just return */
562    if (p == NULL)
563        return;
564
565    if (q == NULL) {
566        /* We found the node, and it's the first in the list */
567        cache->nodes[hashval] = p->next;
568    }
569    else {
570        /* We found the node and it's not the first in the list */
571        q->next = p->next;
572    }
573    (*cache->free)(cache, p->payload);
574    util_ald_free(cache, p);
575    cache->numentries--;
576}
577
578char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
579{
580    unsigned long i;
581    int totchainlen = 0;
582    int nchains = 0;
583    double chainlen;
584    util_cache_node_t *n;
585    char *buf, *buf2;
586    apr_pool_t *p = r->pool;
587
588    if (cache == NULL) {
589        return "";
590    }
591
592    for (i=0; i < cache->size; ++i) {
593        if (cache->nodes[i] != NULL) {
594            nchains++;
595            for (n = cache->nodes[i];
596                 n != NULL && n != n->next;
597                 n = n->next) {
598                totchainlen++;
599            }
600        }
601    }
602    chainlen = nchains? (double)totchainlen / (double)nchains : 0;
603
604    if (id) {
605        buf2 = apr_psprintf(p,
606                 "<a href=\"%s?%s\">%s</a>",
607             ap_escape_html(r->pool, ap_escape_uri(r->pool, r->uri)),
608             id,
609             name);
610    }
611    else {
612        buf2 = name;
613    }
614
615    buf = apr_psprintf(p,
616             "<tr valign='top'>"
617             "<td nowrap>%s</td>"
618             "<td align='right' nowrap>%lu (%.0f%% full)</td>"
619             "<td align='right'>%.1f</td>"
620             "<td align='right'>%lu/%lu</td>"
621             "<td align='right'>%.0f%%</td>"
622             "<td align='right'>%lu/%lu</td>",
623         buf2,
624         cache->numentries,
625         (double)cache->numentries / (double)cache->maxentries * 100.0,
626         chainlen,
627         cache->hits,
628         cache->fetches,
629         (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
630         cache->inserts,
631         cache->removes);
632
633    if (cache->numpurges) {
634        char str_ctime[APR_CTIME_LEN];
635
636        apr_ctime(str_ctime, cache->last_purge);
637        buf = apr_psprintf(p,
638                 "%s"
639                 "<td align='right'>%lu</td>\n"
640                 "<td align='right' nowrap>%s</td>\n",
641             buf,
642             cache->numpurges,
643             str_ctime);
644    }
645    else {
646        buf = apr_psprintf(p,
647                 "%s<td colspan='2' align='center'>(none)</td>\n",
648             buf);
649    }
650
651    buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
652
653    return buf;
654}
655
656char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
657{
658    unsigned long i,j;
659    char *buf, *t1, *t2, *t3;
660    char *id1, *id2, *id3;
661    char *argfmt = "cache=%s&id=%d&off=%d";
662    char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
663    apr_pool_t *pool = r->pool;
664    util_cache_node_t *p = NULL;
665    util_url_node_t *n = NULL;
666
667    util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
668
669
670    if (!util_ldap_cache) {
671        ap_rputs("<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>", r);
672        return NULL;
673    }
674
675    if (r->args && strlen(r->args)) {
676        char cachetype[5], lint[2];
677        unsigned int id, off;
678        char date_str[APR_CTIME_LEN];
679
680        if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
681            (id < util_ldap_cache->size)) {
682
683            if ((p = util_ldap_cache->nodes[id]) != NULL) {
684                n = (util_url_node_t *)p->payload;
685                buf = (char*)n->url;
686            }
687            else {
688                buf = "";
689            }
690
691            ap_rprintf(r,
692                       "<p>\n"
693                       "<table border='0'>\n"
694                       "<tr>\n"
695                       "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
696                       "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
697                       "</tr>\n"
698                       "</table>\n</p>\n",
699                       buf,
700                       cachetype[0] == 'm'? "Main" :
701                       (cachetype[0] == 's' ? "Search" :
702                        (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
703
704            switch (cachetype[0]) {
705                case 'm':
706                    if (util_ldap_cache->marktime) {
707                        apr_ctime(date_str, util_ldap_cache->marktime);
708                    }
709                    else
710                        date_str[0] = 0;
711
712                    ap_rprintf(r,
713                               "<p>\n"
714                               "<table border='0'>\n"
715                               "<tr>\n"
716                               "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
717                               "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
718                               "</tr>\n"
719                               "<tr>\n"
720                               "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
721                               "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
722                               "</tr>\n"
723                               "<tr>\n"
724                               "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
725                               "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
726                               "</tr>\n"
727                               "<tr>\n"
728                               "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
729                               "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
730                               "</tr>\n"
731                               "<tr>\n"
732                               "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
733                               "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
734                               "</tr>\n"
735                               "</table>\n</p>\n",
736                               util_ldap_cache->size,
737                               util_ldap_cache->maxentries,
738                               util_ldap_cache->numentries,
739                               util_ldap_cache->fullmark,
740                               date_str);
741
742                    ap_rputs("<p>\n"
743                             "<table border='0'>\n"
744                             "<tr bgcolor='#000000'>\n"
745                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
746                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
747                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
748                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
749                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
750                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
751                             "</tr>\n", r
752                            );
753                    for (i=0; i < util_ldap_cache->size; ++i) {
754                        for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
755
756                            (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
757                        }
758                    }
759                    ap_rputs("</table>\n</p>\n", r);
760
761
762                    break;
763                case 's':
764                    ap_rputs("<p>\n"
765                             "<table border='0'>\n"
766                             "<tr bgcolor='#000000'>\n"
767                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
768                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
769                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
770                             "</tr>\n", r
771                            );
772                    if (n) {
773                        for (i=0; i < n->search_cache->size; ++i) {
774                            for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
775
776                                (*n->search_cache->display)(r, n->search_cache, p->payload);
777                            }
778                        }
779                    }
780                    ap_rputs("</table>\n</p>\n", r);
781                    break;
782                case 'c':
783                    ap_rputs("<p>\n"
784                             "<table border='0'>\n"
785                             "<tr bgcolor='#000000'>\n"
786                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
787                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
788                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
789                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
790                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
791                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>"
792                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>"
793                             "</tr>\n", r
794                            );
795                    if (n) {
796                        for (i=0; i < n->compare_cache->size; ++i) {
797                            for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
798
799                                (*n->compare_cache->display)(r, n->compare_cache, p->payload);
800                            }
801                        }
802                    }
803                    ap_rputs("</table>\n</p>\n", r);
804                    break;
805                case 'd':
806                    ap_rputs("<p>\n"
807                             "<table border='0'>\n"
808                             "<tr bgcolor='#000000'>\n"
809                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
810                             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
811                             "</tr>\n", r
812                            );
813                    if (n) {
814                        for (i=0; i < n->dn_compare_cache->size; ++i) {
815                            for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
816
817                                (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
818                            }
819                        }
820                    }
821                    ap_rputs("</table>\n</p>\n", r);
822                    break;
823                default:
824                    break;
825            }
826
827        }
828        else {
829            buf = "";
830        }
831    }
832    else {
833        ap_rputs("<p>\n"
834                 "<table border='0'>\n"
835                 "<tr bgcolor='#000000'>\n"
836                 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
837                 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
838                 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
839                 "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
840                 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
841                 "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
842                 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
843                 "</tr>\n", r
844                );
845
846
847        id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
848        buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
849
850        for (i=0; i < util_ldap_cache->size; ++i) {
851            for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
852
853                n = (util_url_node_t *)p->payload;
854
855                t1 = apr_psprintf(pool, "%s (Searches)", n->url);
856                t2 = apr_psprintf(pool, "%s (Compares)", n->url);
857                t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
858                id1 = apr_psprintf(pool, argfmt, "srch", i, j);
859                id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
860                id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
861
862                buf = apr_psprintf(pool, "%s\n\n"
863                                         "%s\n\n"
864                                         "%s\n\n"
865                                         "%s\n\n",
866                                         buf,
867                                         util_ald_cache_display_stats(r, n->search_cache, t1, id1),
868                                         util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
869                                         util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
870                                  );
871            }
872        }
873        ap_rputs(buf, r);
874        ap_rputs("</table>\n</p>\n", r);
875    }
876
877    return buf;
878}
879
880#endif /* APR_HAS_LDAP */
881