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 * Info Module.  Display configuration information for the server and
19 * all included modules.
20 *
21 * <Location /server-info>
22 * SetHandler server-info
23 * </Location>
24 *
25 * GET /server-info - Returns full configuration page for server and all modules
26 * GET /server-info?server - Returns server configuration only
27 * GET /server-info?module_name - Returns configuration for a single module
28 * GET /server-info?list - Returns quick list of included modules
29 * GET /server-info?config - Returns full configuration
30 * GET /server-info?hooks - Returns a listing of the modules active for each hook
31 *
32 * Original Author:
33 *   Rasmus Lerdorf <rasmus vex.net>, May 1996
34 *
35 * Modified By:
36 *   Lou Langholtz <ldl usi.utah.edu>, July 1997
37 *
38 * Apache 2.0 Port:
39 *   Ryan Morgan <rmorgan covalent.net>, August 2000
40 *
41 */
42
43
44#include "apr.h"
45#include "apr_strings.h"
46#include "apr_lib.h"
47#include "apr_version.h"
48#include "apu_version.h"
49#define APR_WANT_STRFUNC
50#include "apr_want.h"
51
52#define CORE_PRIVATE
53
54#include "httpd.h"
55#include "http_config.h"
56#include "http_core.h"
57#include "http_log.h"
58#include "http_main.h"
59#include "http_protocol.h"
60#include "http_connection.h"
61#include "http_request.h"
62#include "util_script.h"
63#include "ap_mpm.h"
64
65typedef struct
66{
67    const char *name;           /* matching module name */
68    const char *info;           /* additional info */
69} info_entry;
70
71typedef struct
72{
73    apr_array_header_t *more_info;
74} info_svr_conf;
75
76module AP_MODULE_DECLARE_DATA info_module;
77
78static void *create_info_config(apr_pool_t * p, server_rec * s)
79{
80    info_svr_conf *conf =
81        (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf));
82
83    conf->more_info = apr_array_make(p, 20, sizeof(info_entry));
84    return conf;
85}
86
87static void *merge_info_config(apr_pool_t * p, void *basev, void *overridesv)
88{
89    info_svr_conf *new =
90        (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf));
91    info_svr_conf *base = (info_svr_conf *) basev;
92    info_svr_conf *overrides = (info_svr_conf *) overridesv;
93
94    new->more_info =
95        apr_array_append(p, overrides->more_info, base->more_info);
96    return new;
97}
98
99static void put_int_flush_right(request_rec * r, int i, int field)
100{
101    if (field > 1 || i > 9)
102        put_int_flush_right(r, i / 10, field - 1);
103    if (i)
104        ap_rputc('0' + i % 10, r);
105    else
106        ap_rputs("&nbsp;", r);
107}
108
109static void mod_info_indent(request_rec * r, int nest,
110                            const char *thisfn, int linenum)
111{
112    int i;
113    const char *prevfn =
114        ap_get_module_config(r->request_config, &info_module);
115    if (thisfn == NULL)
116        thisfn = "*UNKNOWN*";
117    if (prevfn == NULL || 0 != strcmp(prevfn, thisfn)) {
118        thisfn = ap_escape_html(r->pool, thisfn);
119        ap_rprintf(r, "<dd><tt><strong>In file: %s</strong></tt></dd>\n",
120                   thisfn);
121        ap_set_module_config(r->request_config, &info_module,
122                             (void *) thisfn);
123    }
124
125    ap_rputs("<dd><tt>", r);
126    put_int_flush_right(r, linenum > 0 ? linenum : 0, 4);
127    ap_rputs(":&nbsp;", r);
128
129    for (i = 1; i <= nest; ++i) {
130        ap_rputs("&nbsp;&nbsp;", r);
131    }
132}
133
134static void mod_info_show_cmd(request_rec * r, const ap_directive_t * dir,
135                              int nest)
136{
137    mod_info_indent(r, nest, dir->filename, dir->line_num);
138    ap_rprintf(r, "%s <i>%s</i></tt></dd>\n",
139               ap_escape_html(r->pool, dir->directive),
140               ap_escape_html(r->pool, dir->args));
141}
142
143static void mod_info_show_open(request_rec * r, const ap_directive_t * dir,
144                               int nest)
145{
146    mod_info_indent(r, nest, dir->filename, dir->line_num);
147    ap_rprintf(r, "%s %s</tt></dd>\n",
148               ap_escape_html(r->pool, dir->directive),
149               ap_escape_html(r->pool, dir->args));
150}
151
152static void mod_info_show_close(request_rec * r, const ap_directive_t * dir,
153                                int nest)
154{
155    const char *dirname = dir->directive;
156    mod_info_indent(r, nest, dir->filename, 0);
157    if (*dirname == '<') {
158        ap_rprintf(r, "&lt;/%s&gt;</tt></dd>",
159                   ap_escape_html(r->pool, dirname + 1));
160    }
161    else {
162        ap_rprintf(r, "/%s</tt></dd>", ap_escape_html(r->pool, dirname));
163    }
164}
165
166static int mod_info_has_cmd(const command_rec * cmds, ap_directive_t * dir)
167{
168    const command_rec *cmd;
169    if (cmds == NULL)
170        return 1;
171    for (cmd = cmds; cmd->name; ++cmd) {
172        if (strcasecmp(cmd->name, dir->directive) == 0)
173            return 1;
174    }
175    return 0;
176}
177
178static void mod_info_show_parents(request_rec * r, ap_directive_t * node,
179                                  int from, int to)
180{
181    if (from < to)
182        mod_info_show_parents(r, node->parent, from, to - 1);
183    mod_info_show_open(r, node, to);
184}
185
186static int mod_info_module_cmds(request_rec * r, const command_rec * cmds,
187                                ap_directive_t * node, int from, int level)
188{
189    int shown = from;
190    ap_directive_t *dir;
191    if (level == 0)
192        ap_set_module_config(r->request_config, &info_module, NULL);
193    for (dir = node; dir; dir = dir->next) {
194        if (dir->first_child != NULL) {
195            if (level < mod_info_module_cmds(r, cmds, dir->first_child,
196                                             shown, level + 1)) {
197                shown = level;
198                mod_info_show_close(r, dir, level);
199            }
200        }
201        else if (mod_info_has_cmd(cmds, dir)) {
202            if (shown < level) {
203                mod_info_show_parents(r, dir->parent, shown, level - 1);
204                shown = level;
205            }
206            mod_info_show_cmd(r, dir, level);
207        }
208    }
209    return shown;
210}
211
212typedef struct
213{                               /*XXX: should get something from apr_hooks.h instead */
214    void (*pFunc) (void);       /* just to get the right size */
215    const char *szName;
216    const char *const *aszPredecessors;
217    const char *const *aszSuccessors;
218    int nOrder;
219} hook_struct_t;
220
221/*
222 * hook_get_t is a pointer to a function that takes void as an argument and
223 * returns a pointer to an apr_array_header_t.  The nasty WIN32 ifdef
224 * is required to account for the fact that the ap_hook* calls all use
225 * STDCALL calling convention.
226 */
227typedef apr_array_header_t *(
228#ifdef WIN32
229                                __stdcall
230#endif
231                                * hook_get_t)      (void);
232
233typedef struct
234{
235    const char *name;
236    hook_get_t get;
237} hook_lookup_t;
238
239static hook_lookup_t startup_hooks[] = {
240    {"Pre-Config", ap_hook_get_pre_config},
241    {"Test Configuration", ap_hook_get_test_config},
242    {"Post Configuration", ap_hook_get_post_config},
243    {"Open Logs", ap_hook_get_open_logs},
244    {"Child Init", ap_hook_get_child_init},
245    {NULL},
246};
247
248static hook_lookup_t request_hooks[] = {
249    {"Pre-Connection", ap_hook_get_pre_connection},
250    {"Create Connection", ap_hook_get_create_connection},
251    {"Process Connection", ap_hook_get_process_connection},
252    {"Create Request", ap_hook_get_create_request},
253    {"Post-Read Request", ap_hook_get_post_read_request},
254    {"Header Parse", ap_hook_get_header_parser},
255    {"HTTP Scheme", ap_hook_get_http_scheme},
256    {"Default Port", ap_hook_get_default_port},
257    {"Quick Handler", ap_hook_get_quick_handler},
258    {"Translate Name", ap_hook_get_translate_name},
259    {"Map to Storage", ap_hook_get_map_to_storage},
260    {"Check Access", ap_hook_get_access_checker},
261    {"Verify User ID", ap_hook_get_check_user_id},
262    {"Verify User Access", ap_hook_get_auth_checker},
263    {"Check Type", ap_hook_get_type_checker},
264    {"Fixups", ap_hook_get_fixups},
265    {"Insert Filters", ap_hook_get_insert_filter},
266    {"Content Handlers", ap_hook_get_handler},
267    {"Logging", ap_hook_get_log_transaction},
268    {"Insert Errors", ap_hook_get_insert_error_filter},
269    {NULL},
270};
271
272static int module_find_hook(module * modp, hook_get_t hook_get)
273{
274    int i;
275    apr_array_header_t *hooks = hook_get();
276    hook_struct_t *elts;
277
278    if (!hooks) {
279        return 0;
280    }
281
282    elts = (hook_struct_t *) hooks->elts;
283
284    for (i = 0; i < hooks->nelts; i++) {
285        if (strcmp(elts[i].szName, modp->name) == 0) {
286            return 1;
287        }
288    }
289
290    return 0;
291}
292
293static void module_participate(request_rec * r,
294                               module * modp,
295                               hook_lookup_t * lookup, int *comma)
296{
297    if (module_find_hook(modp, lookup->get)) {
298        if (*comma) {
299            ap_rputs(", ", r);
300        }
301        ap_rvputs(r, "<tt>", lookup->name, "</tt>", NULL);
302        *comma = 1;
303    }
304}
305
306static void module_request_hook_participate(request_rec * r, module * modp)
307{
308    int i, comma = 0;
309
310    ap_rputs("<dt><strong>Request Phase Participation:</strong>\n", r);
311
312    for (i = 0; request_hooks[i].name; i++) {
313        module_participate(r, modp, &request_hooks[i], &comma);
314    }
315
316    if (!comma) {
317        ap_rputs("<tt> <em>none</em></tt>", r);
318    }
319    ap_rputs("</dt>\n", r);
320}
321
322static const char *find_more_info(server_rec * s, const char *module_name)
323{
324    int i;
325    info_svr_conf *conf =
326        (info_svr_conf *) ap_get_module_config(s->module_config,
327                                               &info_module);
328    info_entry *entry = (info_entry *) conf->more_info->elts;
329
330    if (!module_name) {
331        return 0;
332    }
333    for (i = 0; i < conf->more_info->nelts; i++) {
334        if (!strcmp(module_name, entry->name)) {
335            return entry->info;
336        }
337        entry++;
338    }
339    return 0;
340}
341
342static int show_server_settings(request_rec * r)
343{
344    server_rec *serv = r->server;
345    int max_daemons, forked, threaded;
346
347    ap_rputs("<h2><a name=\"server\">Server Settings</a></h2>", r);
348    ap_rprintf(r,
349               "<dl><dt><strong>Server Version:</strong> "
350               "<font size=\"+1\"><tt>%s</tt></font></dt>\n",
351               ap_get_server_description());
352    ap_rprintf(r,
353               "<dt><strong>Server Built:</strong> "
354               "<font size=\"+1\"><tt>%s</tt></font></dt>\n",
355               ap_get_server_built());
356    ap_rprintf(r,
357               "<dt><strong>Server loaded APR Version:</strong> "
358               "<tt>%s</tt></dt>\n", apr_version_string());
359    ap_rprintf(r,
360               "<dt><strong>Compiled with APR Version:</strong> "
361               "<tt>%s</tt></dt>\n", APR_VERSION_STRING);
362    ap_rprintf(r,
363               "<dt><strong>Server loaded APU Version:</strong> "
364               "<tt>%s</tt></dt>\n", apu_version_string());
365    ap_rprintf(r,
366               "<dt><strong>Compiled with APU Version:</strong> "
367               "<tt>%s</tt></dt>\n", APU_VERSION_STRING);
368    ap_rprintf(r,
369               "<dt><strong>Module Magic Number:</strong> "
370               "<tt>%d:%d</tt></dt>\n", MODULE_MAGIC_NUMBER_MAJOR,
371               MODULE_MAGIC_NUMBER_MINOR);
372    ap_rprintf(r,
373               "<dt><strong>Hostname/port:</strong> "
374               "<tt>%s:%u</tt></dt>\n",
375               ap_escape_html(r->pool, ap_get_server_name(r)),
376               ap_get_server_port(r));
377    ap_rprintf(r,
378               "<dt><strong>Timeouts:</strong> "
379               "<tt>connection: %d &nbsp;&nbsp; "
380               "keep-alive: %d</tt></dt>",
381               (int) (apr_time_sec(serv->timeout)),
382               (int) (apr_time_sec(serv->keep_alive_timeout)));
383    ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
384    ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);
385    ap_mpm_query(AP_MPMQ_IS_FORKED, &forked);
386    ap_rprintf(r, "<dt><strong>MPM Name:</strong> <tt>%s</tt></dt>\n",
387               ap_show_mpm());
388    ap_rprintf(r,
389               "<dt><strong>MPM Information:</strong> "
390               "<tt>Max Daemons: %d Threaded: %s Forked: %s</tt></dt>\n",
391               max_daemons, threaded ? "yes" : "no", forked ? "yes" : "no");
392    ap_rprintf(r,
393               "<dt><strong>Server Architecture:</strong> "
394               "<tt>%ld-bit</tt></dt>\n", 8 * (long) sizeof(void *));
395    ap_rprintf(r,
396               "<dt><strong>Server Root:</strong> "
397               "<tt>%s</tt></dt>\n", ap_server_root);
398    ap_rprintf(r,
399               "<dt><strong>Config File:</strong> "
400               "<tt>%s</tt></dt>\n", ap_conftree->filename);
401
402    ap_rputs("<dt><strong>Server Built With:</strong>\n"
403             "<tt style=\"white-space: pre;\">\n", r);
404
405    /* TODO: Not all of these defines are getting set like they do in main.c.
406     *       Missing some headers?
407     */
408
409#ifdef BIG_SECURITY_HOLE
410    ap_rputs(" -D BIG_SECURITY_HOLE\n", r);
411#endif
412
413#ifdef SECURITY_HOLE_PASS_AUTHORIZATION
414    ap_rputs(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n", r);
415#endif
416
417#ifdef OS
418    ap_rputs(" -D OS=\"" OS "\"\n", r);
419#endif
420
421#ifdef APACHE_MPM_DIR
422    ap_rputs(" -D APACHE_MPM_DIR=\"" APACHE_MPM_DIR "\"\n", r);
423#endif
424
425#ifdef HAVE_SHMGET
426    ap_rputs(" -D HAVE_SHMGET\n", r);
427#endif
428
429#if APR_FILE_BASED_SHM
430    ap_rputs(" -D APR_FILE_BASED_SHM\n", r);
431#endif
432
433#if APR_HAS_SENDFILE
434    ap_rputs(" -D APR_HAS_SENDFILE\n", r);
435#endif
436
437#if APR_HAS_MMAP
438    ap_rputs(" -D APR_HAS_MMAP\n", r);
439#endif
440
441#ifdef NO_WRITEV
442    ap_rputs(" -D NO_WRITEV\n", r);
443#endif
444
445#ifdef NO_LINGCLOSE
446    ap_rputs(" -D NO_LINGCLOSE\n", r);
447#endif
448
449#if APR_HAVE_IPV6
450    ap_rputs(" -D APR_HAVE_IPV6 (IPv4-mapped addresses ", r);
451#ifdef AP_ENABLE_V4_MAPPED
452    ap_rputs("enabled)\n", r);
453#else
454    ap_rputs("disabled)\n", r);
455#endif
456#endif
457
458#if APR_USE_FLOCK_SERIALIZE
459    ap_rputs(" -D APR_USE_FLOCK_SERIALIZE\n", r);
460#endif
461
462#if APR_USE_SYSVSEM_SERIALIZE
463    ap_rputs(" -D APR_USE_SYSVSEM_SERIALIZE\n", r);
464#endif
465
466#if APR_USE_POSIXSEM_SERIALIZE
467    ap_rputs(" -D APR_USE_POSIXSEM_SERIALIZE\n", r);
468#endif
469
470#if APR_USE_FCNTL_SERIALIZE
471    ap_rputs(" -D APR_USE_FCNTL_SERIALIZE\n", r);
472#endif
473
474#if APR_USE_PROC_PTHREAD_SERIALIZE
475    ap_rputs(" -D APR_USE_PROC_PTHREAD_SERIALIZE\n", r);
476#endif
477#if APR_PROCESS_LOCK_IS_GLOBAL
478    ap_rputs(" -D APR_PROCESS_LOCK_IS_GLOBAL\n", r);
479#endif
480
481#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
482    ap_rputs(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n", r);
483#endif
484
485#if APR_HAS_OTHER_CHILD
486    ap_rputs(" -D APR_HAS_OTHER_CHILD\n", r);
487#endif
488
489#ifdef AP_HAVE_RELIABLE_PIPED_LOGS
490    ap_rputs(" -D AP_HAVE_RELIABLE_PIPED_LOGS\n", r);
491#endif
492
493#ifdef BUFFERED_LOGS
494    ap_rputs(" -D BUFFERED_LOGS\n", r);
495#ifdef PIPE_BUF
496    ap_rputs(" -D PIPE_BUF=%ld\n", (long) PIPE_BUF, r);
497#endif
498#endif
499
500#if APR_CHARSET_EBCDIC
501    ap_rputs(" -D APR_CHARSET_EBCDIC\n", r);
502#endif
503
504#ifdef NEED_HASHBANG_EMUL
505    ap_rputs(" -D NEED_HASHBANG_EMUL\n", r);
506#endif
507
508#ifdef SHARED_CORE
509    ap_rputs(" -D SHARED_CORE\n", r);
510#endif
511
512/* This list displays the compiled in default paths: */
513#ifdef HTTPD_ROOT
514    ap_rputs(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n", r);
515#endif
516
517#ifdef SUEXEC_BIN
518    ap_rputs(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n", r);
519#endif
520
521#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)
522    ap_rputs(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n", r);
523#endif
524
525#ifdef DEFAULT_PIDLOG
526    ap_rputs(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n", r);
527#endif
528
529#ifdef DEFAULT_SCOREBOARD
530    ap_rputs(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n", r);
531#endif
532
533#ifdef DEFAULT_LOCKFILE
534    ap_rputs(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n", r);
535#endif
536
537#ifdef DEFAULT_ERRORLOG
538    ap_rputs(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n", r);
539#endif
540
541
542#ifdef AP_TYPES_CONFIG_FILE
543    ap_rputs(" -D AP_TYPES_CONFIG_FILE=\"" AP_TYPES_CONFIG_FILE "\"\n", r);
544#endif
545
546#ifdef SERVER_CONFIG_FILE
547    ap_rputs(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n", r);
548#endif
549    ap_rputs("</tt></dt>\n", r);
550    ap_rputs("</dl><hr />", r);
551    return 0;
552}
553
554static int dump_a_hook(request_rec * r, hook_get_t hook_get)
555{
556    int i;
557    char qs;
558    hook_struct_t *elts;
559    apr_array_header_t *hooks = hook_get();
560
561    if (!hooks) {
562        return 0;
563    }
564
565    if (r->args && strcasecmp(r->args, "hooks") == 0) {
566        qs = '?';
567    }
568    else {
569        qs = '#';
570    }
571
572    elts = (hook_struct_t *) hooks->elts;
573
574    for (i = 0; i < hooks->nelts; i++) {
575        ap_rprintf(r,
576                   "&nbsp;&nbsp; %02d <a href=\"%c%s\">%s</a> <br/>",
577                   elts[i].nOrder, qs, elts[i].szName, elts[i].szName);
578    }
579    return 0;
580}
581
582static int show_active_hooks(request_rec * r)
583{
584    int i;
585    ap_rputs("<h2><a name=\"startup_hooks\">Startup Hooks</a></h2>\n<dl>", r);
586
587    for (i = 0; startup_hooks[i].name; i++) {
588        ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n",
589                   startup_hooks[i].name);
590        dump_a_hook(r, startup_hooks[i].get);
591        ap_rputs("\n  </tt>\n</dt>\n", r);
592    }
593
594    ap_rputs
595        ("</dl>\n<hr />\n<h2><a name=\"request_hooks\">Request Hooks</a></h2>\n<dl>",
596         r);
597
598    for (i = 0; request_hooks[i].name; i++) {
599        ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n",
600                   request_hooks[i].name);
601        dump_a_hook(r, request_hooks[i].get);
602        ap_rputs("\n  </tt>\n</dt>\n", r);
603    }
604
605    ap_rputs("</dl>\n<hr />\n", r);
606
607    return 0;
608}
609
610static int display_info(request_rec * r)
611{
612    module *modp = NULL;
613    server_rec *serv = r->server;
614    const char *more_info;
615    const command_rec *cmd = NULL;
616    int comma = 0;
617
618    if (strcmp(r->handler, "server-info"))
619        return DECLINED;
620
621    r->allowed |= (AP_METHOD_BIT << M_GET);
622    if (r->method_number != M_GET)
623        return DECLINED;
624
625    ap_set_content_type(r, "text/html; charset=ISO-8859-1");
626
627    ap_rputs(DOCTYPE_XHTML_1_0T
628             "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
629             "<head>\n"
630             "  <title>Server Information</title>\n" "</head>\n", r);
631    ap_rputs("<body><h1 style=\"text-align: center\">"
632             "Apache Server Information</h1>\n", r);
633    if (!r->args || strcasecmp(r->args, "list")) {
634        if (!r->args) {
635            ap_rputs("<dl><dt><tt>Subpages:<br />", r);
636            ap_rputs("<a href=\"?config\">Configuration Files</a>, "
637                     "<a href=\"?server\">Server Settings</a>, "
638                     "<a href=\"?list\">Module List</a>,  "
639                     "<a href=\"?hooks\">Active Hooks</a>", r);
640            ap_rputs("</tt></dt></dl><hr />", r);
641
642            ap_rputs("<dl><dt><tt>Sections:<br />", r);
643            ap_rputs("<a href=\"#server\">Server Settings</a>, "
644                     "<a href=\"#startup_hooks\">Startup Hooks</a>, "
645                     "<a href=\"#request_hooks\">Request Hooks</a>", r);
646            ap_rputs("</tt></dt></dl><hr />", r);
647
648            ap_rputs("<dl><dt><tt>Loaded Modules: <br />", r);
649            /* TODO: Sort by Alpha */
650            for (modp = ap_top_module; modp; modp = modp->next) {
651                ap_rprintf(r, "<a href=\"#%s\">%s</a>", modp->name,
652                           modp->name);
653                if (modp->next) {
654                    ap_rputs(", ", r);
655                }
656            }
657            ap_rputs("</tt></dt></dl><hr />", r);
658        }
659
660        if (!r->args || !strcasecmp(r->args, "server")) {
661            show_server_settings(r);
662        }
663
664        if (!r->args || !strcasecmp(r->args, "hooks")) {
665            show_active_hooks(r);
666        }
667
668        if (r->args && 0 == strcasecmp(r->args, "config")) {
669            ap_rputs("<dl><dt><strong>Configuration:</strong>\n", r);
670            mod_info_module_cmds(r, NULL, ap_conftree, 0, 0);
671            ap_rputs("</dl><hr />", r);
672        }
673        else {
674            for (modp = ap_top_module; modp; modp = modp->next) {
675                if (!r->args || !strcasecmp(modp->name, r->args)) {
676                    ap_rprintf(r,
677                               "<dl><dt><a name=\"%s\"><strong>Module Name:</strong></a> "
678                               "<font size=\"+1\"><tt><a href=\"?%s\">%s</a></tt></font></dt>\n",
679                               modp->name, modp->name, modp->name);
680                    ap_rputs("<dt><strong>Content handlers:</strong> ", r);
681
682                    if (module_find_hook(modp, ap_hook_get_handler)) {
683                        ap_rputs("<tt> <em>yes</em></tt>", r);
684                    }
685                    else {
686                        ap_rputs("<tt> <em>none</em></tt>", r);
687                    }
688
689                    ap_rputs("</dt>", r);
690                    ap_rputs
691                        ("<dt><strong>Configuration Phase Participation:</strong>\n",
692                         r);
693                    if (modp->create_dir_config) {
694                        if (comma) {
695                            ap_rputs(", ", r);
696                        }
697                        ap_rputs("<tt>Create Directory Config</tt>", r);
698                        comma = 1;
699                    }
700                    if (modp->merge_dir_config) {
701                        if (comma) {
702                            ap_rputs(", ", r);
703                        }
704                        ap_rputs("<tt>Merge Directory Configs</tt>", r);
705                        comma = 1;
706                    }
707                    if (modp->create_server_config) {
708                        if (comma) {
709                            ap_rputs(", ", r);
710                        }
711                        ap_rputs("<tt>Create Server Config</tt>", r);
712                        comma = 1;
713                    }
714                    if (modp->merge_server_config) {
715                        if (comma) {
716                            ap_rputs(", ", r);
717                        }
718                        ap_rputs("<tt>Merge Server Configs</tt>", r);
719                        comma = 1;
720                    }
721                    if (!comma)
722                        ap_rputs("<tt> <em>none</em></tt>", r);
723                    comma = 0;
724                    ap_rputs("</dt>", r);
725
726                    module_request_hook_participate(r, modp);
727
728                    cmd = modp->cmds;
729                    if (cmd) {
730                        ap_rputs
731                            ("<dt><strong>Module Directives:</strong></dt>",
732                             r);
733                        while (cmd) {
734                            if (cmd->name) {
735                                ap_rprintf(r, "<dd><tt>%s%s - <i>",
736                                           ap_escape_html(r->pool, cmd->name),
737                                           cmd->name[0] == '<' ? "&gt;" : "");
738                                if (cmd->errmsg) {
739                                    ap_rputs(ap_escape_html(r->pool, cmd->errmsg), r);
740                                }
741                                ap_rputs("</i></tt></dd>\n", r);
742                            }
743                            else {
744                                break;
745                            }
746                            cmd++;
747                        }
748                        ap_rputs
749                            ("<dt><strong>Current Configuration:</strong></dt>\n",
750                             r);
751                        mod_info_module_cmds(r, modp->cmds, ap_conftree, 0,
752                                             0);
753                    }
754                    else {
755                        ap_rputs
756                            ("<dt><strong>Module Directives:</strong> <tt>none</tt></dt>",
757                             r);
758                    }
759                    more_info = find_more_info(serv, modp->name);
760                    if (more_info) {
761                        ap_rputs
762                            ("<dt><strong>Additional Information:</strong>\n</dt><dd>",
763                             r);
764                        ap_rputs(more_info, r);
765                        ap_rputs("</dd>", r);
766                    }
767                    ap_rputs("</dl><hr />\n", r);
768                    if (r->args) {
769                        break;
770                    }
771                }
772            }
773            if (!modp && r->args && strcasecmp(r->args, "server")) {
774                ap_rputs("<p><b>No such module</b></p>\n", r);
775            }
776        }
777    }
778    else {
779        ap_rputs("<dl><dt>Server Module List</dt>", r);
780        for (modp = ap_top_module; modp; modp = modp->next) {
781            ap_rputs("<dd>", r);
782            ap_rputs(modp->name, r);
783            ap_rputs("</dd>", r);
784        }
785        ap_rputs("</dl><hr />", r);
786    }
787    ap_rputs(ap_psignature("", r), r);
788    ap_rputs("</body></html>\n", r);
789    /* Done, turn off timeout, close file and return */
790    return 0;
791}
792
793static const char *add_module_info(cmd_parms * cmd, void *dummy,
794                                   const char *name, const char *info)
795{
796    server_rec *s = cmd->server;
797    info_svr_conf *conf =
798        (info_svr_conf *) ap_get_module_config(s->module_config,
799                                               &info_module);
800    info_entry *new = apr_array_push(conf->more_info);
801
802    new->name = name;
803    new->info = info;
804    return NULL;
805}
806
807static const command_rec info_cmds[] = {
808    AP_INIT_TAKE2("AddModuleInfo", add_module_info, NULL, RSRC_CONF,
809                  "a module name and additional information on that module"),
810    {NULL}
811};
812
813static void register_hooks(apr_pool_t * p)
814{
815    ap_hook_handler(display_info, NULL, NULL, APR_HOOK_MIDDLE);
816}
817
818module AP_MODULE_DECLARE_DATA info_module = {
819    STANDARD20_MODULE_STUFF,
820    NULL,                       /* dir config creater */
821    NULL,                       /* dir merger --- default is to override */
822    create_info_config,         /* server config */
823    merge_info_config,          /* merge server config */
824    info_cmds,                  /* command apr_table_t */
825    register_hooks
826};
827