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 
30 APLOG_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 
40 static 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 
util_ald_free(util_ald_cache_t * cache,const void * ptr)79 void 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 
util_ald_alloc(util_ald_cache_t * cache,unsigned long size)98 void *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 
util_ald_strdup(util_ald_cache_t * cache,const char * s)117 const 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  */
util_ald_sgl_dup(util_ald_cache_t * cache,util_compare_subgroup_t * sgl_in)145 util_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  */
util_ald_sgl_free(util_ald_cache_t * cache,util_compare_subgroup_t ** sgl)184 void 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  */
util_ald_hash_string(int nstr,...)202 unsigned 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 */
util_ald_cache_purge(util_ald_cache_t * cache)232 void 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 now;
237 
238     if (!cache)
239         return;
240 
241     now = cache->last_purge = apr_time_now();
242     cache->npurged = 0;
243     cache->numpurges++;
244 
245     /* If the marktime is farther back than TTL from now,
246        move the marktime forward to include additional expired entries.
247     */
248     if (now - cache->ttl > cache->marktime) {
249         cache->marktime = now - cache->ttl;
250     }
251 
252     for (i=0; i < cache->size; ++i) {
253         pp = cache->nodes + i;
254         p = *pp;
255         while (p != NULL) {
256             if (p->add_time < cache->marktime) {
257                 q = p->next;
258                 (*cache->free)(cache, p->payload);
259                 util_ald_free(cache, p);
260                 cache->numentries--;
261                 cache->npurged++;
262                 p = *pp = q;
263             }
264             else {
265                 pp = &(p->next);
266                 p = *pp;
267             }
268         }
269     }
270 
271     now = apr_time_now();
272     cache->avg_purgetime =
273          ((now - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
274          cache->numpurges;
275 }
276 
277 
278 /*
279  * create caches
280  */
util_ald_create_caches(util_ldap_state_t * st,const char * url)281 util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
282 {
283     util_url_node_t curl;
284     util_ald_cache_t *search_cache;
285     util_ald_cache_t *compare_cache;
286     util_ald_cache_t *dn_compare_cache;
287 
288     /* create the three caches */
289     search_cache = util_ald_create_cache(st,
290                       st->search_cache_size,
291                       st->search_cache_ttl,
292                       util_ldap_search_node_hash,
293                       util_ldap_search_node_compare,
294                       util_ldap_search_node_copy,
295                       util_ldap_search_node_free,
296                       util_ldap_search_node_display);
297     compare_cache = util_ald_create_cache(st,
298                       st->compare_cache_size,
299                       st->compare_cache_ttl,
300                       util_ldap_compare_node_hash,
301                       util_ldap_compare_node_compare,
302                       util_ldap_compare_node_copy,
303                       util_ldap_compare_node_free,
304                       util_ldap_compare_node_display);
305     dn_compare_cache = util_ald_create_cache(st,
306                       st->compare_cache_size,
307                       st->compare_cache_ttl,
308                       util_ldap_dn_compare_node_hash,
309                       util_ldap_dn_compare_node_compare,
310                       util_ldap_dn_compare_node_copy,
311                       util_ldap_dn_compare_node_free,
312                       util_ldap_dn_compare_node_display);
313 
314     /* check that all the caches initialised successfully */
315     if (search_cache && compare_cache && dn_compare_cache) {
316         /* The contents of this structure will be duplicated in shared
317            memory during the insert.  So use stack memory rather than
318            pool memory to avoid a memory leak. */
319         memset (&curl, 0, sizeof(util_url_node_t));
320         curl.url = url;
321         curl.search_cache = search_cache;
322         curl.compare_cache = compare_cache;
323         curl.dn_compare_cache = dn_compare_cache;
324 
325         return util_ald_cache_insert(st->util_ldap_cache, &curl);
326     }
327     else {
328         /* util_ald_destroy_cache is a noop for a NULL argument. */
329         util_ald_destroy_cache(search_cache);
330         util_ald_destroy_cache(compare_cache);
331         util_ald_destroy_cache(dn_compare_cache);
332 
333         return NULL;
334     }
335 }
336 
337 
util_ald_create_cache(util_ldap_state_t * st,long cache_size,long cache_ttl,unsigned long (* hashfunc)(void *),int (* comparefunc)(void *,void *),void * (* copyfunc)(util_ald_cache_t * cache,void *),void (* freefunc)(util_ald_cache_t * cache,void *),void (* displayfunc)(request_rec * r,util_ald_cache_t * cache,void *))338 util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
339                                 long cache_size,
340                                 long cache_ttl,
341                                 unsigned long (*hashfunc)(void *),
342                                 int (*comparefunc)(void *, void *),
343                                 void * (*copyfunc)(util_ald_cache_t *cache, void *),
344                                 void (*freefunc)(util_ald_cache_t *cache, void *),
345                                 void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
346 {
347     util_ald_cache_t *cache;
348     unsigned long i;
349 #if APR_HAS_SHARED_MEMORY
350     apr_rmm_off_t block;
351 #endif
352 
353     if (cache_size <= 0)
354         return NULL;
355 
356 #if APR_HAS_SHARED_MEMORY
357     if (!st->cache_rmm) {
358         cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
359     }
360     else {
361         block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
362         cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
363     }
364 #else
365     cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
366 #endif
367     if (!cache)
368         return NULL;
369 
370 #if APR_HAS_SHARED_MEMORY
371     cache->rmm_addr = st->cache_rmm;
372     cache->shm_addr = st->cache_shm;
373 #endif
374     cache->maxentries = cache_size;
375     cache->numentries = 0;
376     cache->size = cache_size / 3;
377     if (cache->size < 64)
378         cache->size = 64;
379     for (i = 0; primes[i] && primes[i] < cache->size; ++i)
380         ;
381     cache->size = primes[i] ? primes[i] : primes[i-1];
382 
383     cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
384     if (!cache->nodes) {
385         /* This frees cache in the right way even if !APR_HAS_SHARED_MEMORY or !st->cache_rmm */
386         util_ald_free(cache, cache);
387         return NULL;
388     }
389 
390     for (i=0; i < cache->size; ++i)
391         cache->nodes[i] = NULL;
392 
393     cache->hash = hashfunc;
394     cache->compare = comparefunc;
395     cache->copy = copyfunc;
396     cache->free = freefunc;
397     cache->display = displayfunc;
398 
399 
400     cache->fullmark = cache->maxentries / 4 * 3;
401     cache->marktime = 0;
402     cache->ttl = cache_ttl;
403     cache->avg_purgetime = 0.0;
404     cache->numpurges = 0;
405     cache->last_purge = 0;
406     cache->npurged = 0;
407 
408     cache->fetches = 0;
409     cache->hits = 0;
410     cache->inserts = 0;
411     cache->removes = 0;
412 
413     return cache;
414 }
415 
util_ald_destroy_cache(util_ald_cache_t * cache)416 void util_ald_destroy_cache(util_ald_cache_t *cache)
417 {
418     unsigned long i;
419     util_cache_node_t *p, *q;
420 
421     if (cache == NULL)
422         return;
423 
424     for (i = 0; i < cache->size; ++i) {
425         p = cache->nodes[i];
426         q = NULL;
427         while (p != NULL) {
428             q = p->next;
429            (*cache->free)(cache, p->payload);
430            util_ald_free(cache, p);
431            p = q;
432         }
433     }
434     util_ald_free(cache, cache->nodes);
435     util_ald_free(cache, cache);
436 }
437 
util_ald_cache_fetch(util_ald_cache_t * cache,void * payload)438 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
439 {
440     unsigned long hashval;
441     util_cache_node_t *p;
442 
443     if (cache == NULL)
444         return NULL;
445 
446     cache->fetches++;
447 
448     hashval = (*cache->hash)(payload) % cache->size;
449 
450     for (p = cache->nodes[hashval];
451          p && !(*cache->compare)(p->payload, payload);
452          p = p->next) ;
453 
454     if (p != NULL) {
455         cache->hits++;
456         return p->payload;
457     }
458     else {
459         return NULL;
460     }
461 }
462 
463 /*
464  * Insert an item into the cache.
465  * *** Does not catch duplicates!!! ***
466  */
util_ald_cache_insert(util_ald_cache_t * cache,void * payload)467 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
468 {
469     unsigned long hashval;
470     void *tmp_payload;
471     util_cache_node_t *node;
472 
473     /* sanity check */
474     if (cache == NULL || payload == NULL) {
475         return NULL;
476     }
477 
478     /* check if we are full - if so, try purge */
479     if (cache->numentries >= cache->maxentries) {
480         util_ald_cache_purge(cache);
481         if (cache->numentries >= cache->maxentries) {
482             /* if the purge was not effective, we leave now to avoid an overflow */
483             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01323)
484                          "Purge of LDAP cache failed");
485             return NULL;
486         }
487     }
488 
489     node = (util_cache_node_t *)util_ald_alloc(cache,
490                                                sizeof(util_cache_node_t));
491     if (node == NULL) {
492         /*
493          * XXX: The cache management should be rewritten to work
494          * properly when LDAPSharedCacheSize is too small.
495          */
496         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01324)
497                      "LDAPSharedCacheSize is too small. Increase it or "
498                      "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
499         if (cache->numentries < cache->fullmark) {
500             /*
501              * We have not even reached fullmark, trigger a complete purge.
502              * This is still better than not being able to add new entries
503              * at all.
504              */
505             cache->marktime = apr_time_now();
506         }
507         util_ald_cache_purge(cache);
508         node = (util_cache_node_t *)util_ald_alloc(cache,
509                                                    sizeof(util_cache_node_t));
510         if (node == NULL) {
511             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01325)
512                          "Could not allocate memory for LDAP cache entry");
513             return NULL;
514         }
515     }
516 
517     /* Take a copy of the payload before proceeding. */
518     tmp_payload = (*cache->copy)(cache, payload);
519     if (tmp_payload == NULL) {
520         /*
521          * XXX: The cache management should be rewritten to work
522          * properly when LDAPSharedCacheSize is too small.
523          */
524         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01326)
525                      "LDAPSharedCacheSize is too small. Increase it or "
526                      "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
527         if (cache->numentries < cache->fullmark) {
528             /*
529              * We have not even reached fullmark, trigger a complete purge.
530              * This is still better than not being able to add new entries
531              * at all.
532              */
533             cache->marktime = apr_time_now();
534         }
535         util_ald_cache_purge(cache);
536         tmp_payload = (*cache->copy)(cache, payload);
537         if (tmp_payload == NULL) {
538             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01327)
539                          "Could not allocate memory for LDAP cache value");
540             util_ald_free(cache, node);
541             return NULL;
542         }
543     }
544     payload = tmp_payload;
545 
546     /* populate the entry */
547     cache->inserts++;
548     hashval = (*cache->hash)(payload) % cache->size;
549     node->add_time = apr_time_now();
550     node->payload = payload;
551     node->next = cache->nodes[hashval];
552     cache->nodes[hashval] = node;
553 
554     /* if we reach the full mark, note the time we did so
555      * for the benefit of the purge function
556      */
557     if (++cache->numentries == cache->fullmark) {
558         cache->marktime=apr_time_now();
559     }
560 
561     return node->payload;
562 }
563 
util_ald_cache_remove(util_ald_cache_t * cache,void * payload)564 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
565 {
566     unsigned long hashval;
567     util_cache_node_t *p, *q;
568 
569     if (cache == NULL)
570         return;
571 
572     cache->removes++;
573     hashval = (*cache->hash)(payload) % cache->size;
574     for (p = cache->nodes[hashval], q=NULL;
575          p && !(*cache->compare)(p->payload, payload);
576          p = p->next) {
577          q = p;
578     }
579 
580     /* If p is null, it means that we couldn't find the node, so just return */
581     if (p == NULL)
582         return;
583 
584     if (q == NULL) {
585         /* We found the node, and it's the first in the list */
586         cache->nodes[hashval] = p->next;
587     }
588     else {
589         /* We found the node and it's not the first in the list */
590         q->next = p->next;
591     }
592     (*cache->free)(cache, p->payload);
593     util_ald_free(cache, p);
594     cache->numentries--;
595 }
596 
util_ald_cache_display_stats(request_rec * r,util_ald_cache_t * cache,char * name,char * id)597 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
598 {
599     unsigned long i;
600     int totchainlen = 0;
601     int nchains = 0;
602     double chainlen;
603     util_cache_node_t *n;
604     char *buf, *buf2;
605     apr_pool_t *p = r->pool;
606 
607     if (cache == NULL) {
608         return "";
609     }
610 
611     for (i=0; i < cache->size; ++i) {
612         if (cache->nodes[i] != NULL) {
613             nchains++;
614             for (n = cache->nodes[i];
615                  n != NULL && n != n->next;
616                  n = n->next) {
617                 totchainlen++;
618             }
619         }
620     }
621     chainlen = nchains? (double)totchainlen / (double)nchains : 0;
622 
623     if (id) {
624         buf2 = apr_psprintf(p,
625                  "<a href=\"%s?%s\">%s</a>",
626              ap_escape_html(r->pool, ap_escape_uri(r->pool, r->uri)),
627              id,
628              name);
629     }
630     else {
631         buf2 = name;
632     }
633 
634     buf = apr_psprintf(p,
635              "<tr valign='top'>"
636              "<td nowrap>%s</td>"
637              "<td align='right' nowrap>%lu (%.0f%% full)</td>"
638              "<td align='right'>%.1f</td>"
639              "<td align='right'>%lu/%lu</td>"
640              "<td align='right'>%.0f%%</td>"
641              "<td align='right'>%lu/%lu</td>",
642          buf2,
643          cache->numentries,
644          (double)cache->numentries / (double)cache->maxentries * 100.0,
645          chainlen,
646          cache->hits,
647          cache->fetches,
648          (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
649          cache->inserts,
650          cache->removes);
651 
652     if (cache->numpurges) {
653         char str_ctime[APR_CTIME_LEN];
654 
655         apr_ctime(str_ctime, cache->last_purge);
656         buf = apr_psprintf(p,
657                  "%s"
658                  "<td align='right'>%lu</td>\n"
659                  "<td align='right' nowrap>%s</td>\n",
660              buf,
661              cache->numpurges,
662              str_ctime);
663     }
664     else {
665         buf = apr_psprintf(p,
666                  "%s<td colspan='2' align='center'>(none)</td>\n",
667              buf);
668     }
669 
670     buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
671 
672     return buf;
673 }
674 
util_ald_cache_display(request_rec * r,util_ldap_state_t * st)675 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
676 {
677     unsigned long i,j;
678     char *buf, *t1, *t2, *t3;
679     char *id1, *id2, *id3;
680     char *argfmt = "cache=%s&id=%d&off=%d";
681     char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
682     apr_pool_t *pool = r->pool;
683     util_cache_node_t *p = NULL;
684     util_url_node_t *n = NULL;
685 
686     util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
687 
688 
689     if (!util_ldap_cache) {
690         ap_rputs("<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>", r);
691         return NULL;
692     }
693 
694     if (r->args && strlen(r->args)) {
695         char cachetype[5], lint[2];
696         unsigned int id, off;
697         char date_str[APR_CTIME_LEN];
698 
699         if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
700             (id < util_ldap_cache->size)) {
701 
702             if ((p = util_ldap_cache->nodes[id]) != NULL) {
703                 n = (util_url_node_t *)p->payload;
704                 buf = (char*)n->url;
705             }
706             else {
707                 buf = "";
708             }
709 
710             ap_rprintf(r,
711                        "<p>\n"
712                        "<table border='0'>\n"
713                        "<tr>\n"
714                        "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
715                        "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
716                        "</tr>\n"
717                        "</table>\n</p>\n",
718                        buf,
719                        cachetype[0] == 'm'? "Main" :
720                        (cachetype[0] == 's' ? "Search" :
721                         (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
722 
723             switch (cachetype[0]) {
724                 case 'm':
725                     if (util_ldap_cache->marktime) {
726                         apr_ctime(date_str, util_ldap_cache->marktime);
727                     }
728                     else
729                         date_str[0] = 0;
730 
731                     ap_rprintf(r,
732                                "<p>\n"
733                                "<table border='0'>\n"
734                                "<tr>\n"
735                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
736                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
737                                "</tr>\n"
738                                "<tr>\n"
739                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
740                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
741                                "</tr>\n"
742                                "<tr>\n"
743                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
744                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
745                                "</tr>\n"
746                                "<tr>\n"
747                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>TTL (sec):</b></font></td>"
748                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%" APR_TIME_T_FMT "</b></font></td>"
749                                "</tr>\n"
750                                "<tr>\n"
751                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
752                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
753                                "</tr>\n"
754                                "<tr>\n"
755                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
756                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
757                                "</tr>\n"
758                                "</table>\n</p>\n",
759                                util_ldap_cache->size,
760                                util_ldap_cache->maxentries,
761                                util_ldap_cache->numentries,
762                                apr_time_sec(util_ldap_cache->ttl),
763                                util_ldap_cache->fullmark,
764                                date_str);
765 
766                     ap_rputs("<p>\n"
767                              "<table border='0'>\n"
768                              "<tr bgcolor='#000000'>\n"
769                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
770                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
771                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
772                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
773                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>TTL (sec)</b></font></td>"
774                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
775                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
776                              "</tr>\n", r
777                             );
778                     for (i=0; i < util_ldap_cache->size; ++i) {
779                         for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
780 
781                             (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
782                         }
783                     }
784                     ap_rputs("</table>\n</p>\n", r);
785 
786 
787                     break;
788                 case 's':
789                     ap_rputs("<p>\n"
790                              "<table border='0'>\n"
791                              "<tr bgcolor='#000000'>\n"
792                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
793                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
794                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
795                              "</tr>\n", r
796                             );
797                     if (n) {
798                         for (i=0; i < n->search_cache->size; ++i) {
799                             for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
800 
801                                 (*n->search_cache->display)(r, n->search_cache, p->payload);
802                             }
803                         }
804                     }
805                     ap_rputs("</table>\n</p>\n", r);
806                     break;
807                 case 'c':
808                     ap_rputs("<p>\n"
809                              "<table border='0'>\n"
810                              "<tr bgcolor='#000000'>\n"
811                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
812                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
813                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
814                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
815                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
816                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>"
817                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>"
818                              "</tr>\n", r
819                             );
820                     if (n) {
821                         for (i=0; i < n->compare_cache->size; ++i) {
822                             for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
823 
824                                 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
825                             }
826                         }
827                     }
828                     ap_rputs("</table>\n</p>\n", r);
829                     break;
830                 case 'd':
831                     ap_rputs("<p>\n"
832                              "<table border='0'>\n"
833                              "<tr bgcolor='#000000'>\n"
834                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
835                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
836                              "</tr>\n", r
837                             );
838                     if (n) {
839                         for (i=0; i < n->dn_compare_cache->size; ++i) {
840                             for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
841 
842                                 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
843                             }
844                         }
845                     }
846                     ap_rputs("</table>\n</p>\n", r);
847                     break;
848                 default:
849                     break;
850             }
851 
852         }
853         else {
854             buf = "";
855         }
856     }
857     else {
858         ap_rputs("<p>\n"
859                  "<table border='0'>\n"
860                  "<tr bgcolor='#000000'>\n"
861                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
862                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
863                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
864                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
865                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
866                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
867                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
868                  "</tr>\n", r
869                 );
870 
871 
872         id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
873         buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
874 
875         for (i=0; i < util_ldap_cache->size; ++i) {
876             for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
877 
878                 n = (util_url_node_t *)p->payload;
879 
880                 t1 = apr_psprintf(pool, "%s (Searches)", n->url);
881                 t2 = apr_psprintf(pool, "%s (Compares)", n->url);
882                 t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
883                 id1 = apr_psprintf(pool, argfmt, "srch", i, j);
884                 id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
885                 id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
886 
887                 buf = apr_psprintf(pool, "%s\n\n"
888                                          "%s\n\n"
889                                          "%s\n\n"
890                                          "%s\n\n",
891                                          buf,
892                                          util_ald_cache_display_stats(r, n->search_cache, t1, id1),
893                                          util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
894                                          util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
895                                   );
896             }
897         }
898         ap_rputs(buf, r);
899         ap_rputs("</table>\n</p>\n", r);
900     }
901 
902     return buf;
903 }
904 
905 #endif /* APR_HAS_LDAP */
906