#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "util_script.h"
#include "http_conf_globals.h"
typedef
struct
{
char
*name;
char
*info;
} info_entry;
typedef
struct
{
array_header *more_info;
} info_svr_conf;
typedef
struct
info_cfg_lines {
char
*cmd;
char
*line;
struct
info_cfg_lines *next;
} info_cfg_lines;
typedef
struct
{
char
*fname;
} info_fnames;
typedef
struct
{
info_cfg_lines *clines;
char
*fname;
} info_clines;
module MODULE_VAR_EXPORT info_module;
extern
module API_VAR_EXPORT *top_module;
static
int
fname_alphasort(
const
void
*fn1,
const
void
*fn2)
{
const
info_fnames *f1 = fn1;
const
info_fnames *f2 = fn2;
return
strcmp
(f1->fname,f2->fname);
}
static
void
*create_info_config(pool *p, server_rec *s)
{
info_svr_conf *conf = (info_svr_conf *) ap_pcalloc(p,
sizeof
(info_svr_conf));
conf->more_info = ap_make_array(p, 20,
sizeof
(info_entry));
return
conf;
}
static
void
*merge_info_config(pool *p,
void
*basev,
void
*overridesv)
{
info_svr_conf *
new
= (info_svr_conf *) ap_pcalloc(p,
sizeof
(info_svr_conf));
info_svr_conf *base = (info_svr_conf *) basev;
info_svr_conf *overrides = (info_svr_conf *) overridesv;
new
->more_info = ap_append_arrays(p, overrides->more_info, base->more_info);
return
new
;
}
static
char
*mod_info_html_cmd_string(
const
char
*string,
char
*buf,
size_t
buf_len)
{
const
char
*s;
char
*t;
char
*end_buf;
s = string;
t = buf;
end_buf = buf + buf_len - 1;
while
((*s) && (t < end_buf)) {
if
(*s ==
'<'
) {
strncpy
(t,
"<"
, end_buf - t);
t += 4;
}
else
if
(*s ==
'>'
) {
strncpy
(t,
">"
, end_buf - t);
t += 4;
}
else
if
(*s ==
'&'
) {
strncpy
(t,
"&"
, end_buf - t);
t += 5;
}
else
{
*t++ = *s;
}
s++;
}
if
(t > end_buf) {
*end_buf =
'\0'
;
}
else
{
*t =
'\0'
;
}
return
(buf);
}
static
info_cfg_lines *mod_info_load_config(pool *p,
const
char
*filename,
request_rec *r)
{
char
s[MAX_STRING_LEN];
configfile_t *fp;
info_cfg_lines *
new
, *ret, *prev;
const
char
*t;
fp = ap_pcfg_openfile(p, filename);
if
(!fp) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
"mod_info: couldn't open config file %s"
,
filename);
return
NULL;
}
ret = NULL;
prev = NULL;
while
(!ap_cfg_getline(s, MAX_STRING_LEN, fp)) {
if
(*s ==
'#'
) {
continue
;
}
new
= ap_palloc(p,
sizeof
(
struct
info_cfg_lines));
new
->next = NULL;
if
(!ret) {
ret =
new
;
}
if
(prev) {
prev->next =
new
;
}
t = s;
new
->cmd = ap_getword_conf(p, &t);
if
(*t) {
new
->line = ap_pstrdup(p, t);
}
else
{
new
->line = NULL;
}
prev =
new
;
}
ap_cfg_closefile(fp);
return
(ret);
}
static
void
mod_info_module_cmds(request_rec *r, info_cfg_lines *cfg,
const
command_rec *cmds,
char
*label)
{
const
command_rec *cmd = cmds;
info_cfg_lines *li = cfg, *li_st = NULL, *li_se = NULL;
info_cfg_lines *block_start = NULL;
int
lab = 0, nest = 0;
char
buf[MAX_STRING_LEN];
while
(li) {
if
(!strncasecmp(li->cmd,
"<directory"
, 10) ||
!strncasecmp(li->cmd,
"<location"
, 9) ||
!strncasecmp(li->cmd,
"<limit"
, 6) ||
!strncasecmp(li->cmd,
"<files"
, 6)) {
if
(nest) {
li_se = li;
}
else
{
li_st = li;
}
li = li->next;
nest++;
continue
;
}
else
if
(nest && (!strncasecmp(li->cmd,
"</limit"
, 7) ||
!strncasecmp(li->cmd,
"</location"
, 10) ||
!strncasecmp(li->cmd,
"</directory"
, 11) ||
!strncasecmp(li->cmd,
"</files"
, 7))) {
if
(block_start) {
if
((nest == 1 && block_start == li_st) ||
(nest == 2 && block_start == li_se)) {
ap_rputs(
"<dd><tt>"
, r);
if
(nest == 2) {
ap_rputs(
" "
, r);
}
ap_rputs(mod_info_html_cmd_string(li->cmd, buf,
sizeof
(buf)), r);
ap_rputs(
" "
, r);
if
(li->line) {
ap_rputs(mod_info_html_cmd_string(li->line, buf,
sizeof
(buf)), r);
}
ap_rputs(
"</tt>\n"
, r);
nest--;
if
(!nest) {
block_start = NULL;
li_st = NULL;
}
else
{
block_start = li_st;
}
li_se = NULL;
}
else
{
nest--;
if
(!nest) {
li_st = NULL;
}
li_se = NULL;
}
}
else
{
nest--;
if
(!nest) {
li_st = NULL;
}
li_se = NULL;
}
li = li->next;
continue
;
}
cmd = cmds;
while
(cmd) {
if
(cmd->name) {
if
(!strcasecmp(cmd->name, li->cmd)) {
if
(!lab) {
ap_rputs(
"<dt><strong>"
, r);
ap_rputs(label, r);
ap_rputs(
"</strong>\n"
, r);
lab = 1;
}
if
(((nest && block_start == NULL) ||
(nest == 2 && block_start == li_st)) &&
(strncasecmp(li->cmd,
"<directory"
, 10) &&
strncasecmp(li->cmd,
"<location"
, 9) &&
strncasecmp(li->cmd,
"<limit"
, 6) &&
strncasecmp(li->cmd,
"</limit"
, 7) &&
strncasecmp(li->cmd,
"</location"
, 10) &&
strncasecmp(li->cmd,
"</directory"
, 11) &&
strncasecmp(li->cmd,
"</files"
, 7))) {
ap_rputs(
"<dd><tt>"
, r);
ap_rputs(mod_info_html_cmd_string(li_st->cmd, buf,
sizeof
(buf)), r);
ap_rputs(
" "
, r);
if
(li_st->line) {
ap_rputs(mod_info_html_cmd_string(li_st->line, buf,
sizeof
(buf)), r);
}
ap_rputs(
"</tt>\n"
, r);
block_start = li_st;
if
(li_se) {
ap_rputs(
"<dd><tt> "
, r);
ap_rputs(mod_info_html_cmd_string(li_se->cmd, buf,
sizeof
(buf)), r);
ap_rputs(
" "
, r);
if
(li_se->line) {
ap_rputs(mod_info_html_cmd_string(li_se->line, buf,
sizeof
(buf)), r);
}
ap_rputs(
"</tt>\n"
, r);
block_start = li_se;
}
}
ap_rputs(
"<dd><tt>"
, r);
if
(nest) {
ap_rputs(
" "
, r);
}
if
(nest == 2) {
ap_rputs(
" "
, r);
}
ap_rputs(mod_info_html_cmd_string(li->cmd, buf,
sizeof
(buf)), r);
if
(li->line) {
ap_rputs(
" <i>"
, r);
ap_rputs(mod_info_html_cmd_string(li->line, buf,
sizeof
(buf)), r);
ap_rputs(
"</i>"
, r);
}
ap_rputs(
"</tt>"
, r);
}
}
else
break
;
cmd++;
}
li = li->next;
}
}
static
char
*find_more_info(server_rec *s,
const
char
*module_name)
{
int
i;
info_svr_conf *conf = (info_svr_conf *) ap_get_module_config(s->module_config,
&info_module);
info_entry *entry = (info_entry *) conf->more_info->elts;
if
(!module_name) {
return
0;
}
for
(i = 0; i < conf->more_info->nelts; i++) {
if
(!
strcmp
(module_name, entry->name)) {
return
entry->info;
}
entry++;
}
return
0;
}
static
void
mod_info_dirwalk(pool *p,
const
char
*fname,
request_rec *r, array_header *carray)
{
info_clines *cnew = NULL;
info_cfg_lines *mod_info_cfg_tmp = NULL;
if
(!ap_is_rdirectory(fname)) {
mod_info_cfg_tmp = mod_info_load_config(p, fname, r);
cnew = (info_clines *) ap_push_array(carray);
cnew->fname = ap_pstrdup(p, fname);
cnew->clines = mod_info_cfg_tmp;
}
else
{
DIR *dirp;
struct
DIR_TYPE *dir_entry;
int
current;
array_header *candidates = NULL;
info_fnames *fnew;
dirp = ap_popendir(p, fname);
if
(dirp == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
"mod_info: couldn't open config directory %s"
,
fname);
return
;
}
candidates = ap_make_array(p, 1,
sizeof
(info_fnames));
while
((dir_entry = readdir(dirp)) != NULL) {
if
(
strcmp
(dir_entry->d_name,
"."
) &&
strcmp
(dir_entry->d_name,
".."
)) {
fnew = (info_fnames *) ap_push_array(candidates);
fnew->fname = ap_make_full_path(p, fname, dir_entry->d_name);
}
}
ap_pclosedir(p, dirp);
if
(candidates->nelts != 0) {
qsort
((
void
*) candidates->elts, candidates->nelts,
sizeof
(info_fnames), fname_alphasort);
for
(current = 0; current < candidates->nelts; ++current) {
fnew = &((info_fnames *) candidates->elts)[current];
mod_info_dirwalk(p, fnew->fname, r, carray);
}
}
}
return
;
}
static
int
display_info(request_rec *r)
{
module *modp = NULL;
char
buf[MAX_STRING_LEN], *cfname;
char
*more_info;
const
command_rec *cmd = NULL;
const
handler_rec *hand = NULL;
server_rec *serv = r->server;
int
comma = 0;
array_header *allconfigs = NULL;
info_clines *cnew = NULL;
int
current;
char
*relpath;
r->allowed |= (1 << M_GET);
if
(r->method_number != M_GET)
return
DECLINED;
r->content_type =
"text/html"
;
ap_send_http_header(r);
if
(r->header_only) {
return
0;
}
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1);
#endif
ap_hard_timeout(
"send server info"
, r);
ap_rputs(DOCTYPE_HTML_3_2
"<html><head><title>Server Information</title></head>\n"
, r);
ap_rputs(
"<body><h1 align=center>Apache Server Information</h1>\n"
, r);
if
(!r->args || strcasecmp(r->args,
"list"
)) {
allconfigs = ap_make_array(r->pool, 1,
sizeof
(info_clines));
cfname = ap_server_root_relative(r->pool, ap_server_confname);
mod_info_dirwalk(r->pool, cfname, r, allconfigs);
cfname = ap_server_root_relative(r->pool, serv->srm_confname);
mod_info_dirwalk(r->pool, cfname, r, allconfigs);
cfname = ap_server_root_relative(r->pool, serv->access_confname);
mod_info_dirwalk(r->pool, cfname, r, allconfigs);
if
(!r->args) {
ap_rputs(
"<tt><a href=\"#server\">Server Settings</a>, "
, r);
for
(modp = top_module; modp; modp = modp->next) {
ap_rprintf(r,
"<a href=\"#%s\">%s</a>"
, modp->name, modp->name);
if
(modp->next) {
ap_rputs(
", "
, r);
}
}
ap_rputs(
"</tt><hr>"
, r);
}
if
(!r->args || !strcasecmp(r->args,
"server"
)) {
ap_rprintf(r,
"<a name=\"server\"><strong>Server Version:</strong> "
"<font size=+1><tt>%s</tt></a></font><br>\n"
,
ap_get_server_version());
ap_rprintf(r,
"<strong>Server Built:</strong> "
"<font size=+1><tt>%s</tt></a></font><br>\n"
,
ap_get_server_built());
ap_rprintf(r,
"<strong>API Version:</strong> "
"<tt>%d:%d</tt><br>\n"
,
MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
ap_rprintf(r,
"<strong>Run Mode:</strong> <tt>%s</tt><br>\n"
,
(ap_standalone ?
"standalone"
:
"inetd"
));
ap_rprintf(r,
"<strong>User/Group:</strong> "
"<tt>%s(%d)/%d</tt><br>\n"
,
ap_user_name, (
int
) ap_user_id, (
int
) ap_group_id);
ap_rprintf(r,
"<strong>Hostname/port:</strong> "
"<tt>%s:%u</tt><br>\n"
,
serv->server_hostname, serv->port);
ap_rprintf(r,
"<strong>Daemons:</strong> "
"<tt>start: %d "
"min idle: %d "
"max idle: %d "
"max: %d</tt><br>\n"
,
ap_daemons_to_start, ap_daemons_min_free,
ap_daemons_max_free, ap_daemons_limit);
ap_rprintf(r,
"<strong>Max Requests:</strong> "
"<tt>per child: %d "
"keep alive: %s "
"max per connection: %d</tt><br>\n"
,
ap_max_requests_per_child,
(serv->keep_alive ?
"on"
:
"off"
),
serv->keep_alive_max);
ap_rprintf(r,
"<strong>Threads:</strong> "
"<tt>per child: %d </tt><br>\n"
,
ap_threads_per_child);
ap_rprintf(r,
"<strong>Excess requests:</strong> "
"<tt>per child: %d </tt><br>\n"
,
ap_excess_requests_per_child);
ap_rprintf(r,
"<strong>Timeouts:</strong> "
"<tt>connection: %d "
"keep-alive: %d</tt><br>"
,
serv->timeout, serv->keep_alive_timeout);
ap_rprintf(r,
"<strong>Server Root:</strong> "
"<tt>%s</tt><br>\n"
, ap_server_root);
ap_rprintf(r,
"<strong>Config File:</strong> "
"<tt>%s</tt><br>\n"
, ap_server_confname);
ap_rprintf(r,
"<strong>PID File:</strong> "
"<tt>%s</tt><br>\n"
, ap_pid_fname);
ap_rprintf(r,
"<strong>Scoreboard File:</strong> "
"<tt>%s</tt><br>\n"
, ap_scoreboard_fname);
}
ap_rputs(
"<hr><dl>"
, r);
for
(modp = top_module; modp; modp = modp->next) {
if
(!r->args || !strcasecmp(modp->name, r->args)) {
ap_rprintf(r,
"<dt><a name=\"%s\"><strong>Module Name:</strong> "
"<font size=+1><tt>%s</tt></a></font>\n"
,
modp->name, modp->name);
ap_rputs(
"<dt><strong>Content handlers:</strong>"
, r);
hand = modp->handlers;
if
(hand) {
while
(hand) {
if
(hand->content_type) {
ap_rprintf(r,
" <tt>%s</tt>\n"
, hand->content_type);
}
else
{
break
;
}
hand++;
if
(hand && hand->content_type) {
ap_rputs(
","
, r);
}
}
}
else
{
ap_rputs(
"<tt> <EM>none</EM></tt>"
, r);
}
ap_rputs(
"<dt><strong>Configuration Phase Participation:</strong> \n"
,
r);
if
(modp->child_init) {
ap_rputs(
"<tt>Child Init</tt>"
, r);
comma = 1;
}
if
(modp->create_dir_config) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Create Directory Config</tt>"
, r);
comma = 1;
}
if
(modp->merge_dir_config) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Merge Directory Configs</tt>"
, r);
comma = 1;
}
if
(modp->create_server_config) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Create Server Config</tt>"
, r);
comma = 1;
}
if
(modp->merge_server_config) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Merge Server Configs</tt>"
, r);
comma = 1;
}
if
(modp->child_exit) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Child Exit</tt>"
, r);
comma = 1;
}
if
(!comma)
ap_rputs(
"<tt> <EM>none</EM></tt>"
, r);
comma = 0;
ap_rputs(
"<dt><strong>Request Phase Participation:</strong> \n"
,
r);
if
(modp->post_read_request) {
ap_rputs(
"<tt>Post-Read Request</tt>"
, r);
comma = 1;
}
if
(modp->header_parser) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Header Parse</tt>"
, r);
comma = 1;
}
if
(modp->translate_handler) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Translate Path</tt>"
, r);
comma = 1;
}
if
(modp->access_checker) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Check Access</tt>"
, r);
comma = 1;
}
if
(modp->ap_check_user_id) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Verify User ID</tt>"
, r);
comma = 1;
}
if
(modp->auth_checker) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Verify User Access</tt>"
, r);
comma = 1;
}
if
(modp->type_checker) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Check Type</tt>"
, r);
comma = 1;
}
if
(modp->fixer_upper) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Fixups</tt>"
, r);
comma = 1;
}
if
(modp->logger) {
if
(comma) {
ap_rputs(
", "
, r);
}
ap_rputs(
"<tt>Logging</tt>"
, r);
comma = 1;
}
if
(!comma)
ap_rputs(
"<tt> <EM>none</EM></tt>"
, r);
comma = 0;
ap_rputs(
"<dt><strong>Module Directives:</strong> "
, r);
cmd = modp->cmds;
if
(cmd) {
while
(cmd) {
if
(cmd->name) {
ap_rprintf(r,
"<dd><tt>%s - <i>"
,
mod_info_html_cmd_string(cmd->name,
buf,
sizeof
(buf)));
if
(cmd->errmsg) {
ap_rputs(cmd->errmsg, r);
}
ap_rputs(
"</i></tt>\n"
, r);
}
else
{
break
;
}
cmd++;
}
ap_rputs(
"<dt><strong>Current Configuration:</strong>\n"
, r);
for
(current = 0; current < allconfigs->nelts; ++current) {
cnew = &((info_clines *) allconfigs->elts)[current];
relpath = ap_stripprefix(cnew->fname,ap_server_root);
if
(*relpath !=
'\0'
&& relpath != cnew->fname &&
*relpath ==
'/'
)
relpath++;
mod_info_module_cmds(r, cnew->clines, modp->cmds,
relpath);
}
}
else
{
ap_rputs(
"<tt> none</tt>\n"
, r);
}
more_info = find_more_info(serv, modp->name);
if
(more_info) {
ap_rputs(
"<dt><strong>Additional Information:</strong>\n<dd>"
,
r);
ap_rputs(more_info, r);
}
ap_rputs(
"<dt><hr>\n"
, r);
if
(r->args) {
break
;
}
}
}
if
(!modp && r->args && strcasecmp(r->args,
"server"
)) {
ap_rputs(
"<b>No such module</b>\n"
, r);
}
}
else
{
for
(modp = top_module; modp; modp = modp->next) {
ap_rputs(modp->name, r);
if
(modp->next) {
ap_rputs(
"<br>"
, r);
}
}
}
ap_rputs(
"</dl>\n"
, r);
ap_rputs(ap_psignature(
""
,r), r);
ap_rputs(
"</body></html>\n"
, r);
ap_kill_timeout(r);
return
0;
}
static
const
char
*add_module_info(cmd_parms *cmd,
void
*dummy,
char
*name,
char
*info)
{
server_rec *s = cmd->server;
info_svr_conf *conf = (info_svr_conf *) ap_get_module_config(s->module_config,
&info_module);
info_entry *
new
= ap_push_array(conf->more_info);
new
->name = name;
new
->info = info;
return
NULL;
}
static
const
command_rec info_cmds[] =
{
{
"AddModuleInfo"
, add_module_info, NULL, RSRC_CONF, TAKE2,
"a module name and additional information on that module"
},
{NULL}
};
static
const
handler_rec info_handlers[] =
{
{
"server-info"
, display_info},
{NULL}
};
module MODULE_VAR_EXPORT info_module =
{
STANDARD_MODULE_STUFF,
NULL,
NULL,
NULL,
create_info_config,
merge_info_config,
info_cmds,
info_handlers,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};