1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58 
59 /*
60  * Info Module.  Display configuration information for the server and
61  * all included modules.
62  *
63  * <Location /server-info>
64  * SetHandler server-info
65  * </Location>
66  *
67  * GET /server-info - Returns full configuration page for server and all modules
68  * GET /server-info?server - Returns server configuration only
69  * GET /server-info?module_name - Returns configuration for a single module
70  * GET /server-info?list - Returns quick list of included modules
71  *
72  * Rasmus Lerdorf <rasmus@php.net>, May 1996
73  *
74  * 05.01.96 Initial Version
75  *
76  * Lou Langholtz <ldl@usi.utah.edu>, July 1997
77  *
78  * 07.11.97 Addition of the AddModuleInfo directive
79  *
80  */
81 
82 #include "httpd.h"
83 #include "http_config.h"
84 #include "http_core.h"
85 #include "http_log.h"
86 #include "http_main.h"
87 #include "http_protocol.h"
88 #include "util_script.h"
89 #include "http_conf_globals.h"
90 
91 __RCSID("$MirOS: src/usr.sbin/httpd/src/modules/standard/mod_info.c,v 1.2 2008/12/03 11:23:01 tg Exp $");
92 
93 typedef struct {
94     char *name;                 /* matching module name */
95     char *info;                 /* additional info */
96 } info_entry;
97 
98 typedef struct {
99     array_header *more_info;
100 } info_svr_conf;
101 
102 typedef struct info_cfg_lines {
103     char *cmd;
104     char *line;
105     struct info_cfg_lines *next;
106 } info_cfg_lines;
107 
108 typedef struct {                /* shamelessly lifted from http_config.c */
109     char *fname;
110 } info_fnames;
111 
112 typedef struct {
113     info_cfg_lines *clines;
114     char *fname;
115 } info_clines;
116 
117 module MODULE_VAR_EXPORT info_module;
118 extern module API_VAR_EXPORT *top_module;
119 
120 /* shamelessly lifted from http_config.c */
fname_alphasort(const void * fn1,const void * fn2)121 static int fname_alphasort(const void *fn1, const void *fn2)
122 {
123     const info_fnames *f1 = fn1;
124     const info_fnames *f2 = fn2;
125 
126     return strcmp(f1->fname,f2->fname);
127 }
128 
create_info_config(pool * p,server_rec * s)129 static void *create_info_config(pool *p, server_rec *s)
130 {
131     info_svr_conf *conf = (info_svr_conf *) ap_pcalloc(p, sizeof(info_svr_conf));
132 
133     conf->more_info = ap_make_array(p, 20, sizeof(info_entry));
134     return conf;
135 }
136 
merge_info_config(pool * p,void * basev,void * overridesv)137 static void *merge_info_config(pool *p, void *basev, void *overridesv)
138 {
139     info_svr_conf *new = (info_svr_conf *) ap_pcalloc(p, sizeof(info_svr_conf));
140     info_svr_conf *base = (info_svr_conf *) basev;
141     info_svr_conf *overrides = (info_svr_conf *) overridesv;
142 
143     new->more_info = ap_append_arrays(p, overrides->more_info, base->more_info);
144     return new;
145 }
146 
mod_info_html_cmd_string(const char * string,char * buf,size_t buf_len)147 static char *mod_info_html_cmd_string(const char *string, char *buf, size_t buf_len)
148 {
149     const char *s;
150     char *t;
151     char *end_buf;
152 
153     s = string;
154     t = buf;
155     /* keep space for \0 byte */
156     end_buf = buf + buf_len - 1;
157     while ((*s) && (t < end_buf)) {
158         if (*s == '<') {
159             strncpy(t, "&lt;", end_buf - t);
160             t += 4;
161         }
162         else if (*s == '>') {
163             strncpy(t, "&gt;", end_buf - t);
164             t += 4;
165         }
166         else if (*s == '&') {
167             strncpy(t, "&amp;", end_buf - t);
168             t += 5;
169         }
170         else {
171             *t++ = *s;
172         }
173         s++;
174     }
175     /* oops, overflowed... don't overwrite */
176     if (t > end_buf) {
177         *end_buf = '\0';
178     }
179     else {
180         *t = '\0';
181     }
182     return (buf);
183 }
184 
mod_info_load_config(pool * p,const char * filename,request_rec * r)185 static info_cfg_lines *mod_info_load_config(pool *p, const char *filename,
186                                             request_rec *r)
187 {
188     char s[MAX_STRING_LEN];
189     configfile_t *fp;
190     info_cfg_lines *new, *ret, *prev;
191     const char *t;
192 
193     fp = ap_pcfg_openfile(p, filename);
194     if (!fp) {
195         ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
196                     "mod_info: couldn't open config file %s",
197                     filename);
198         return NULL;
199     }
200     ret = NULL;
201     prev = NULL;
202     while (!ap_cfg_getline(s, MAX_STRING_LEN, fp)) {
203         if (*s == '#') {
204             continue;           /* skip comments */
205         }
206         new = ap_palloc(p, sizeof(struct info_cfg_lines));
207         new->next = NULL;
208         if (!ret) {
209             ret = new;
210         }
211         if (prev) {
212             prev->next = new;
213         }
214         t = s;
215         new->cmd = ap_getword_conf(p, &t);
216         if (*t) {
217             new->line = ap_pstrdup(p, t);
218         }
219         else {
220             new->line = NULL;
221         }
222         prev = new;
223     }
224     ap_cfg_closefile(fp);
225     return (ret);
226 }
227 
mod_info_module_cmds(request_rec * r,info_cfg_lines * cfg,const command_rec * cmds,char * label)228 static void mod_info_module_cmds(request_rec *r, info_cfg_lines *cfg,
229                                  const command_rec *cmds, char *label)
230 {
231     const command_rec *cmd = cmds;
232     info_cfg_lines *li = cfg, *li_st = NULL, *li_se = NULL;
233     info_cfg_lines *block_start = NULL;
234     int lab = 0, nest = 0;
235     char buf[MAX_STRING_LEN];
236 
237     while (li) {
238         if (!strncasecmp(li->cmd, "<directory", 10) ||
239             !strncasecmp(li->cmd, "<location", 9) ||
240             !strncasecmp(li->cmd, "<limit", 6) ||
241             !strncasecmp(li->cmd, "<files", 6)) {
242             if (nest) {
243                 li_se = li;
244             }
245             else {
246                 li_st = li;
247             }
248             li = li->next;
249             nest++;
250             continue;
251         }
252         else if (nest && (!strncasecmp(li->cmd, "</limit", 7) ||
253                           !strncasecmp(li->cmd, "</location", 10) ||
254                           !strncasecmp(li->cmd, "</directory", 11) ||
255                           !strncasecmp(li->cmd, "</files", 7))) {
256             if (block_start) {
257                 if ((nest == 1 && block_start == li_st) ||
258                     (nest == 2 && block_start == li_se)) {
259                     ap_rputs("<dd><tt>", r);
260                     if (nest == 2) {
261                         ap_rputs("&nbsp;&nbsp;", r);
262                     }
263                     ap_rputs(mod_info_html_cmd_string(li->cmd, buf, sizeof(buf)), r);
264                     ap_rputs(" ", r);
265                     if (li->line) {
266                         ap_rputs(mod_info_html_cmd_string(li->line, buf, sizeof(buf)), r);
267                     }
268                     ap_rputs("</tt>\n", r);
269                     nest--;
270                     if (!nest) {
271                         block_start = NULL;
272                         li_st = NULL;
273                     }
274                     else {
275                         block_start = li_st;
276                     }
277                     li_se = NULL;
278                 }
279                 else {
280                     nest--;
281                     if (!nest) {
282                         li_st = NULL;
283                     }
284                     li_se = NULL;
285                 }
286             }
287             else {
288                 nest--;
289                 if (!nest) {
290                     li_st = NULL;
291                 }
292                 li_se = NULL;
293             }
294             li = li->next;
295             continue;
296         }
297         cmd = cmds;
298         while (cmd) {
299             if (cmd->name) {
300                 if (!strcasecmp(cmd->name, li->cmd)) {
301                     if (!lab) {
302                         ap_rputs("<dt><strong>", r);
303                         ap_rputs(label, r);
304                         ap_rputs("</strong>\n", r);
305                         lab = 1;
306                     }
307                     if (((nest && block_start == NULL) ||
308                          (nest == 2 && block_start == li_st)) &&
309                         (strncasecmp(li->cmd, "<directory", 10) &&
310                          strncasecmp(li->cmd, "<location", 9) &&
311                          strncasecmp(li->cmd, "<limit", 6) &&
312                          strncasecmp(li->cmd, "</limit", 7) &&
313                          strncasecmp(li->cmd, "</location", 10) &&
314                          strncasecmp(li->cmd, "</directory", 11) &&
315                          strncasecmp(li->cmd, "</files", 7))) {
316                         ap_rputs("<dd><tt>", r);
317                         ap_rputs(mod_info_html_cmd_string(li_st->cmd, buf, sizeof(buf)), r);
318                         ap_rputs(" ", r);
319                         if (li_st->line) {
320                             ap_rputs(mod_info_html_cmd_string(li_st->line, buf, sizeof(buf)), r);
321                         }
322                         ap_rputs("</tt>\n", r);
323                         block_start = li_st;
324                         if (li_se) {
325                             ap_rputs("<dd><tt>&nbsp;&nbsp;", r);
326                             ap_rputs(mod_info_html_cmd_string(li_se->cmd, buf, sizeof(buf)), r);
327                             ap_rputs(" ", r);
328                             if (li_se->line) {
329                                 ap_rputs(mod_info_html_cmd_string(li_se->line, buf, sizeof(buf)), r);
330                             }
331                             ap_rputs("</tt>\n", r);
332                             block_start = li_se;
333                         }
334                     }
335                     ap_rputs("<dd><tt>", r);
336                     if (nest) {
337                         ap_rputs("&nbsp;&nbsp;", r);
338                     }
339                     if (nest == 2) {
340                         ap_rputs("&nbsp;&nbsp;", r);
341                     }
342                     ap_rputs(mod_info_html_cmd_string(li->cmd, buf, sizeof(buf)), r);
343                     if (li->line) {
344                         ap_rputs(" <i>", r);
345                         ap_rputs(mod_info_html_cmd_string(li->line, buf, sizeof(buf)), r);
346                         ap_rputs("</i>", r);
347                     }
348                     ap_rputs("</tt>", r);
349                 }
350             }
351             else
352                 break;
353             cmd++;
354         }
355         li = li->next;
356     }
357 }
358 
find_more_info(server_rec * s,const char * module_name)359 static char *find_more_info(server_rec *s, const char *module_name)
360 {
361     int i;
362     info_svr_conf *conf = (info_svr_conf *) ap_get_module_config(s->module_config,
363                                                               &info_module);
364     info_entry *entry = (info_entry *) conf->more_info->elts;
365 
366     if (!module_name) {
367         return 0;
368     }
369     for (i = 0; i < conf->more_info->nelts; i++) {
370         if (!strcmp(module_name, entry->name)) {
371             return entry->info;
372         }
373         entry++;
374     }
375     return 0;
376 }
377 
mod_info_dirwalk(pool * p,const char * fname,request_rec * r,array_header * carray)378 static void mod_info_dirwalk(pool *p, const char *fname,
379                              request_rec *r, array_header *carray)
380 {
381     info_clines *cnew = NULL;
382     info_cfg_lines *mod_info_cfg_tmp = NULL;
383 
384     if (!ap_is_rdirectory(fname)) {
385         mod_info_cfg_tmp = mod_info_load_config(p, fname, r);
386         cnew = (info_clines *) ap_push_array(carray);
387         cnew->fname = ap_pstrdup(p, fname);
388         cnew->clines = mod_info_cfg_tmp;
389     } else {
390         DIR *dirp;
391         struct DIR_TYPE *dir_entry;
392         int current;
393         array_header *candidates = NULL;
394         info_fnames *fnew;
395 
396         dirp = ap_popendir(p, fname);
397         if (dirp == NULL) {
398             ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
399                     "mod_info: couldn't open config directory %s",
400                     fname);
401             return;
402         }
403         candidates = ap_make_array(p, 1, sizeof(info_fnames));
404         while ((dir_entry = readdir(dirp)) != NULL) {
405             /* strip out '.' and '..' */
406             if (strcmp(dir_entry->d_name, ".") &&
407                 strcmp(dir_entry->d_name, "..")) {
408                 fnew = (info_fnames *) ap_push_array(candidates);
409                 fnew->fname = ap_make_full_path(p, fname, dir_entry->d_name);
410             }
411         }
412         ap_pclosedir(p, dirp);
413         if (candidates->nelts != 0) {
414             qsort((void *) candidates->elts, candidates->nelts,
415               sizeof(info_fnames), fname_alphasort);
416             for (current = 0; current < candidates->nelts; ++current) {
417                 fnew = &((info_fnames *) candidates->elts)[current];
418                 mod_info_dirwalk(p, fnew->fname, r, carray);
419             }
420         }
421     }
422     return;
423 }
424 
display_info(request_rec * r)425 static int display_info(request_rec *r)
426 {
427     module *modp = NULL;
428     char buf[MAX_STRING_LEN], *cfname;
429     char *more_info;
430     const command_rec *cmd = NULL;
431     const handler_rec *hand = NULL;
432     server_rec *serv = r->server;
433     int comma = 0;
434     array_header *allconfigs = NULL;
435     info_clines *cnew = NULL;
436     int current;
437     char *relpath;
438 
439     r->allowed |= (1 << M_GET);
440     if (r->method_number != M_GET)
441         return DECLINED;
442 
443     r->content_type = "text/html";
444     ap_send_http_header(r);
445     if (r->header_only) {
446         return 0;
447     }
448     ap_hard_timeout("send server info", r);
449 
450     ap_rputs(DOCTYPE_HTML_3_2
451              "<html><head><title>Server Information</title></head>\n", r);
452     ap_rputs("<body><h1 align=center>Apache Server Information</h1>\n", r);
453     if (!r->args || strcasecmp(r->args, "list")) {
454         allconfigs = ap_make_array(r->pool, 1, sizeof(info_clines));
455         cfname = ap_server_root_relative(r->pool, ap_server_confname);
456         mod_info_dirwalk(r->pool, cfname, r, allconfigs);
457         cfname = ap_server_root_relative(r->pool, serv->srm_confname);
458         mod_info_dirwalk(r->pool, cfname, r, allconfigs);
459         cfname = ap_server_root_relative(r->pool, serv->access_confname);
460         mod_info_dirwalk(r->pool, cfname, r, allconfigs);
461         if (!r->args) {
462             ap_rputs("<tt><a href=\"#server\">Server Settings</a>, ", r);
463             for (modp = top_module; modp; modp = modp->next) {
464                 ap_rprintf(r, "<a href=\"#%s\">%s</a>", modp->name, modp->name);
465                 if (modp->next) {
466                     ap_rputs(", ", r);
467                 }
468             }
469             ap_rputs("</tt><hr>", r);
470 
471         }
472         if (!r->args || !strcasecmp(r->args, "server")) {
473             ap_rprintf(r, "<a name=\"server\"><strong>Server Version:</strong> "
474                         "<font size=+1><tt>%s</tt></a></font><br>\n",
475                         ap_get_server_version());
476             ap_rprintf(r, "<strong>API Version:</strong> "
477                         "<tt>%d:%d</tt><br>\n",
478                         MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
479             ap_rprintf(r, "<strong>Run Mode:</strong> <tt>%s</tt><br>\n",
480                         (ap_standalone ? "standalone" : "inetd"));
481             ap_rprintf(r, "<strong>User/Group:</strong> "
482                         "<tt>%s(%d)/%d</tt><br>\n",
483                         ap_user_name, (int) ap_user_id, (int) ap_group_id);
484             ap_rprintf(r, "<strong>Hostname/port:</strong> "
485                         "<tt>%s:%u</tt><br>\n",
486                         serv->server_hostname, serv->port);
487             ap_rprintf(r, "<strong>Daemons:</strong> "
488                         "<tt>start: %d &nbsp;&nbsp; "
489                         "min idle: %d &nbsp;&nbsp; "
490                         "max idle: %d &nbsp;&nbsp; "
491                         "max: %d</tt><br>\n",
492                         ap_daemons_to_start, ap_daemons_min_free,
493                         ap_daemons_max_free, ap_daemons_limit);
494             ap_rprintf(r, "<strong>Per-child rlimits:</strong><br>\n"
495                         "<tt>RLIMIT_CPU: %d &nbsp;&nbsp; </tt><br>\n"
496                         "<tt>RLIMIT_DATA: %d &nbsp;&nbsp; </tt><br>\n"
497                         "<tt>RLIMIT_NOFILE: %d &nbsp;&nbsp; </tt><br>\n"
498                         "<tt>RLIMIT_RSS: %d &nbsp;&nbsp; </tt><br>\n"
499                         "<tt>RLIMIT_STACK: %d &nbsp;&nbsp; </tt><br>\n"
500 #ifdef RLIMIT_TIME
501                         "<tt>RLIMIT_TIME: %d &nbsp;&nbsp; </tt><br>\n"
502 #endif
503 			,
504                         ap_max_cpu_per_child, ap_max_data_per_child,
505                         ap_max_nofile_per_child, ap_max_rss_per_child,
506                         ap_max_stack_per_child
507 #ifdef RLIMIT_TIME
508 			, ap_max_time_per_child
509 #endif
510 			);
511             ap_rprintf(r, "<strong>Max Requests:</strong> "
512                         "<tt>per child: %d &nbsp;&nbsp; "
513                         "keep alive: %s &nbsp;&nbsp; "
514                         "max per connection: %d</tt><br>\n",
515                         ap_max_requests_per_child,
516                         (serv->keep_alive ? "on" : "off"),
517                         serv->keep_alive_max);
518             ap_rprintf(r, "<strong>Threads:</strong> "
519                         "<tt>per child: %d &nbsp;&nbsp; </tt><br>\n",
520                         ap_threads_per_child);
521             ap_rprintf(r, "<strong>Excess requests:</strong> "
522                         "<tt>per child: %d &nbsp;&nbsp; </tt><br>\n",
523                         ap_excess_requests_per_child);
524             ap_rprintf(r, "<strong>Timeouts:</strong> "
525                         "<tt>connection: %d &nbsp;&nbsp; "
526                         "keep-alive: %d</tt><br>",
527                         serv->timeout, serv->keep_alive_timeout);
528             ap_rprintf(r, "<strong>Server Root:</strong> "
529                         "<tt>%s</tt><br>\n", ap_server_root);
530             ap_rprintf(r, "<strong>Config File:</strong> "
531                         "<tt>%s</tt><br>\n", ap_server_confname);
532             ap_rprintf(r, "<strong>PID File:</strong> "
533                         "<tt>%s</tt><br>\n", ap_pid_fname);
534             ap_rprintf(r, "<strong>Scoreboard File:</strong> "
535                         "<tt>%s</tt><br>\n", ap_scoreboard_fname);
536         }
537         ap_rputs("<hr><dl>", r);
538         for (modp = top_module; modp; modp = modp->next) {
539             if (!r->args || !strcasecmp(modp->name, r->args)) {
540                 ap_rprintf(r, "<dt><a name=\"%s\"><strong>Module Name:</strong> "
541                             "<font size=+1><tt>%s</tt></a></font>\n",
542                             modp->name, modp->name);
543                 ap_rputs("<dt><strong>Content handlers:</strong>", r);
544                 hand = modp->handlers;
545                 if (hand) {
546                     while (hand) {
547                         if (hand->content_type) {
548                             ap_rprintf(r, " <tt>%s</tt>\n", hand->content_type);
549                         }
550                         else {
551                             break;
552                         }
553                         hand++;
554                         if (hand && hand->content_type) {
555                             ap_rputs(",", r);
556                         }
557                     }
558                 }
559                 else {
560                     ap_rputs("<tt> <EM>none</EM></tt>", r);
561                 }
562                 ap_rputs("<dt><strong>Configuration Phase Participation:</strong> \n",
563                       r);
564                 if (modp->child_init) {
565                     ap_rputs("<tt>Child Init</tt>", r);
566                     comma = 1;
567                 }
568                 if (modp->create_dir_config) {
569                     if (comma) {
570                         ap_rputs(", ", r);
571                     }
572                     ap_rputs("<tt>Create Directory Config</tt>", r);
573                     comma = 1;
574                 }
575                 if (modp->merge_dir_config) {
576                     if (comma) {
577                         ap_rputs(", ", r);
578                     }
579                     ap_rputs("<tt>Merge Directory Configs</tt>", r);
580                     comma = 1;
581                 }
582                 if (modp->create_server_config) {
583                     if (comma) {
584                         ap_rputs(", ", r);
585                     }
586                     ap_rputs("<tt>Create Server Config</tt>", r);
587                     comma = 1;
588                 }
589                 if (modp->merge_server_config) {
590                     if (comma) {
591                         ap_rputs(", ", r);
592                     }
593                     ap_rputs("<tt>Merge Server Configs</tt>", r);
594                     comma = 1;
595                 }
596                 if (modp->child_exit) {
597                     if (comma) {
598                         ap_rputs(", ", r);
599                     }
600                     ap_rputs("<tt>Child Exit</tt>", r);
601                     comma = 1;
602                 }
603                 if (!comma)
604                     ap_rputs("<tt> <EM>none</EM></tt>", r);
605                 comma = 0;
606                 ap_rputs("<dt><strong>Request Phase Participation:</strong> \n",
607                       r);
608                 if (modp->post_read_request) {
609                     ap_rputs("<tt>Post-Read Request</tt>", r);
610                     comma = 1;
611                 }
612                 if (modp->header_parser) {
613                     if (comma) {
614                         ap_rputs(", ", r);
615                     }
616                     ap_rputs("<tt>Header Parse</tt>", r);
617                     comma = 1;
618                 }
619                 if (modp->translate_handler) {
620                     if (comma) {
621                         ap_rputs(", ", r);
622                     }
623                     ap_rputs("<tt>Translate Path</tt>", r);
624                     comma = 1;
625                 }
626                 if (modp->access_checker) {
627                     if (comma) {
628                         ap_rputs(", ", r);
629                     }
630                     ap_rputs("<tt>Check Access</tt>", r);
631                     comma = 1;
632                 }
633                 if (modp->ap_check_user_id) {
634                     if (comma) {
635                         ap_rputs(", ", r);
636                     }
637                     ap_rputs("<tt>Verify User ID</tt>", r);
638                     comma = 1;
639                 }
640                 if (modp->auth_checker) {
641                     if (comma) {
642                         ap_rputs(", ", r);
643                     }
644                     ap_rputs("<tt>Verify User Access</tt>", r);
645                     comma = 1;
646                 }
647                 if (modp->type_checker) {
648                     if (comma) {
649                         ap_rputs(", ", r);
650                     }
651                     ap_rputs("<tt>Check Type</tt>", r);
652                     comma = 1;
653                 }
654                 if (modp->fixer_upper) {
655                     if (comma) {
656                         ap_rputs(", ", r);
657                     }
658                     ap_rputs("<tt>Fixups</tt>", r);
659                     comma = 1;
660                 }
661                 if (modp->logger) {
662                     if (comma) {
663                         ap_rputs(", ", r);
664                     }
665                     ap_rputs("<tt>Logging</tt>", r);
666                     comma = 1;
667                 }
668                 if (!comma)
669                     ap_rputs("<tt> <EM>none</EM></tt>", r);
670                 comma = 0;
671                 ap_rputs("<dt><strong>Module Directives:</strong> ", r);
672                 cmd = modp->cmds;
673                 if (cmd) {
674                     while (cmd) {
675                         if (cmd->name) {
676                             ap_rprintf(r, "<dd><tt>%s - <i>",
677                                     mod_info_html_cmd_string(cmd->name,
678                                         buf, sizeof(buf)));
679                             if (cmd->errmsg) {
680                                 ap_rputs(cmd->errmsg, r);
681                             }
682                             ap_rputs("</i></tt>\n", r);
683                         }
684                         else {
685                             break;
686                         }
687                         cmd++;
688                     }
689                     ap_rputs("<dt><strong>Current Configuration:</strong>\n", r);
690                     for (current = 0; current < allconfigs->nelts; ++current) {
691                         cnew = &((info_clines *) allconfigs->elts)[current];
692                         /* get relative pathname with some safeguards */
693 			relpath = ap_stripprefix(cnew->fname,ap_server_root);
694 			if (*relpath != '\0' && relpath != cnew->fname &&
695                             *relpath == '/')
696                             relpath++;
697                         mod_info_module_cmds(r, cnew->clines, modp->cmds,
698                                              relpath);
699                     }
700                 }
701                 else {
702                     ap_rputs("<tt> none</tt>\n", r);
703                 }
704                 more_info = find_more_info(serv, modp->name);
705                 if (more_info) {
706                     ap_rputs("<dt><strong>Additional Information:</strong>\n<dd>",
707                           r);
708                     ap_rputs(more_info, r);
709                 }
710                 ap_rputs("<dt><hr>\n", r);
711                 if (r->args) {
712                     break;
713                 }
714             }
715         }
716         if (!modp && r->args && strcasecmp(r->args, "server")) {
717             ap_rputs("<b>No such module</b>\n", r);
718         }
719     }
720     else {
721         for (modp = top_module; modp; modp = modp->next) {
722             ap_rputs(modp->name, r);
723             if (modp->next) {
724                 ap_rputs("<br>", r);
725             }
726         }
727     }
728     ap_rputs("</dl>\n", r);
729     ap_rputs(ap_psignature("",r), r);
730     ap_rputs("</body></html>\n", r);
731     /* Done, turn off timeout, close file and return */
732     ap_kill_timeout(r);
733     return 0;
734 }
735 
add_module_info(cmd_parms * cmd,void * dummy,char * name,char * info)736 static const char *add_module_info(cmd_parms *cmd, void *dummy, char *name,
737                                    char *info)
738 {
739     server_rec *s = cmd->server;
740     info_svr_conf *conf = (info_svr_conf *) ap_get_module_config(s->module_config,
741                                                               &info_module);
742     info_entry *new = ap_push_array(conf->more_info);
743 
744     new->name = name;
745     new->info = info;
746     return NULL;
747 }
748 
749 static const command_rec info_cmds[] =
750 {
751     {"AddModuleInfo", add_module_info, NULL, RSRC_CONF, TAKE2,
752      "a module name and additional information on that module"},
753     {NULL}
754 };
755 
756 static const handler_rec info_handlers[] =
757 {
758     {"server-info", display_info},
759     {NULL}
760 };
761 
762 module MODULE_VAR_EXPORT info_module =
763 {
764     STANDARD_MODULE_STUFF,
765     NULL,                       /* initializer */
766     NULL,                       /* dir config creater */
767     NULL,                       /* dir merger --- default is to override */
768     create_info_config,         /* server config */
769     merge_info_config,          /* merge server config */
770     info_cmds,                  /* command table */
771     info_handlers,              /* handlers */
772     NULL,                       /* filename translation */
773     NULL,                       /* check_user_id */
774     NULL,                       /* check auth */
775     NULL,                       /* check access */
776     NULL,                       /* type_checker */
777     NULL,                       /* fixups */
778     NULL,                       /* logger */
779     NULL,                       /* header parser */
780     NULL,                       /* child_init */
781     NULL,                       /* child_exit */
782     NULL                        /* post read-request */
783 };
784 
785