Apache 1.3.9 baseline for the Apache 2.0 repository.

Obtained from: Apache 1.3.9 (minus unused files), tag APACHE_1_3_9
Submitted by: Apache Group


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@83751 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roy T. Fielding
1999-08-24 06:55:44 +00:00
parent 6f96ad5227
commit e3e87d34a0
55 changed files with 34461 additions and 0 deletions

1
modules/.cvsignore Normal file
View File

@ -0,0 +1 @@
Makefile

34
modules/README Normal file
View File

@ -0,0 +1,34 @@
The directory structure for this level is as follows:
standard/
In this directory are the standard supported modules for
Apache. Not all are compiled by default.
proxy/
This houses the code for the proxy module for Apache.
experimental/
In this directory we've placed some modules which we think
provide some pretty interesting functionality, but which
are still in the early stages of development and could
evolve radically in the future. This code isn't supported
officially.
extra/
This is the directory for third-party modules, such as mod_jserv.
test/
This directory houses modules which test various components
of Apache. You should not compile these into a production
server.
example/
This directory houses example modules, to help module authors
figure their way around the Apache API and module concept.

6
modules/aaa/.cvsignore Normal file
View File

@ -0,0 +1,6 @@
Makefile
*.lo
*.so
*.dll
*.def
*.exp

410
modules/aaa/mod_access.c Normal file
View File

@ -0,0 +1,410 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* Security options etc.
*
* Module derived from code originally written by Rob McCool
*
*/
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_request.h"
enum allowdeny_type {
T_ENV,
T_ALL,
T_IP,
T_HOST,
T_FAIL
};
typedef struct {
int limited;
union {
char *from;
struct {
unsigned long net;
unsigned long mask;
} ip;
} x;
enum allowdeny_type type;
} allowdeny;
/* things in the 'order' array */
#define DENY_THEN_ALLOW 0
#define ALLOW_THEN_DENY 1
#define MUTUAL_FAILURE 2
typedef struct {
int order[METHODS];
array_header *allows;
array_header *denys;
} access_dir_conf;
module MODULE_VAR_EXPORT access_module;
static void *create_access_dir_config(pool *p, char *dummy)
{
access_dir_conf *conf =
(access_dir_conf *) ap_pcalloc(p, sizeof(access_dir_conf));
int i;
for (i = 0; i < METHODS; ++i)
conf->order[i] = DENY_THEN_ALLOW;
conf->allows = ap_make_array(p, 1, sizeof(allowdeny));
conf->denys = ap_make_array(p, 1, sizeof(allowdeny));
return (void *) conf;
}
static const char *order(cmd_parms *cmd, void *dv, char *arg)
{
access_dir_conf *d = (access_dir_conf *) dv;
int i, o;
if (!strcasecmp(arg, "allow,deny"))
o = ALLOW_THEN_DENY;
else if (!strcasecmp(arg, "deny,allow"))
o = DENY_THEN_ALLOW;
else if (!strcasecmp(arg, "mutual-failure"))
o = MUTUAL_FAILURE;
else
return "unknown order";
for (i = 0; i < METHODS; ++i)
if (cmd->limited & (1 << i))
d->order[i] = o;
return NULL;
}
static int is_ip(const char *host)
{
while ((*host == '.') || ap_isdigit(*host))
host++;
return (*host == '\0');
}
static const char *allow_cmd(cmd_parms *cmd, void *dv, char *from, char *where)
{
access_dir_conf *d = (access_dir_conf *) dv;
allowdeny *a;
char *s;
if (strcasecmp(from, "from"))
return "allow and deny must be followed by 'from'";
a = (allowdeny *) ap_push_array(cmd->info ? d->allows : d->denys);
a->x.from = where;
a->limited = cmd->limited;
if (!strncasecmp(where, "env=", 4)) {
a->type = T_ENV;
a->x.from += 4;
}
else if (!strcasecmp(where, "all")) {
a->type = T_ALL;
}
else if ((s = strchr(where, '/'))) {
unsigned long mask;
a->type = T_IP;
/* trample on where, we won't be using it any more */
*s++ = '\0';
if (!is_ip(where)
|| (a->x.ip.net = ap_inet_addr(where)) == INADDR_NONE) {
a->type = T_FAIL;
return "syntax error in network portion of network/netmask";
}
/* is_ip just tests if it matches [\d.]+ */
if (!is_ip(s)) {
a->type = T_FAIL;
return "syntax error in mask portion of network/netmask";
}
/* is it in /a.b.c.d form? */
if (strchr(s, '.')) {
mask = ap_inet_addr(s);
if (mask == INADDR_NONE) {
a->type = T_FAIL;
return "syntax error in mask portion of network/netmask";
}
}
else {
/* assume it's in /nnn form */
mask = atoi(s);
if (mask > 32 || mask <= 0) {
a->type = T_FAIL;
return "invalid mask in network/netmask";
}
mask = 0xFFFFFFFFUL << (32 - mask);
mask = htonl(mask);
}
a->x.ip.mask = mask;
a->x.ip.net = (a->x.ip.net & mask); /* pjr - This fixes PR 4770 */
}
else if (ap_isdigit(*where) && is_ip(where)) {
/* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
int shift;
char *t;
int octet;
a->type = T_IP;
/* parse components */
s = where;
a->x.ip.net = 0;
a->x.ip.mask = 0;
shift = 24;
while (*s) {
t = s;
if (!ap_isdigit(*t)) {
a->type = T_FAIL;
return "invalid ip address";
}
while (ap_isdigit(*t)) {
++t;
}
if (*t == '.') {
*t++ = 0;
}
else if (*t) {
a->type = T_FAIL;
return "invalid ip address";
}
if (shift < 0) {
return "invalid ip address, only 4 octets allowed";
}
octet = atoi(s);
if (octet < 0 || octet > 255) {
a->type = T_FAIL;
return "each octet must be between 0 and 255 inclusive";
}
a->x.ip.net |= octet << shift;
a->x.ip.mask |= 0xFFUL << shift;
s = t;
shift -= 8;
}
a->x.ip.net = ntohl(a->x.ip.net);
a->x.ip.mask = ntohl(a->x.ip.mask);
}
else {
a->type = T_HOST;
}
return NULL;
}
static char its_an_allow;
static const command_rec access_cmds[] =
{
{"order", order, NULL, OR_LIMIT, TAKE1,
"'allow,deny', 'deny,allow', or 'mutual-failure'"},
{"allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
{"deny", allow_cmd, NULL, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
{NULL}
};
static int in_domain(const char *domain, const char *what)
{
int dl = strlen(domain);
int wl = strlen(what);
if ((wl - dl) >= 0) {
if (strcasecmp(domain, &what[wl - dl]) != 0)
return 0;
/* Make sure we matched an *entire* subdomain --- if the user
* said 'allow from good.com', we don't want people from nogood.com
* to be able to get in.
*/
if (wl == dl)
return 1; /* matched whole thing */
else
return (domain[0] == '.' || what[wl - dl - 1] == '.');
}
else
return 0;
}
static int find_allowdeny(request_rec *r, array_header *a, int method)
{
allowdeny *ap = (allowdeny *) a->elts;
int mmask = (1 << method);
int i;
int gothost = 0;
const char *remotehost = NULL;
for (i = 0; i < a->nelts; ++i) {
if (!(mmask & ap[i].limited))
continue;
switch (ap[i].type) {
case T_ENV:
if (ap_table_get(r->subprocess_env, ap[i].x.from)) {
return 1;
}
break;
case T_ALL:
return 1;
case T_IP:
if (ap[i].x.ip.net != INADDR_NONE
&& (r->connection->remote_addr.sin_addr.s_addr
& ap[i].x.ip.mask) == ap[i].x.ip.net) {
return 1;
}
break;
case T_HOST:
if (!gothost) {
remotehost = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_DOUBLE_REV);
if ((remotehost == NULL) || is_ip(remotehost))
gothost = 1;
else
gothost = 2;
}
if ((gothost == 2) && in_domain(ap[i].x.from, remotehost))
return 1;
break;
case T_FAIL:
/* do nothing? */
break;
}
}
return 0;
}
static int check_dir_access(request_rec *r)
{
int method = r->method_number;
access_dir_conf *a =
(access_dir_conf *)
ap_get_module_config(r->per_dir_config, &access_module);
int ret = OK;
if (a->order[method] == ALLOW_THEN_DENY) {
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
}
else if (a->order[method] == DENY_THEN_ALLOW) {
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
}
else {
if (find_allowdeny(r, a->allows, method)
&& !find_allowdeny(r, a->denys, method))
ret = OK;
else
ret = FORBIDDEN;
}
if (ret == FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client denied by server configuration: %s",
r->filename);
}
return ret;
}
module MODULE_VAR_EXPORT access_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_access_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
access_cmds,
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
check_dir_access, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

333
modules/aaa/mod_auth.c Normal file
View File

@ -0,0 +1,333 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_auth: authentication
*
* Rob McCool
*
* Adapted to Apache by rst.
*
* dirkx - Added Authoritative control to allow passing on to lower
* modules if and only if the user-id is not known to this
* module. A known user with a faulty or absent password still
* causes an AuthRequired. The default is 'Authoritative', i.e.
* no control is passed along.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
typedef struct auth_config_struct {
char *auth_pwfile;
char *auth_grpfile;
int auth_authoritative;
} auth_config_rec;
static void *create_auth_dir_config(pool *p, char *d)
{
auth_config_rec *sec =
(auth_config_rec *) ap_pcalloc(p, sizeof(auth_config_rec));
sec->auth_pwfile = NULL; /* just to illustrate the default really */
sec->auth_grpfile = NULL; /* unless you have a broken HP cc */
sec->auth_authoritative = 1; /* keep the fortress secure by default */
return sec;
}
static const char *set_auth_slot(cmd_parms *cmd, void *offset, char *f, char *t)
{
if (t && strcmp(t, "standard"))
return ap_pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL);
return ap_set_file_slot(cmd, offset, f);
}
static const command_rec auth_cmds[] =
{
{"AuthUserFile", set_auth_slot,
(void *) XtOffsetOf(auth_config_rec, auth_pwfile), OR_AUTHCFG, TAKE12,
"text file containing user IDs and passwords"},
{"AuthGroupFile", set_auth_slot,
(void *) XtOffsetOf(auth_config_rec, auth_grpfile), OR_AUTHCFG, TAKE12,
"text file containing group names and member user IDs"},
{"AuthAuthoritative", ap_set_flag_slot,
(void *) XtOffsetOf(auth_config_rec, auth_authoritative),
OR_AUTHCFG, FLAG,
"Set to 'no' to allow access control to be passed along to lower modules if the UserID is not known to this module"},
{NULL}
};
module MODULE_VAR_EXPORT auth_module;
static char *get_pw(request_rec *r, char *user, char *auth_pwfile)
{
configfile_t *f;
char l[MAX_STRING_LEN];
const char *rpw, *w;
if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"Could not open password file: %s", auth_pwfile);
return NULL;
}
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
if ((l[0] == '#') || (!l[0]))
continue;
rpw = l;
w = ap_getword(r->pool, &rpw, ':');
if (!strcmp(user, w)) {
ap_cfg_closefile(f);
return ap_getword(r->pool, &rpw, ':');
}
}
ap_cfg_closefile(f);
return NULL;
}
static table *groups_for_user(pool *p, char *user, char *grpfile)
{
configfile_t *f;
table *grps = ap_make_table(p, 15);
pool *sp;
char l[MAX_STRING_LEN];
const char *group_name, *ll, *w;
if (!(f = ap_pcfg_openfile(p, grpfile))) {
/*add? aplog_error(APLOG_MARK, APLOG_ERR, NULL,
"Could not open group file: %s", grpfile);*/
return NULL;
}
sp = ap_make_sub_pool(p);
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
if ((l[0] == '#') || (!l[0]))
continue;
ll = l;
ap_clear_pool(sp);
group_name = ap_getword(sp, &ll, ':');
while (ll[0]) {
w = ap_getword_conf(sp, &ll);
if (!strcmp(w, user)) {
ap_table_setn(grps, ap_pstrdup(p, group_name), "in");
break;
}
}
}
ap_cfg_closefile(f);
ap_destroy_pool(sp);
return grps;
}
/* These functions return 0 if client is OK, and proper error status
* if not... either AUTH_REQUIRED, if we made a check, and it failed, or
* SERVER_ERROR, if things are so totally confused that we couldn't
* figure out how to tell if the client is authorized or not.
*
* If they return DECLINED, and all other modules also decline, that's
* treated by the server core as a configuration error, logged and
* reported as such.
*/
/* Determine user ID, and check if it really is that user, for HTTP
* basic authentication...
*/
static int authenticate_basic_user(request_rec *r)
{
auth_config_rec *sec =
(auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_module);
conn_rec *c = r->connection;
const char *sent_pw;
char *real_pw;
char *invalid_pw;
int res;
if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
return res;
if (!sec->auth_pwfile)
return DECLINED;
if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) {
if (!(sec->auth_authoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not found: %s", c->user, r->uri);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
invalid_pw = ap_validate_password(sent_pw, real_pw);
if (invalid_pw != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s: authentication failure for \"%s\": %s",
c->user, r->uri, invalid_pw);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
return OK;
}
/* Checking ID */
static int check_user_access(request_rec *r)
{
auth_config_rec *sec =
(auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_module);
char *user = r->connection->user;
int m = r->method_number;
int method_restricted = 0;
register int x;
const char *t, *w;
table *grpstatus;
const array_header *reqs_arr = ap_requires(r);
require_line *reqs;
/* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive,
* then any user will do.
*/
if (!reqs_arr)
return (OK);
reqs = (require_line *) reqs_arr->elts;
if (sec->auth_grpfile)
grpstatus = groups_for_user(r->pool, user, sec->auth_grpfile);
else
grpstatus = NULL;
for (x = 0; x < reqs_arr->nelts; x++) {
if (!(reqs[x].method_mask & (1 << m)))
continue;
method_restricted = 1;
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if (!strcmp(w, "valid-user"))
return OK;
if (!strcmp(w, "user")) {
while (t[0]) {
w = ap_getword_conf(r->pool, &t);
if (!strcmp(user, w))
return OK;
}
}
else if (!strcmp(w, "group")) {
if (!grpstatus)
return DECLINED; /* DBM group? Something else? */
while (t[0]) {
w = ap_getword_conf(r->pool, &t);
if (ap_table_get(grpstatus, w))
return OK;
}
} else if (sec->auth_authoritative) {
/* if we aren't authoritative, any require directive could be
* valid even if we don't grok it. However, if we are
* authoritative, we can warn the user they did something wrong.
* That something could be a missing "AuthAuthoritative off", but
* more likely is a typo in the require directive.
*/
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"access to %s failed, reason: unknown require directive:"
"\"%s\"", r->uri, reqs[x].requirement);
}
}
if (!method_restricted)
return OK;
if (!(sec->auth_authoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"access to %s failed, reason: user %s not allowed access",
r->uri, user);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
module MODULE_VAR_EXPORT auth_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_auth_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
auth_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
authenticate_basic_user, /* check_user_id */
check_user_access, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

315
modules/aaa/mod_auth_anon.c Normal file
View File

@ -0,0 +1,315 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_auth: authentication
*
* Rob McCool & Brian Behlendorf.
*
* Adapted to Apache by rst.
*
* Version 0.5 May 1996
*
* Modified by Dirk.vanGulik@jrc.it to
*
* Adapted to allow anonymous logins, just like with Anon-FTP, when
* one gives the magic user name 'anonymous' and ones email address
* as the password.
*
* Just add the following tokes to your <directory> setup:
*
* Anonymous magic-user-id [magic-user-id]...
*
* Anonymous_MustGiveEmail [ on | off ] default = off
* Anonymous_LogEmail [ on | off ] default = on
* Anonymous_VerifyEmail [ on | off ] default = off
* Anonymous_NoUserId [ on | off ] default = off
* Anonymous_Authoritative [ on | off ] default = off
*
* The magic user id is something like 'anonymous', it is NOT case sensitive.
*
* The MustGiveEmail flag can be used to force users to enter something
* in the password field (like an email address). Default is off.
*
* Furthermore the 'NoUserID' flag can be set to allow completely empty
* usernames in as well; this can be is convenient as a single return
* in broken GUIs like W95 is often given by the user. The Default is off.
*
* Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx
*
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
typedef struct auth_anon {
char *password;
struct auth_anon *next;
} auth_anon;
typedef struct {
auth_anon *auth_anon_passwords;
int auth_anon_nouserid;
int auth_anon_logemail;
int auth_anon_verifyemail;
int auth_anon_mustemail;
int auth_anon_authoritative;
} anon_auth_config_rec;
static void *create_anon_auth_dir_config(pool *p, char *d)
{
anon_auth_config_rec *sec = (anon_auth_config_rec *)
ap_pcalloc(p, sizeof(anon_auth_config_rec));
if (!sec)
return NULL; /* no memory... */
/* just to illustrate the defaults really. */
sec->auth_anon_passwords = NULL;
sec->auth_anon_nouserid = 0;
sec->auth_anon_logemail = 1;
sec->auth_anon_verifyemail = 0;
sec->auth_anon_mustemail = 1;
sec->auth_anon_authoritative = 0;
return sec;
}
static const char *anon_set_passwd_flag(cmd_parms *cmd,
anon_auth_config_rec * sec, int arg)
{
sec->auth_anon_mustemail = arg;
return NULL;
}
static const char *anon_set_userid_flag(cmd_parms *cmd,
anon_auth_config_rec * sec, int arg)
{
sec->auth_anon_nouserid = arg;
return NULL;
}
static const char *anon_set_logemail_flag(cmd_parms *cmd,
anon_auth_config_rec * sec, int arg)
{
sec->auth_anon_logemail = arg;
return NULL;
}
static const char *anon_set_verifyemail_flag(cmd_parms *cmd,
anon_auth_config_rec * sec, int arg)
{
sec->auth_anon_verifyemail = arg;
return NULL;
}
static const char *anon_set_authoritative_flag(cmd_parms *cmd,
anon_auth_config_rec * sec, int arg)
{
sec->auth_anon_authoritative = arg;
return NULL;
}
static const char *anon_set_string_slots(cmd_parms *cmd,
anon_auth_config_rec * sec, char *arg)
{
auth_anon *first;
if (!(*arg))
return "Anonymous string cannot be empty, use Anonymous_NoUserId instead";
/* squeeze in a record */
first = sec->auth_anon_passwords;
if (
(!(sec->auth_anon_passwords = (auth_anon *) ap_palloc(cmd->pool, sizeof(auth_anon)))) ||
(!(sec->auth_anon_passwords->password = arg))
)
return "Failed to claim memory for an anonymous password...";
/* and repair the next */
sec->auth_anon_passwords->next = first;
return NULL;
}
static const command_rec anon_auth_cmds[] =
{
{"Anonymous", anon_set_string_slots, NULL, OR_AUTHCFG, ITERATE,
"a space-separated list of user IDs"},
{"Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_AUTHCFG, FLAG,
"Limited to 'on' or 'off'"},
{"Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_AUTHCFG, FLAG,
"Limited to 'on' or 'off'"},
{"Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_AUTHCFG, FLAG,
"Limited to 'on' or 'off'"},
{"Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_AUTHCFG, FLAG,
"Limited to 'on' or 'off'"},
{"Anonymous_Authoritative", anon_set_authoritative_flag, NULL, OR_AUTHCFG, FLAG,
"Limited to 'on' or 'off'"},
{NULL}
};
module MODULE_VAR_EXPORT anon_auth_module;
static int anon_authenticate_basic_user(request_rec *r)
{
anon_auth_config_rec *sec =
(anon_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&anon_auth_module);
conn_rec *c = r->connection;
const char *sent_pw;
int res = DECLINED;
if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
return res;
/* Ignore if we are not configured */
if (!sec->auth_anon_passwords)
return DECLINED;
/* Do we allow an empty userID and/or is it the magic one
*/
if ((!(c->user[0])) && (sec->auth_anon_nouserid)) {
res = OK;
}
else {
auth_anon *p = sec->auth_anon_passwords;
res = DECLINED;
while ((res == DECLINED) && (p != NULL)) {
if (!(strcasecmp(c->user, p->password)))
res = OK;
p = p->next;
}
}
if (
/* username is OK */
(res == OK)
/* password been filled out ? */
&& ((!sec->auth_anon_mustemail) || strlen(sent_pw))
/* does the password look like an email address ? */
&& ((!sec->auth_anon_verifyemail)
|| ((strpbrk("@", sent_pw) != NULL)
&& (strpbrk(".", sent_pw) != NULL)))) {
if (sec->auth_anon_logemail && ap_is_initial_req(r)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
"Anonymous: Passwd <%s> Accepted",
sent_pw ? sent_pw : "\'none\'");
}
return OK;
}
else {
if (sec->auth_anon_authoritative) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Anonymous: Authoritative, Passwd <%s> not accepted",
sent_pw ? sent_pw : "\'none\'");
return AUTH_REQUIRED;
}
/* Drop out the bottom to return DECLINED */
}
return DECLINED;
}
static int check_anon_access(request_rec *r)
{
#ifdef NOTYET
conn_rec *c = r->connection;
anon_auth_config_rec *sec =
(anon_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&anon_auth_module);
if (!sec->auth_anon)
return DECLINED;
if (strcasecmp(r->connection->user, sec->auth_anon))
return DECLINED;
return OK;
#endif
return DECLINED;
}
module MODULE_VAR_EXPORT anon_auth_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_anon_auth_dir_config, /* dir config creater */
NULL, /* dir merger ensure strictness */
NULL, /* server config */
NULL, /* merge server config */
anon_auth_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
anon_authenticate_basic_user, /* check_user_id */
check_anon_access, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

347
modules/aaa/mod_auth_db.c Normal file
View File

@ -0,0 +1,347 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_auth_db: authentication
*
* Original work by Rob McCool & Brian Behlendorf.
*
* Adapted to Apache by rst (mod_auth_dbm)
*
* Adapted for Berkeley DB by Andrew Cohen
*
* mod_auth_db was based on mod_auth_dbm.
*
* Warning, this is not a drop in replacement for mod_auth_dbm,
* for people wanting to switch from dbm to Berkeley DB.
* It requires the use of AuthDBUserFile and AuthDBGroupFile
* instead of AuthDBMUserFile AuthDBMGroupFile
*
* Also, in the configuration file you need to specify
* db_auth_module rather than dbm_auth_module
*
* On some BSD systems (e.g. FreeBSD and NetBSD) dbm is automatically
* mapped to Berkeley DB. You can use either mod_auth_dbm or
* mod_auth_db. The latter makes it more obvious that it's Berkeley.
* On other platforms where you want to use the DB library you
* usually have to install it first. See http://www.sleepycat.com/
* for the distribution. The interface this module uses is the
* one from DB version 1.85 and 1.86, but DB version 2.x
* can also be used when compatibility mode is enabled.
*
* dirkx - Added Authoritative control to allow passing on to lower
* modules if and only if the user-id is not known to this
* module. A known user with a faulty or absent password still
* causes an AuthRequired. The default is 'Authoritative', i.e.
* no control is passed along.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include <db.h>
#if defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 2)
#define DB2
#endif
typedef struct {
char *auth_dbpwfile;
char *auth_dbgrpfile;
int auth_dbauthoritative;
} db_auth_config_rec;
static void *create_db_auth_dir_config(pool *p, char *d)
{
db_auth_config_rec *sec
= (db_auth_config_rec *) ap_pcalloc(p, sizeof(db_auth_config_rec));
sec->auth_dbpwfile = NULL;
sec->auth_dbgrpfile = NULL;
sec->auth_dbauthoritative = 1; /* fortress is secure by default */
return sec;
}
static const char *set_db_slot(cmd_parms *cmd, void *offset, char *f, char *t)
{
if (!t || strcmp(t, "db"))
return DECLINE_CMD;
return ap_set_file_slot(cmd, offset, f);
}
static const command_rec db_auth_cmds[] =
{
{"AuthDBUserFile", ap_set_file_slot,
(void *) XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
OR_AUTHCFG, TAKE1, NULL},
{"AuthDBGroupFile", ap_set_file_slot,
(void *) XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
OR_AUTHCFG, TAKE1, NULL},
{"AuthUserFile", set_db_slot,
(void *) XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
OR_AUTHCFG, TAKE12, NULL},
{"AuthGroupFile", set_db_slot,
(void *) XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
OR_AUTHCFG, TAKE12, NULL},
{"AuthDBAuthoritative", ap_set_flag_slot,
(void *) XtOffsetOf(db_auth_config_rec, auth_dbauthoritative),
OR_AUTHCFG, FLAG,
"Set to 'no' to allow access control to be passed along to lower modules if the userID is not known to this module"},
{NULL}
};
module db_auth_module;
static char *get_db_pw(request_rec *r, char *user, const char *auth_dbpwfile)
{
DB *f;
DBT d, q;
char *pw = NULL;
memset(&d, 0, sizeof(d));
memset(&q, 0, sizeof(q));
q.data = user;
q.size = strlen(q.data);
#ifdef DB2
if (db_open(auth_dbpwfile, DB_HASH, DB_RDONLY, 0664, NULL, NULL, &f) != 0) {
#else
if (!(f = dbopen(auth_dbpwfile, O_RDONLY, 0664, DB_HASH, NULL))) {
#endif
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"could not open db auth file: %s", auth_dbpwfile);
return NULL;
}
#ifdef DB2
if (!((f->get) (f, NULL, &q, &d, 0))) {
#else
if (!((f->get) (f, &q, &d, 0))) {
#endif
pw = ap_palloc(r->pool, d.size + 1);
strncpy(pw, d.data, d.size);
pw[d.size] = '\0'; /* Terminate the string */
}
#ifdef DB2
(f->close) (f, 0);
#else
(f->close) (f);
#endif
return pw;
}
/* We do something strange with the group file. If the group file
* contains any : we assume the format is
* key=username value=":"groupname [":"anything here is ignored]
* otherwise we now (0.8.14+) assume that the format is
* key=username value=groupname
* The first allows the password and group files to be the same
* physical DB file; key=username value=password":"groupname[":"anything]
*
* mark@telescope.org, 22Sep95
*/
static char *get_db_grp(request_rec *r, char *user, const char *auth_dbgrpfile)
{
char *grp_data = get_db_pw(r, user, auth_dbgrpfile);
char *grp_colon;
char *grp_colon2;
if (grp_data == NULL)
return NULL;
if ((grp_colon = strchr(grp_data, ':')) != NULL) {
grp_colon2 = strchr(++grp_colon, ':');
if (grp_colon2)
*grp_colon2 = '\0';
return grp_colon;
}
return grp_data;
}
static int db_authenticate_basic_user(request_rec *r)
{
db_auth_config_rec *sec =
(db_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&db_auth_module);
conn_rec *c = r->connection;
const char *sent_pw;
char *real_pw, *colon_pw;
char *invalid_pw;
int res;
if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
return res;
if (!sec->auth_dbpwfile)
return DECLINED;
if (!(real_pw = get_db_pw(r, c->user, sec->auth_dbpwfile))) {
if (!(sec->auth_dbauthoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"DB user %s not found: %s", c->user, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
/* Password is up to first : if exists */
colon_pw = strchr(real_pw, ':');
if (colon_pw) {
*colon_pw = '\0';
}
invalid_pw = ap_validate_password(sent_pw, real_pw);
if (invalid_pw != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"DB user %s: authentication failure for \"%s\": %s",
c->user, r->uri, invalid_pw);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
return OK;
}
/* Checking ID */
static int db_check_auth(request_rec *r)
{
db_auth_config_rec *sec =
(db_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&db_auth_module);
char *user = r->connection->user;
int m = r->method_number;
const array_header *reqs_arr = ap_requires(r);
require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
register int x;
const char *t;
char *w;
if (!sec->auth_dbgrpfile)
return DECLINED;
if (!reqs_arr)
return DECLINED;
for (x = 0; x < reqs_arr->nelts; x++) {
if (!(reqs[x].method_mask & (1 << m)))
continue;
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if (!strcmp(w, "group") && sec->auth_dbgrpfile) {
const char *orig_groups, *groups;
char *v;
if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) {
if (!(sec->auth_dbauthoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not in DB group file %s: %s",
user, sec->auth_dbgrpfile, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
orig_groups = groups;
while (t[0]) {
w = ap_getword_white(r->pool, &t);
groups = orig_groups;
while (groups[0]) {
v = ap_getword(r->pool, &groups, ',');
if (!strcmp(v, w))
return OK;
}
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not in right group: %s", user, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
}
return DECLINED;
}
module db_auth_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_db_auth_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
db_auth_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
db_authenticate_basic_user, /* check_user_id */
db_check_auth, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,36 @@
Name: db_auth_module
ConfigStart
DB_VERSION=''
DB_LIB=''
if ./helpers/TestCompile func db_open; then
DB_VERSION='Berkeley-DB/2.x'
else
if ./helpers/TestCompile lib db db_open; then
DB_VERSION='Berkeley-DB/2.x'
DB_LIB='-ldb'
else
if ./helpers/TestCompile func dbopen; then
DB_VERSION='Berkeley-DB/1.x'
else
if ./helpers/TestCompile lib db dbopen; then
DB_VERSION='Berkeley-DB/1.x'
DB_LIB='-ldb'
fi
fi
fi
fi
if [ ".$DB_VERSION" != . ]; then
if [ ".$DB_LIB" != . ]; then
LIBS="$LIBS $DB_LIB"
echo " using $DB_VERSION for mod_auth_db ($DB_LIB)"
else
echo " using $DB_VERSION for mod_auth_db (-lc)"
fi
else
echo "Error: Neither Berkeley-DB/1.x nor Berkeley-DB/2.x library found."
echo " Either disable mod_auth_db or provide us with the paths"
echo " to the Berkeley-DB include and library files."
echo " (Hint: INCLUDES, LDFLAGS, LIBS)"
exit 1
fi
ConfigEnd

335
modules/aaa/mod_auth_dbm.c Normal file
View File

@ -0,0 +1,335 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_auth: authentication
*
* Rob McCool & Brian Behlendorf.
*
* Adapted to Apache by rst.
*
* dirkx - Added Authoritative control to allow passing on to lower
* modules if and only if the user-id is not known to this
* module. A known user with a faulty or absent password still
* causes an AuthRequired. The default is 'Authoritative', i.e.
* no control is passed along.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
&& __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
#include <db1/ndbm.h>
#else
#include <ndbm.h>
#endif
/*
* Module definition information - the part between the -START and -END
* lines below is used by Configure. This could be stored in a separate
* instead.
*
* MODULE-DEFINITION-START
* Name: dbm_auth_module
* ConfigStart
. ./helpers/find-dbm-lib
* ConfigEnd
* MODULE-DEFINITION-END
*/
typedef struct {
char *auth_dbmpwfile;
char *auth_dbmgrpfile;
int auth_dbmauthoritative;
} dbm_auth_config_rec;
static void *create_dbm_auth_dir_config(pool *p, char *d)
{
dbm_auth_config_rec *sec
= (dbm_auth_config_rec *) ap_pcalloc(p, sizeof(dbm_auth_config_rec));
sec->auth_dbmpwfile = NULL;
sec->auth_dbmgrpfile = NULL;
sec->auth_dbmauthoritative = 1; /* fortress is secure by default */
return sec;
}
static const char *set_dbm_slot(cmd_parms *cmd, void *offset, char *f, char *t)
{
if (!t || strcmp(t, "dbm"))
return DECLINE_CMD;
return ap_set_file_slot(cmd, offset, f);
}
static const command_rec dbm_auth_cmds[] =
{
{"AuthDBMUserFile", ap_set_file_slot,
(void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
OR_AUTHCFG, TAKE1, NULL},
{"AuthDBMGroupFile", ap_set_file_slot,
(void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
OR_AUTHCFG, TAKE1, NULL},
{"AuthUserFile", set_dbm_slot,
(void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
OR_AUTHCFG, TAKE12, NULL},
{"AuthGroupFile", set_dbm_slot,
(void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
OR_AUTHCFG, TAKE12, NULL},
{"AuthDBMAuthoritative", ap_set_flag_slot,
(void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmauthoritative),
OR_AUTHCFG, FLAG, "Set to 'no' to allow access control to be passed along to lower modules, if the UserID is not known in this module"},
{NULL}
};
module dbm_auth_module;
static char *get_dbm_pw(request_rec *r, char *user, char *auth_dbmpwfile)
{
DBM *f;
datum d, q;
char *pw = NULL;
q.dptr = user;
#ifndef NETSCAPE_DBM_COMPAT
q.dsize = strlen(q.dptr);
#else
q.dsize = strlen(q.dptr) + 1;
#endif
if (!(f = dbm_open(auth_dbmpwfile, O_RDONLY, 0664))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"could not open dbm auth file: %s", auth_dbmpwfile);
return NULL;
}
d = dbm_fetch(f, q);
if (d.dptr) {
pw = ap_palloc(r->pool, d.dsize + 1);
strncpy(pw, d.dptr, d.dsize);
pw[d.dsize] = '\0'; /* Terminate the string */
}
dbm_close(f);
return pw;
}
/* We do something strange with the group file. If the group file
* contains any : we assume the format is
* key=username value=":"groupname [":"anything here is ignored]
* otherwise we now (0.8.14+) assume that the format is
* key=username value=groupname
* The first allows the password and group files to be the same
* physical DBM file; key=username value=password":"groupname[":"anything]
*
* mark@telescope.org, 22Sep95
*/
static char *get_dbm_grp(request_rec *r, char *user, char *auth_dbmgrpfile)
{
char *grp_data = get_dbm_pw(r, user, auth_dbmgrpfile);
char *grp_colon;
char *grp_colon2;
if (grp_data == NULL)
return NULL;
if ((grp_colon = strchr(grp_data, ':')) != NULL) {
grp_colon2 = strchr(++grp_colon, ':');
if (grp_colon2)
*grp_colon2 = '\0';
return grp_colon;
}
return grp_data;
}
static int dbm_authenticate_basic_user(request_rec *r)
{
dbm_auth_config_rec *sec =
(dbm_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&dbm_auth_module);
conn_rec *c = r->connection;
const char *sent_pw;
char *real_pw, *colon_pw;
char *invalid_pw;
int res;
if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
return res;
if (!sec->auth_dbmpwfile)
return DECLINED;
if (!(real_pw = get_dbm_pw(r, c->user, sec->auth_dbmpwfile))) {
if (!(sec->auth_dbmauthoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"DBM user %s not found: %s", c->user, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
/* Password is up to first : if exists */
colon_pw = strchr(real_pw, ':');
if (colon_pw) {
*colon_pw = '\0';
}
invalid_pw = ap_validate_password(sent_pw, real_pw);
if (invalid_pw != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"DBM user %s: authentication failure for \"%s\": %s",
c->user, r->uri, invalid_pw);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
return OK;
}
/* Checking ID */
static int dbm_check_auth(request_rec *r)
{
dbm_auth_config_rec *sec =
(dbm_auth_config_rec *) ap_get_module_config(r->per_dir_config,
&dbm_auth_module);
char *user = r->connection->user;
int m = r->method_number;
const array_header *reqs_arr = ap_requires(r);
require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
register int x;
const char *t;
char *w;
if (!sec->auth_dbmgrpfile)
return DECLINED;
if (!reqs_arr)
return DECLINED;
for (x = 0; x < reqs_arr->nelts; x++) {
if (!(reqs[x].method_mask & (1 << m)))
continue;
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if (!strcmp(w, "group") && sec->auth_dbmgrpfile) {
const char *orig_groups, *groups;
char *v;
if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) {
if (!(sec->auth_dbmauthoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not in DBM group file %s: %s",
user, sec->auth_dbmgrpfile, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
orig_groups = groups;
while (t[0]) {
w = ap_getword_white(r->pool, &t);
groups = orig_groups;
while (groups[0]) {
v = ap_getword(r->pool, &groups, ',');
if (!strcmp(v, w))
return OK;
}
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not in right group: %s",
user, r->filename);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
}
return DECLINED;
}
module dbm_auth_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_dbm_auth_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
dbm_auth_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
dbm_authenticate_basic_user, /* check_user_id */
dbm_check_auth, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
Makefile
*.lo
*.so

View File

@ -0,0 +1,53 @@
README for Apache 1.2 Example Module
[April, 1997]
The files in the src/modules/example directory under the Apache
distribution directory tree are provided as an example to those that
wish to write modules that use the Apache API.
The main file is mod_example.c, which illustrates all the different
callback mechanisms and call syntaces. By no means does an add-on
module need to include routines for all of the callbacks - quite the
contrary!
The example module is an actual working module. If you link it into
your server, enable the "example-handler" handler for a location, and then
browse to that location, you will see a display of some of the tracing
the example module did as the various callbacks were made.
To include the example module in your server, follow the steps below:
1. Uncomment the "Module example_module" line near the bottom of
the src/Configuration file. If there isn't one, add it; it
should look like this:
Module example_module modules/example/mod_example.o
2. Run the src/Configure script ("cd src; ./Configure"). This will
build the Makefile for the server itself, and update the
src/modules/Makefile for any additional modules you have
requested from beneath that subdirectory.
3. Make the server (run "make" in the src directory).
To add another module of your own:
A. mkdir src/modules/mymodule
B. cp src/modules/example/* src/modules/mymodule
C. Modify the files in the new directory
D. Follow steps [1] through [3] above, with appropriate changes.
To activate the example module, include a block similar to the
following in your srm.conf file:
<Location /example-info>
SetHandler example-handler
</Location>
As an alternative, you can put the following into a .htaccess file and
then request the file "test.example" from that location:
AddHandler example-handler .example
After reloading/restarting your server, you should be able to browse
to this location and see the brief display mentioned earlier.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,145 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "util_script.h"
#include "http_main.h"
#include "http_request.h"
static int asis_handler(request_rec *r)
{
FILE *f;
const char *location;
r->allowed |= (1 << M_GET);
if (r->method_number != M_GET)
return DECLINED;
if (r->finfo.st_mode == 0) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"File does not exist: %s", r->filename);
return NOT_FOUND;
}
f = ap_pfopen(r->pool, r->filename, "r");
if (f == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"file permissions deny server access: %s", r->filename);
return FORBIDDEN;
}
ap_scan_script_header_err(r, f, NULL);
location = ap_table_get(r->headers_out, "Location");
if (location && location[0] == '/' &&
((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) {
ap_pfclose(r->pool, f);
/* Internal redirect -- fake-up a pseudo-request */
r->status = HTTP_OK;
/* This redirect needs to be a GET no matter what the original
* method was.
*/
r->method = ap_pstrdup(r->pool, "GET");
r->method_number = M_GET;
ap_internal_redirect_handler(location, r);
return OK;
}
ap_send_http_header(r);
if (!r->header_only)
ap_send_fd(f, r);
ap_pfclose(r->pool, f);
return OK;
}
static const handler_rec asis_handlers[] =
{
{ASIS_MAGIC_TYPE, asis_handler},
{"send-as-is", asis_handler},
{NULL}
};
module MODULE_VAR_EXPORT asis_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command table */
asis_handlers, /* handlers */
NULL, /* translate_handler */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* pre-run fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,609 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_script: keeps all script-related ramblings together.
*
* Compliant to CGI/1.1 spec
*
* Adapted by rst from original NCSA code by Rob McCool
*
* Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
* custom error responses, and DOCUMENT_ROOT because we found it useful.
* It also adds SERVER_ADMIN - useful for scripts to know who to mail when
* they fail.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
#include "http_conf_globals.h"
module MODULE_VAR_EXPORT cgi_module;
/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
* in ScriptAliased directories, which means we need to know if this
* request came through ScriptAlias or not... so the Alias module
* leaves a note for us.
*/
static int is_scriptaliased(request_rec *r)
{
const char *t = ap_table_get(r->notes, "alias-forced-type");
return t && (!strcasecmp(t, "cgi-script"));
}
/* Configuration stuff */
#define DEFAULT_LOGBYTES 10385760
#define DEFAULT_BUFBYTES 1024
typedef struct {
char *logname;
long logbytes;
int bufbytes;
} cgi_server_conf;
static void *create_cgi_config(pool *p, server_rec *s)
{
cgi_server_conf *c =
(cgi_server_conf *) ap_pcalloc(p, sizeof(cgi_server_conf));
c->logname = NULL;
c->logbytes = DEFAULT_LOGBYTES;
c->bufbytes = DEFAULT_BUFBYTES;
return c;
}
static void *merge_cgi_config(pool *p, void *basev, void *overridesv)
{
cgi_server_conf *base = (cgi_server_conf *) basev, *overrides = (cgi_server_conf *) overridesv;
return overrides->logname ? overrides : base;
}
static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
{
server_rec *s = cmd->server;
cgi_server_conf *conf =
(cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
conf->logname = arg;
return NULL;
}
static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg)
{
server_rec *s = cmd->server;
cgi_server_conf *conf =
(cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
conf->logbytes = atol(arg);
return NULL;
}
static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, char *arg)
{
server_rec *s = cmd->server;
cgi_server_conf *conf =
(cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
conf->bufbytes = atoi(arg);
return NULL;
}
static const command_rec cgi_cmds[] =
{
{"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
"the name of a log for script debugging info"},
{"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1,
"the maximum length (in bytes) of the script debug log"},
{"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
"the maximum size (in bytes) to record of a POST request"},
{NULL}
};
static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
int show_errno, char *error)
{
FILE *f;
struct stat finfo;
ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r,
"%s: %s", error, r->filename);
if (!conf->logname ||
((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
&& (finfo.st_size > conf->logbytes)) ||
((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
"a")) == NULL)) {
return ret;
}
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
/* "%% 500 /usr/local/apache/cgi-bin */
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fprintf(f, "%%error\n%s\n", error);
ap_pfclose(r->pool, f);
return ret;
}
static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err)
{
array_header *hdrs_arr = ap_table_elts(r->headers_in);
table_entry *hdrs = (table_entry *) hdrs_arr->elts;
char argsbuffer[HUGE_STRING_LEN];
FILE *f;
int i;
struct stat finfo;
if (!conf->logname ||
((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
&& (finfo.st_size > conf->logbytes)) ||
((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
"a")) == NULL)) {
/* Soak up script output */
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
continue;
#ifdef WIN32
/* Soak up stderr and redirect it to the error log.
* Script output to stderr is already directed to the error log
* on Unix, thanks to the magic of fork().
*/
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
"%s", argsbuffer);
}
#else
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
continue;
#endif
return ret;
}
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
/* "%% 500 /usr/local/apache/cgi-bin" */
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fputs("%request\n", f);
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key)
continue;
fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
}
if ((r->method_number == M_POST || r->method_number == M_PUT)
&& *dbuf) {
fprintf(f, "\n%s\n", dbuf);
}
fputs("%response\n", f);
hdrs_arr = ap_table_elts(r->err_headers_out);
hdrs = (table_entry *) hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key)
continue;
fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
}
if (sbuf && *sbuf)
fprintf(f, "%s\n", sbuf);
if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
fputs("%stdout\n", f);
fputs(argsbuffer, f);
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
fputs(argsbuffer, f);
fputs("\n", f);
}
if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
fputs("%stderr\n", f);
fputs(argsbuffer, f);
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
fputs(argsbuffer, f);
fputs("\n", f);
}
ap_bclose(script_in);
ap_bclose(script_err);
ap_pfclose(r->pool, f);
return ret;
}
/****************************************************************
*
* Actual CGI handling...
*/
struct cgi_child_stuff {
#ifdef TPF
TPF_FORK_CHILD t;
#endif
request_rec *r;
int nph;
int debug;
char *argv0;
};
static int cgi_child(void *child_stuff, child_info *pinfo)
{
struct cgi_child_stuff *cld = (struct cgi_child_stuff *) child_stuff;
request_rec *r = cld->r;
char *argv0 = cld->argv0;
int child_pid;
#ifdef DEBUG_CGI
#ifdef OS2
/* Under OS/2 need to use device con. */
FILE *dbg = fopen("con", "w");
#else
FILE *dbg = fopen("/dev/tty", "w");
#endif
int i;
#endif
char **env;
RAISE_SIGSTOP(CGI_CHILD);
#ifdef DEBUG_CGI
fprintf(dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
r->filename, cld->nph ? "NPH " : "", argv0);
#endif
ap_add_cgi_vars(r);
env = ap_create_environment(r->pool, r->subprocess_env);
#ifdef DEBUG_CGI
fprintf(dbg, "Environment: \n");
for (i = 0; env[i]; ++i)
fprintf(dbg, "'%s'\n", env[i]);
#endif
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
if (!cld->debug)
ap_error_log2stderr(r->server);
/* Transumute outselves into the script.
* NB only ISINDEX scripts get decoded arguments.
*/
#ifdef TPF
return (0);
#else
ap_cleanup_for_exec();
child_pid = ap_call_exec(r, pinfo, argv0, env, 0);
#if defined(WIN32) || defined(OS2)
return (child_pid);
#else
/* Uh oh. Still here. Where's the kaboom? There was supposed to be an
* EARTH-shattering kaboom!
*
* Oh, well. Muddle through as best we can...
*
* Note that only stderr is available at this point, so don't pass in
* a server to aplog_error.
*/
ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename);
exit(0);
/* NOT REACHED */
return (0);
#endif
#endif /* TPF */
}
static int cgi_handler(request_rec *r)
{
int retval, nph, dbpos = 0;
char *argv0, *dbuf = NULL;
BUFF *script_out, *script_in, *script_err;
char argsbuffer[HUGE_STRING_LEN];
int is_included = !strcmp(r->protocol, "INCLUDED");
void *sconf = r->server->module_config;
cgi_server_conf *conf =
(cgi_server_conf *) ap_get_module_config(sconf, &cgi_module);
struct cgi_child_stuff cld;
if (r->method_number == M_OPTIONS) {
/* 99 out of 100 CGI scripts, this is all they support */
r->allowed |= (1 << M_GET);
r->allowed |= (1 << M_POST);
return DECLINED;
}
if ((argv0 = strrchr(r->filename, '/')) != NULL)
argv0++;
else
argv0 = r->filename;
nph = !(strncmp(argv0, "nph-", 4));
if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
"Options ExecCGI is off in this directory");
if (nph && is_included)
return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
"attempt to include NPH CGI script");
#if defined(OS2) || defined(WIN32)
/* Allow for cgi files without the .EXE extension on them under OS/2 */
if (r->finfo.st_mode == 0) {
struct stat statbuf;
char *newfile;
newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL);
if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) {
return log_scripterror(r, conf, NOT_FOUND, 0,
"script not found or unable to stat");
} else {
r->filename = newfile;
}
}
#else
if (r->finfo.st_mode == 0)
return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO,
"script not found or unable to stat");
#endif
if (S_ISDIR(r->finfo.st_mode))
return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
"attempt to invoke directory as script");
if (!ap_suexec_enabled) {
if (!ap_can_exec(&r->finfo))
return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
"file permissions deny server execution");
}
if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
return retval;
ap_add_common_vars(r);
cld.argv0 = argv0;
cld.r = r;
cld.nph = nph;
cld.debug = conf->logname ? 1 : 0;
#ifdef TPF
cld.t.filename = r->filename;
cld.t.subprocess_env = r->subprocess_env;
cld.t.prog_type = FORK_FILE;
#endif /* TPF */
#ifdef CHARSET_EBCDIC
/* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */
/* Or must we check the Content-Type first? */
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
#endif /*CHARSET_EBCDIC*/
/*
* we spawn out of r->main if it's there so that we can avoid
* waiting for free_proc_chain to cleanup in the middle of an
* SSI request -djg
*/
if (!ap_bspawn_child(r->main ? r->main->pool : r->pool, cgi_child,
(void *) &cld, kill_after_timeout,
&script_out, &script_in, &script_err)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"couldn't spawn child process: %s", r->filename);
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Transfer any put/post args, CERN style...
* Note that we already ignore SIGPIPE in the core server.
*/
if (ap_should_client_block(r)) {
int dbsize, len_read;
if (conf->logname) {
dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1);
dbpos = 0;
}
ap_hard_timeout("copy script args", r);
while ((len_read =
ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
if (conf->logname) {
if ((dbpos + len_read) > conf->bufbytes) {
dbsize = conf->bufbytes - dbpos;
}
else {
dbsize = len_read;
}
memcpy(dbuf + dbpos, argsbuffer, dbsize);
dbpos += dbsize;
}
ap_reset_timeout(r);
if (ap_bwrite(script_out, argsbuffer, len_read) < len_read) {
/* silly script stopped reading, soak up remaining message */
while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) {
/* dump it */
}
break;
}
}
ap_bflush(script_out);
ap_kill_timeout(r);
}
ap_bclose(script_out);
/* Handle script return... */
if (script_in && !nph) {
const char *location;
char sbuf[MAX_STRING_LEN];
int ret;
if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) {
return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
}
#ifdef CHARSET_EBCDIC
/* Now check the Content-Type to decide if conversion is needed */
ap_checkconv(r);
#endif /*CHARSET_EBCDIC*/
location = ap_table_get(r->headers_out, "Location");
if (location && location[0] == '/' && r->status == 200) {
/* Soak up all the script output */
ap_hard_timeout("read from script", r);
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
continue;
}
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
continue;
}
ap_kill_timeout(r);
/* This redirect needs to be a GET no matter what the original
* method was.
*/
r->method = ap_pstrdup(r->pool, "GET");
r->method_number = M_GET;
/* We already read the message body (if any), so don't allow
* the redirected request to think it has one. We can ignore
* Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
*/
ap_table_unset(r->headers_in, "Content-Length");
ap_internal_redirect_handler(location, r);
return OK;
}
else if (location && r->status == 200) {
/* XX Note that if a script wants to produce its own Redirect
* body, it now has to explicitly *say* "Status: 302"
*/
return REDIRECT;
}
ap_send_http_header(r);
if (!r->header_only) {
ap_send_fb(script_in, r);
}
ap_bclose(script_in);
ap_soft_timeout("soaking script stderr", r);
while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
continue;
}
ap_kill_timeout(r);
ap_bclose(script_err);
}
if (script_in && nph) {
ap_send_fb(script_in, r);
}
return OK; /* NOT r->status, even if it has changed. */
}
static const handler_rec cgi_handlers[] =
{
{CGI_MAGIC_TYPE, cgi_handler},
{"cgi-script", cgi_handler},
{NULL}
};
module MODULE_VAR_EXPORT cgi_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_cgi_config, /* server config */
merge_cgi_config, /* merge server config */
cgi_cmds, /* command table */
cgi_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,695 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* Info Module. Display configuration information for the server and
* all included modules.
*
* <Location /server-info>
* SetHandler server-info
* </Location>
*
* GET /server-info - Returns full configuration page for server and all modules
* GET /server-info?server - Returns server configuration only
* GET /server-info?module_name - Returns configuration for a single module
* GET /server-info?list - Returns quick list of included modules
*
* Rasmus Lerdorf <rasmus@vex.net>, May 1996
*
* 05.01.96 Initial Version
*
* Lou Langholtz <ldl@usi.utah.edu>, July 1997
*
* 07.11.97 Addition of the AddModuleInfo directive
*
*/
#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; /* matching module name */
char *info; /* additional 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;
module MODULE_VAR_EXPORT info_module;
extern module *top_module;
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;
/* keep space for \0 byte */
end_buf = buf + buf_len - 1;
while ((*s) && (t < end_buf)) {
if (*s == '<') {
strncpy(t, "&lt;", end_buf - t);
t += 4;
}
else if (*s == '>') {
strncpy(t, "&gt;", end_buf - t);
t += 4;
}
else if (*s == '&') {
strncpy(t, "&amp;", end_buf - t);
t += 5;
}
else {
*t++ = *s;
}
s++;
}
/* oops, overflowed... don't overwrite */
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; /* skip comments */
}
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("&nbsp;&nbsp;", 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>&nbsp;&nbsp;", 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("&nbsp;&nbsp;", r);
}
if (nest == 2) {
ap_rputs("&nbsp;&nbsp;", 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 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;
info_cfg_lines *mod_info_cfg_httpd = NULL;
info_cfg_lines *mod_info_cfg_srm = NULL;
info_cfg_lines *mod_info_cfg_access = NULL;
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;
}
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")) {
cfname = ap_server_root_relative(r->pool, ap_server_confname);
mod_info_cfg_httpd = mod_info_load_config(r->pool, cfname, r);
cfname = ap_server_root_relative(r->pool, serv->srm_confname);
mod_info_cfg_srm = mod_info_load_config(r->pool, cfname, r);
cfname = ap_server_root_relative(r->pool, serv->access_confname);
mod_info_cfg_access = mod_info_load_config(r->pool, cfname, r);
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 &nbsp;&nbsp; "
"min idle: %d &nbsp;&nbsp; "
"max idle: %d &nbsp;&nbsp; "
"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 &nbsp;&nbsp; "
"keep alive: %s &nbsp;&nbsp; "
"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 &nbsp;&nbsp; </tt><br>\n",
ap_threads_per_child);
ap_rprintf(r, "<strong>Excess requests:</strong> "
"<tt>per child: %d &nbsp;&nbsp; </tt><br>\n",
ap_excess_requests_per_child);
ap_rprintf(r, "<strong>Timeouts:</strong> "
"<tt>connection: %d &nbsp;&nbsp; "
"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);
mod_info_module_cmds(r, mod_info_cfg_httpd, modp->cmds,
"httpd.conf");
mod_info_module_cmds(r, mod_info_cfg_srm, modp->cmds,
"srm.conf");
mod_info_module_cmds(r, mod_info_cfg_access, modp->cmds,
"access.conf");
}
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);
/* Done, turn off timeout, close file and return */
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, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_info_config, /* server config */
merge_info_config, /* merge server config */
info_cmds, /* command table */
info_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,773 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/* Status Module. Display lots of internal data about how Apache is
* performing and the state of all children processes.
*
* To enable this, add the following lines into any config file:
*
* <Location /server-status>
* SetHandler server-status
* </Location>
*
* You may want to protect this location by password or domain so no one
* else can look at it. Then you can access the statistics with a URL like:
*
* http://your_server_name/server-status
*
* /server-status - Returns page using tables
* /server-status?notable - Returns page for browsers without table support
* /server-status?refresh - Returns page with 1 second refresh
* /server-status?refresh=6 - Returns page with refresh every 6 seconds
* /server-status?auto - Returns page with data for automatic parsing
*
* Mark Cox, mark@ukweb.com, November 1995
*
* 12.11.95 Initial version for www.telescope.org
* 13.3.96 Updated to remove rprintf's [Mark]
* 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie]
* 18.3.96 Make extra Scoreboard variables #definable
* 25.3.96 Make short report have full precision [Ben Laurie suggested]
* 25.3.96 Show uptime better [Mark/Ben Laurie]
* 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested]
* 09.4.96 Added message for non-STATUS compiled version
* 18.4.96 Added per child and per slot counters [Jim Jagielski]
* 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.]
* 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing
* piece in short reports [Ben Laurie]
* 21.5.96 Additional Status codes (DNS and LOGGING only enabled if
* extended STATUS is enabled) [George Burgyan/Jim J.]
* 10.8.98 Allow for extended status info at runtime (no more STATUS)
* [Jim J.]
*/
#define CORE_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_conf_globals.h" /* for ap_extended_status */
#include "http_main.h"
#include "util_script.h"
#include <time.h>
#include "scoreboard.h"
#include "http_log.h"
#ifdef NEXT
#if (NX_CURRENT_COMPILER_RELEASE == 410)
#ifdef m68k
#define HZ 64
#else
#define HZ 100
#endif
#else
#include <machine/param.h>
#endif
#endif /* NEXT */
#define STATUS_MAXLINE 64
#define KBYTE 1024
#define MBYTE 1048576L
#define GBYTE 1073741824L
#ifndef DEFAULT_TIME_FORMAT
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
#endif
module MODULE_VAR_EXPORT status_module;
/*
*command-related code. This is here to prevent use of ExtendedStatus
* without status_module included.
*/
static const char *set_extended_status(cmd_parms *cmd, void *dummy, char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) {
ap_extended_status = 0;
}
else {
ap_extended_status = 1;
}
return NULL;
}
static const command_rec status_module_cmds[] =
{
{ "ExtendedStatus", set_extended_status, NULL, RSRC_CONF, TAKE1,
"\"On\" to enable extended status information, \"Off\" to disable" },
{NULL}
};
/* Format the number of bytes nicely */
static void format_byte_out(request_rec *r, unsigned long bytes)
{
if (bytes < (5 * KBYTE))
ap_rprintf(r, "%d B", (int) bytes);
else if (bytes < (MBYTE / 2))
ap_rprintf(r, "%.1f kB", (float) bytes / KBYTE);
else if (bytes < (GBYTE / 2))
ap_rprintf(r, "%.1f MB", (float) bytes / MBYTE);
else
ap_rprintf(r, "%.1f GB", (float) bytes / GBYTE);
}
static void format_kbyte_out(request_rec *r, unsigned long kbytes)
{
if (kbytes < KBYTE)
ap_rprintf(r, "%d kB", (int) kbytes);
else if (kbytes < MBYTE)
ap_rprintf(r, "%.1f MB", (float) kbytes / KBYTE);
else
ap_rprintf(r, "%.1f GB", (float) kbytes / MBYTE);
}
static void show_time(request_rec *r, time_t tsecs)
{
long days, hrs, mins, secs;
secs = tsecs % 60;
tsecs /= 60;
mins = tsecs % 60;
tsecs /= 60;
hrs = tsecs % 24;
days = tsecs / 24;
if (days)
ap_rprintf(r, " %ld day%s", days, days == 1 ? "" : "s");
if (hrs)
ap_rprintf(r, " %ld hour%s", hrs, hrs == 1 ? "" : "s");
if (mins)
ap_rprintf(r, " %ld minute%s", mins, mins == 1 ? "" : "s");
if (secs)
ap_rprintf(r, " %ld second%s", secs, secs == 1 ? "" : "s");
}
/* Main handler for x-httpd-status requests */
/* ID values for command table */
#define STAT_OPT_END -1
#define STAT_OPT_REFRESH 0
#define STAT_OPT_NOTABLE 1
#define STAT_OPT_AUTO 2
struct stat_opt {
int id;
const char *form_data_str;
const char *hdr_out_str;
};
static const struct stat_opt status_options[] = /* see #defines above */
{
{STAT_OPT_REFRESH, "refresh", "Refresh"},
{STAT_OPT_NOTABLE, "notable", NULL},
{STAT_OPT_AUTO, "auto", NULL},
{STAT_OPT_END, NULL, NULL}
};
static char status_flags[SERVER_NUM_STATUS];
static int status_handler(request_rec *r)
{
char *loc;
time_t nowtime = time(NULL);
time_t up_time;
int i, res;
int ready = 0;
int busy = 0;
unsigned long count = 0;
unsigned long lres, bytes;
unsigned long my_lres, my_bytes, conn_bytes;
unsigned short conn_lres;
unsigned long bcount = 0;
unsigned long kbcount = 0;
long req_time;
#ifndef NO_TIMES
#ifdef _SC_CLK_TCK
float tick = sysconf(_SC_CLK_TCK);
#else
float tick = HZ;
#endif
#endif
int short_report = 0;
int no_table_report = 0;
short_score score_record;
parent_score ps_record;
char stat_buffer[HARD_SERVER_LIMIT];
int pid_buffer[HARD_SERVER_LIMIT];
clock_t tu, ts, tcu, tcs;
server_rec *vhost;
tu = ts = tcu = tcs = 0;
if (!ap_exists_scoreboard_image()) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Server status unavailable in inetd mode");
return HTTP_INTERNAL_SERVER_ERROR;
}
r->allowed = (1 << M_GET);
if (r->method_number != M_GET)
return DECLINED;
r->content_type = "text/html";
/*
* Simple table-driven form data set parser that lets you alter the header
*/
if (r->args) {
i = 0;
while (status_options[i].id != STAT_OPT_END) {
if ((loc = strstr(r->args, status_options[i].form_data_str)) != NULL) {
switch (status_options[i].id) {
case STAT_OPT_REFRESH:
if (*(loc + strlen(status_options[i].form_data_str)) == '=')
ap_table_set(r->headers_out,
status_options[i].hdr_out_str,
loc + strlen(status_options[i].hdr_out_str) + 1);
else
ap_table_set(r->headers_out,
status_options[i].hdr_out_str, "1");
break;
case STAT_OPT_NOTABLE:
no_table_report = 1;
break;
case STAT_OPT_AUTO:
r->content_type = "text/plain";
short_report = 1;
break;
}
}
i++;
}
}
ap_send_http_header(r);
if (r->header_only)
return 0;
ap_sync_scoreboard_image();
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
score_record = ap_scoreboard_image->servers[i];
ps_record = ap_scoreboard_image->parent[i];
res = score_record.status;
stat_buffer[i] = status_flags[res];
pid_buffer[i] = (int) ps_record.pid;
if (res == SERVER_READY)
ready++;
else if (res != SERVER_DEAD)
busy++;
if (ap_extended_status) {
lres = score_record.access_count;
bytes = score_record.bytes_served;
if (lres != 0 || (res != SERVER_READY && res != SERVER_DEAD)) {
#ifndef NO_TIMES
tu += score_record.times.tms_utime;
ts += score_record.times.tms_stime;
tcu += score_record.times.tms_cutime;
tcs += score_record.times.tms_cstime;
#endif /* NO_TIMES */
count += lres;
bcount += bytes;
if (bcount >= KBYTE) {
kbcount += (bcount >> 10);
bcount = bcount & 0x3ff;
}
}
}
}
up_time = nowtime - ap_restart_time;
ap_hard_timeout("send status info", r);
if (!short_report) {
ap_rputs(DOCTYPE_HTML_3_2
"<HTML><HEAD>\n<TITLE>Apache Status</TITLE>\n</HEAD><BODY>\n",
r);
ap_rputs("<H1>Apache Server Status for ", r);
ap_rvputs(r, ap_get_server_name(r), "</H1>\n\n", NULL);
ap_rvputs(r, "Server Version: ",
ap_get_server_version(), "<br>\n", NULL);
ap_rvputs(r, "Server Built: ",
ap_get_server_built(), "<br>\n<hr>\n", NULL);
ap_rvputs(r, "Current Time: ",
ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0), "<br>\n", NULL);
ap_rvputs(r, "Restart Time: ",
ap_ht_time(r->pool, ap_restart_time, DEFAULT_TIME_FORMAT, 0),
"<br>\n", NULL);
ap_rprintf(r, "Parent Server Generation: %d <br>\n", (int) ap_my_generation);
ap_rputs("Server uptime: ", r);
show_time(r, up_time);
ap_rputs("<br>\n", r);
}
if (ap_extended_status) {
if (short_report) {
ap_rprintf(r, "Total Accesses: %lu\nTotal kBytes: %lu\n",
count, kbcount);
#ifndef NO_TIMES
/* Allow for OS/2 not having CPU stats */
if (ts || tu || tcu || tcs)
ap_rprintf(r, "CPULoad: %g\n",
(tu + ts + tcu + tcs) / tick / up_time * 100.);
#endif
ap_rprintf(r, "Uptime: %ld\n", (long) (up_time));
if (up_time > 0)
ap_rprintf(r, "ReqPerSec: %g\n",
(float) count / (float) up_time);
if (up_time > 0)
ap_rprintf(r, "BytesPerSec: %g\n",
KBYTE * (float) kbcount / (float) up_time);
if (count > 0)
ap_rprintf(r, "BytesPerReq: %g\n",
KBYTE * (float) kbcount / (float) count);
}
else { /* !short_report */
ap_rprintf(r, "Total accesses: %lu - Total Traffic: ", count);
format_kbyte_out(r, kbcount);
#ifndef NO_TIMES
/* Allow for OS/2 not having CPU stats */
ap_rputs("<br>\n", r);
ap_rprintf(r, "CPU Usage: u%g s%g cu%g cs%g",
tu / tick, ts / tick, tcu / tick, tcs / tick);
if (ts || tu || tcu || tcs)
ap_rprintf(r, " - %.3g%% CPU load",
(tu + ts + tcu + tcs) / tick / up_time * 100.);
#endif
ap_rputs("<br>\n", r);
if (up_time > 0)
ap_rprintf(r, "%.3g requests/sec - ",
(float) count / (float) up_time);
if (up_time > 0) {
format_byte_out(r, KBYTE * (float) kbcount / (float) up_time);
ap_rputs("/second - ", r);
}
if (count > 0) {
format_byte_out(r, KBYTE * (float) kbcount / (float) count);
ap_rputs("/request", r);
}
ap_rputs("<br>\n", r);
} /* short_report */
} /* ap_extended_status */
if (!short_report)
ap_rprintf(r, "\n%d requests currently being processed, %d idle servers\n"
,busy, ready);
else
ap_rprintf(r, "BusyServers: %d\nIdleServers: %d\n", busy, ready);
/* send the scoreboard 'table' out */
if (!short_report)
ap_rputs("<PRE>", r);
else
ap_rputs("Scoreboard: ", r);
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
ap_rputc(stat_buffer[i], r);
if ((i % STATUS_MAXLINE == (STATUS_MAXLINE - 1)) && !short_report)
ap_rputs("\n", r);
}
if (short_report)
ap_rputs("\n", r);
else {
ap_rputs("</PRE>\n", r);
ap_rputs("Scoreboard Key: <br>\n", r);
ap_rputs("\"<B><code>_</code></B>\" Waiting for Connection, \n", r);
ap_rputs("\"<B><code>S</code></B>\" Starting up, \n", r);
ap_rputs("\"<B><code>R</code></B>\" Reading Request,<BR>\n", r);
ap_rputs("\"<B><code>W</code></B>\" Sending Reply, \n", r);
ap_rputs("\"<B><code>K</code></B>\" Keepalive (read), \n", r);
ap_rputs("\"<B><code>D</code></B>\" DNS Lookup,<BR>\n", r);
ap_rputs("\"<B><code>L</code></B>\" Logging, \n", r);
ap_rputs("\"<B><code>G</code></B>\" Gracefully finishing, \n", r);
ap_rputs("\"<B><code>.</code></B>\" Open slot with no current process<P>\n", r);
ap_rputs("<P>\n", r);
if (!ap_extended_status) {
int j = 0;
ap_rputs("PID Key: <br>\n", r);
ap_rputs("<PRE>\n", r);
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
if (stat_buffer[i] != '.') {
ap_rprintf(r, " %d in state: %c ", pid_buffer[i],
stat_buffer[i]);
if (++j >= 3) {
ap_rputs("\n", r);
j = 0;
} else
ap_rputs(",", r);
}
}
ap_rputs("\n", r);
ap_rputs("</PRE>\n", r);
}
}
if (ap_extended_status) {
if (!short_report) {
if (no_table_report)
ap_rputs("<p><hr><h2>Server Details</h2>\n\n", r);
else
#ifdef NO_TIMES
/* Allow for OS/2 not having CPU stats */
ap_rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M\n<th>SS<th>Req<th>Conn<th>Child<th>Slot<th>Client<th>VHost<th>Request</tr>\n\n", r);
#else
ap_rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M<th>CPU\n<th>SS<th>Req<th>Conn<th>Child<th>Slot<th>Client<th>VHost<th>Request</tr>\n\n", r);
#endif
}
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
score_record = ap_scoreboard_image->servers[i];
ps_record = ap_scoreboard_image->parent[i];
vhost = score_record.vhostrec;
if (ps_record.generation != ap_my_generation) {
vhost = NULL;
}
#if defined(NO_GETTIMEOFDAY)
#ifndef NO_TIMES
if (score_record.start_time == (clock_t) 0)
#endif /* NO_TIMES */
req_time = 0L;
#ifndef NO_TIMES
else {
req_time = score_record.stop_time - score_record.start_time;
req_time = (req_time * 1000) / (int) tick;
}
#endif /* NO_TIMES */
#else
if (score_record.start_time.tv_sec == 0L &&
score_record.start_time.tv_usec == 0L)
req_time = 0L;
else
req_time =
((score_record.stop_time.tv_sec - score_record.start_time.tv_sec) * 1000) +
((score_record.stop_time.tv_usec - score_record.start_time.tv_usec) / 1000);
#endif
if (req_time < 0L)
req_time = 0L;
lres = score_record.access_count;
my_lres = score_record.my_access_count;
conn_lres = score_record.conn_count;
bytes = score_record.bytes_served;
my_bytes = score_record.my_bytes_served;
conn_bytes = score_record.conn_bytes;
if (lres != 0 || (score_record.status != SERVER_READY
&& score_record.status != SERVER_DEAD)) {
if (!short_report) {
if (no_table_report) {
if (score_record.status == SERVER_DEAD)
ap_rprintf(r,
"<b>Server %d-%d</b> (-): %d|%lu|%lu [",
i, (int) ps_record.generation, (int) conn_lres,
my_lres, lres);
else
ap_rprintf(r,
"<b>Server %d-%d</b> (%d): %d|%lu|%lu [",
i, (int) ps_record.generation,
(int) ps_record.pid,
(int) conn_lres, my_lres, lres);
switch (score_record.status) {
case SERVER_READY:
ap_rputs("Ready", r);
break;
case SERVER_STARTING:
ap_rputs("Starting", r);
break;
case SERVER_BUSY_READ:
ap_rputs("<b>Read</b>", r);
break;
case SERVER_BUSY_WRITE:
ap_rputs("<b>Write</b>", r);
break;
case SERVER_BUSY_KEEPALIVE:
ap_rputs("<b>Keepalive</b>", r);
break;
case SERVER_BUSY_LOG:
ap_rputs("<b>Logging</b>", r);
break;
case SERVER_BUSY_DNS:
ap_rputs("<b>DNS lookup</b>", r);
break;
case SERVER_DEAD:
ap_rputs("Dead", r);
break;
case SERVER_GRACEFUL:
ap_rputs("Graceful", r);
break;
default:
ap_rputs("?STATE?", r);
break;
}
#ifdef NO_TIMES
/* Allow for OS/2 not having CPU stats */
ap_rprintf(r, "]\n %.0f %ld (",
#else
ap_rprintf(r, "] u%g s%g cu%g cs%g\n %.0f %ld (",
score_record.times.tms_utime / tick,
score_record.times.tms_stime / tick,
score_record.times.tms_cutime / tick,
score_record.times.tms_cstime / tick,
#endif
#ifdef OPTIMIZE_TIMEOUTS
difftime(nowtime, ps_record.last_rtime),
#else
difftime(nowtime, score_record.last_used),
#endif
(long) req_time);
format_byte_out(r, conn_bytes);
ap_rputs("|", r);
format_byte_out(r, my_bytes);
ap_rputs("|", r);
format_byte_out(r, bytes);
ap_rputs(")\n", r);
ap_rprintf(r, " <i>%s {%s}</i> <b>[%s]</b><br>\n\n",
score_record.client,
ap_escape_html(r->pool, score_record.request),
vhost ? vhost->server_hostname : "(unavailable)");
}
else { /* !no_table_report */
if (score_record.status == SERVER_DEAD)
ap_rprintf(r,
"<tr><td><b>%d-%d</b><td>-<td>%d/%lu/%lu",
i, (int) ps_record.generation,
(int) conn_lres, my_lres, lres);
else
ap_rprintf(r,
"<tr><td><b>%d-%d</b><td>%d<td>%d/%lu/%lu",
i, (int) ps_record.generation,
(int) ps_record.pid, (int) conn_lres,
my_lres, lres);
switch (score_record.status) {
case SERVER_READY:
ap_rputs("<td>_", r);
break;
case SERVER_STARTING:
ap_rputs("<td><b>S</b>", r);
break;
case SERVER_BUSY_READ:
ap_rputs("<td><b>R</b>", r);
break;
case SERVER_BUSY_WRITE:
ap_rputs("<td><b>W</b>", r);
break;
case SERVER_BUSY_KEEPALIVE:
ap_rputs("<td><b>K</b>", r);
break;
case SERVER_BUSY_LOG:
ap_rputs("<td><b>L</b>", r);
break;
case SERVER_BUSY_DNS:
ap_rputs("<td><b>D</b>", r);
break;
case SERVER_DEAD:
ap_rputs("<td>.", r);
break;
case SERVER_GRACEFUL:
ap_rputs("<td>G", r);
break;
default:
ap_rputs("<td>?", r);
break;
}
#ifdef NO_TIMES
/* Allow for OS/2 not having CPU stats */
ap_rprintf(r, "\n<td>%.0f<td>%ld",
#else
ap_rprintf(r, "\n<td>%.2f<td>%.0f<td>%ld",
(score_record.times.tms_utime +
score_record.times.tms_stime +
score_record.times.tms_cutime +
score_record.times.tms_cstime) / tick,
#endif
#ifdef OPTIMIZE_TIMEOUTS
difftime(nowtime, ps_record.last_rtime),
#else
difftime(nowtime, score_record.last_used),
#endif
(long) req_time);
ap_rprintf(r, "<td>%-1.1f<td>%-2.2f<td>%-2.2f\n",
(float) conn_bytes / KBYTE, (float) my_bytes / MBYTE,
(float) bytes / MBYTE);
if (score_record.status == SERVER_BUSY_READ)
ap_rprintf(r,
"<td>?<td nowrap>?<td nowrap>..reading.. </tr>\n\n");
else
ap_rprintf(r,
"<td>%s<td nowrap>%s<td nowrap>%s</tr>\n\n",
score_record.client,
vhost ? vhost->server_hostname : "(unavailable)",
ap_escape_html(r->pool, score_record.request));
} /* no_table_report */
} /* !short_report */
} /* if (<active child>) */
} /* for () */
if (!(short_report || no_table_report)) {
#ifdef NO_TIMES
ap_rputs("</table>\n \
<hr> \
<table>\n \
<tr><th>Srv<td>Child Server number - generation\n \
<tr><th>PID<td>OS process ID\n \
<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \
<tr><th>M<td>Mode of operation\n \
<tr><th>SS<td>Seconds since beginning of most recent request\n \
<tr><th>Req<td>Milliseconds required to process most recent request\n \
<tr><th>Conn<td>Kilobytes transferred this connection\n \
<tr><th>Child<td>Megabytes transferred this child\n \
<tr><th>Slot<td>Total megabytes transferred this slot\n \
</table>\n", r);
#else
ap_rputs("</table>\n \
<hr> \
<table>\n \
<tr><th>Srv<td>Child Server number - generation\n \
<tr><th>PID<td>OS process ID\n \
<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \
<tr><th>M<td>Mode of operation\n \
<tr><th>CPU<td>CPU usage, number of seconds\n \
<tr><th>SS<td>Seconds since beginning of most recent request\n \
<tr><th>Req<td>Milliseconds required to process most recent request\n \
<tr><th>Conn<td>Kilobytes transferred this connection\n \
<tr><th>Child<td>Megabytes transferred this child\n \
<tr><th>Slot<td>Total megabytes transferred this slot\n \
</table>\n", r);
#endif
}
} else {
ap_rputs("<hr>To obtain a full report with current status information ", r);
ap_rputs("you need to use the <code>ExtendedStatus On</code> directive. \n", r);
}
if (!short_report) {
ap_rputs(ap_psignature("<HR>\n",r), r);
ap_rputs("</BODY></HTML>\n", r);
}
ap_kill_timeout(r);
return 0;
}
static void status_init(server_rec *s, pool *p)
{
status_flags[SERVER_DEAD] = '.'; /* We don't want to assume these are in */
status_flags[SERVER_READY] = '_'; /* any particular order in scoreboard.h */
status_flags[SERVER_STARTING] = 'S';
status_flags[SERVER_BUSY_READ] = 'R';
status_flags[SERVER_BUSY_WRITE] = 'W';
status_flags[SERVER_BUSY_KEEPALIVE] = 'K';
status_flags[SERVER_BUSY_LOG] = 'L';
status_flags[SERVER_BUSY_DNS] = 'D';
status_flags[SERVER_GRACEFUL] = 'G';
}
static const handler_rec status_handlers[] =
{
{STATUS_MAGIC_TYPE, status_handler},
{"server-status", status_handler},
{NULL}
};
module MODULE_VAR_EXPORT status_module =
{
STANDARD_MODULE_STUFF,
status_init, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
status_module_cmds, /* command table */
status_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

402
modules/http/mod_mime.c Normal file
View File

@ -0,0 +1,402 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_mime.c: Sends/gets MIME headers for requests
*
* Rob McCool
*
*/
#define MIME_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
typedef struct handlers_info {
char *name;
} handlers_info;
typedef struct {
table *forced_types; /* Additional AddTyped stuff */
table *encoding_types; /* Added with AddEncoding... */
table *language_types; /* Added with AddLanguage... */
table *handlers; /* Added with AddHandler... */
array_header *handlers_remove; /* List of handlers to remove */
char *type; /* Type forced with ForceType */
char *handler; /* Handler forced with SetHandler */
char *default_language; /* Language if no AddLanguage ext found */
} mime_dir_config;
module MODULE_VAR_EXPORT mime_module;
static void *create_mime_dir_config(pool *p, char *dummy)
{
mime_dir_config *new =
(mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config));
new->forced_types = ap_make_table(p, 4);
new->encoding_types = ap_make_table(p, 4);
new->language_types = ap_make_table(p, 4);
new->handlers = ap_make_table(p, 4);
new->handlers_remove = ap_make_array(p, 4, sizeof(handlers_info));
new->type = NULL;
new->handler = NULL;
new->default_language = NULL;
return new;
}
static void *merge_mime_dir_configs(pool *p, void *basev, void *addv)
{
mime_dir_config *base = (mime_dir_config *) basev;
mime_dir_config *add = (mime_dir_config *) addv;
mime_dir_config *new =
(mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config));
int i;
handlers_info *hand;
hand = (handlers_info *) add->handlers_remove->elts;
for (i = 0; i < add->handlers_remove->nelts; i++) {
ap_table_unset(base->handlers, hand[i].name);
}
new->forced_types = ap_overlay_tables(p, add->forced_types,
base->forced_types);
new->encoding_types = ap_overlay_tables(p, add->encoding_types,
base->encoding_types);
new->language_types = ap_overlay_tables(p, add->language_types,
base->language_types);
new->handlers = ap_overlay_tables(p, add->handlers,
base->handlers);
new->type = add->type ? add->type : base->type;
new->handler = add->handler ? add->handler : base->handler;
new->default_language = add->default_language ?
add->default_language : base->default_language;
return new;
}
static const char *add_type(cmd_parms *cmd, mime_dir_config * m, char *ct,
char *ext)
{
if (*ext == '.')
++ext;
ap_str_tolower(ct);
ap_table_setn(m->forced_types, ext, ct);
return NULL;
}
static const char *add_encoding(cmd_parms *cmd, mime_dir_config * m, char *enc,
char *ext)
{
if (*ext == '.')
++ext;
ap_str_tolower(enc);
ap_table_setn(m->encoding_types, ext, enc);
return NULL;
}
static const char *add_language(cmd_parms *cmd, mime_dir_config * m, char *lang,
char *ext)
{
if (*ext == '.')
++ext;
ap_str_tolower(lang);
ap_table_setn(m->language_types, ext, lang);
return NULL;
}
static const char *add_handler(cmd_parms *cmd, mime_dir_config * m, char *hdlr,
char *ext)
{
if (*ext == '.')
++ext;
ap_str_tolower(hdlr);
ap_table_setn(m->handlers, ext, hdlr);
return NULL;
}
/*
* Note handler names that should be un-added for this location. This
* will keep the association from being inherited, as well, but not
* from being re-added at a subordinate level.
*/
static const char *remove_handler(cmd_parms *cmd, void *m, char *ext)
{
mime_dir_config *mcfg = (mime_dir_config *) m;
handlers_info *hand;
if (*ext == '.') {
++ext;
}
hand = (handlers_info *) ap_push_array(mcfg->handlers_remove);
hand->name = ap_pstrdup(cmd->pool, ext);
return NULL;
}
/* The sole bit of server configuration that the MIME module has is
* the name of its config file, so...
*/
static const char *set_types_config(cmd_parms *cmd, void *dummy, char *arg)
{
ap_set_module_config(cmd->server->module_config, &mime_module, arg);
return NULL;
}
static const command_rec mime_cmds[] =
{
{"AddType", add_type, NULL, OR_FILEINFO, ITERATE2,
"a mime type followed by one or more file extensions"},
{"AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2,
"an encoding (e.g., gzip), followed by one or more file extensions"},
{"AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2,
"a language (e.g., fr), followed by one or more file extensions"},
{"AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2,
"a handler name followed by one or more file extensions"},
{"ForceType", ap_set_string_slot_lower,
(void *)XtOffsetOf(mime_dir_config, type), OR_FILEINFO, TAKE1,
"a media type"},
{"RemoveHandler", remove_handler, NULL, OR_FILEINFO, ITERATE,
"one or more file extensions"},
{"SetHandler", ap_set_string_slot_lower,
(void *)XtOffsetOf(mime_dir_config, handler), OR_FILEINFO, TAKE1,
"a handler name"},
{"TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1,
"the MIME types config file"},
{"DefaultLanguage", ap_set_string_slot,
(void*)XtOffsetOf(mime_dir_config, default_language), OR_FILEINFO, TAKE1,
"language to use for documents with no other language file extension" },
{NULL}
};
/* Hash table --- only one of these per daemon; virtual hosts can
* get private versions through AddType...
*/
#define MIME_HASHSIZE (32)
#define hash(i) (ap_tolower(i) % MIME_HASHSIZE)
static table *hash_buckets[MIME_HASHSIZE];
static void init_mime(server_rec *s, pool *p)
{
configfile_t *f;
char l[MAX_STRING_LEN];
int x;
char *types_confname = ap_get_module_config(s->module_config, &mime_module);
if (!types_confname)
types_confname = TYPES_CONFIG_FILE;
types_confname = ap_server_root_relative(p, types_confname);
if (!(f = ap_pcfg_openfile(p, types_confname))) {
ap_log_error(APLOG_MARK, APLOG_ERR, s,
"could not open mime types log file %s.", types_confname);
exit(1);
}
for (x = 0; x < MIME_HASHSIZE; x++)
hash_buckets[x] = ap_make_table(p, 10);
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
const char *ll = l, *ct;
if (l[0] == '#')
continue;
ct = ap_getword_conf(p, &ll);
while (ll[0]) {
char *ext = ap_getword_conf(p, &ll);
ap_str_tolower(ext); /* ??? */
ap_table_setn(hash_buckets[hash(ext[0])], ext, ct);
}
}
ap_cfg_closefile(f);
}
static int find_ct(request_rec *r)
{
const char *fn = strrchr(r->filename, '/');
mime_dir_config *conf =
(mime_dir_config *) ap_get_module_config(r->per_dir_config, &mime_module);
char *ext;
const char *orighandler = r->handler;
const char *type;
if (S_ISDIR(r->finfo.st_mode)) {
r->content_type = DIR_MAGIC_TYPE;
return OK;
}
/* TM -- FIXME
* if r->filename does not contain a '/', the following passes a null
* pointer to getword, causing a SEGV ..
*/
if (fn == NULL)
fn = r->filename;
/* Parse filename extensions, which can be in any order */
while ((ext = ap_getword(r->pool, &fn, '.')) && *ext) {
int found = 0;
/* Check for Content-Type */
if ((type = ap_table_get(conf->forced_types, ext))
|| (type = ap_table_get(hash_buckets[hash(*ext)], ext))) {
r->content_type = type;
found = 1;
}
/* Check for Content-Language */
if ((type = ap_table_get(conf->language_types, ext))) {
const char **new;
r->content_language = type; /* back compat. only */
if (!r->content_languages)
r->content_languages = ap_make_array(r->pool, 2, sizeof(char *));
new = (const char **) ap_push_array(r->content_languages);
*new = type;
found = 1;
}
/* Check for Content-Encoding */
if ((type = ap_table_get(conf->encoding_types, ext))) {
if (!r->content_encoding)
r->content_encoding = type;
else
r->content_encoding = ap_pstrcat(r->pool, r->content_encoding,
", ", type, NULL);
found = 1;
}
/* Check for a special handler, but not for proxy request */
if ((type = ap_table_get(conf->handlers, ext)) && !r->proxyreq) {
r->handler = type;
found = 1;
}
/* This is to deal with cases such as foo.gif.bak, which we want
* to not have a type. So if we find an unknown extension, we
* zap the type/language/encoding and reset the handler
*/
if (!found) {
r->content_type = NULL;
r->content_language = NULL;
r->content_languages = NULL;
r->content_encoding = NULL;
r->handler = orighandler;
}
}
/* Set default language, if none was specified by the extensions
* and we have a DefaultLanguage setting in force
*/
if (!r->content_languages && conf->default_language) {
const char **new;
r->content_language = conf->default_language; /* back compat. only */
if (!r->content_languages)
r->content_languages = ap_make_array(r->pool, 2, sizeof(char *));
new = (const char **) ap_push_array(r->content_languages);
*new = conf->default_language;
}
/* Check for overrides with ForceType/SetHandler */
if (conf->type && strcmp(conf->type, "none"))
r->content_type = conf->type;
if (conf->handler && strcmp(conf->handler, "none"))
r->handler = conf->handler;
if (!r->content_type)
return DECLINED;
return OK;
}
module MODULE_VAR_EXPORT mime_module =
{
STANDARD_MODULE_STUFF,
init_mime, /* initializer */
create_mime_dir_config, /* dir config creator */
merge_mime_dir_configs, /* dir config merger */
NULL, /* server config */
NULL, /* merge server config */
mime_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
find_ct, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,232 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_actions.c: executes scripts based on MIME type or HTTP method
*
* by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c,
* adapted by rst from original NCSA code by Rob McCool
*
* Usage instructions:
*
* Action mime/type /cgi-bin/script
*
* will activate /cgi-bin/script when a file of content type mime/type is
* requested. It sends the URL and file path of the requested document using
* the standard CGI PATH_INFO and PATH_TRANSLATED environment variables.
*
* Script PUT /cgi-bin/script
*
* will activate /cgi-bin/script when a request is received with the
* HTTP method "PUT". The available method names are defined in httpd.h.
* If the method is GET, the script will only be activated if the requested
* URI includes query information (stuff after a ?-mark).
*/
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
typedef struct {
table *action_types; /* Added with Action... */
char *scripted[METHODS]; /* Added with Script... */
} action_dir_config;
module action_module;
static void *create_action_dir_config(pool *p, char *dummy)
{
action_dir_config *new =
(action_dir_config *) ap_palloc(p, sizeof(action_dir_config));
new->action_types = ap_make_table(p, 4);
memset(new->scripted, 0, sizeof(new->scripted));
return new;
}
static void *merge_action_dir_configs(pool *p, void *basev, void *addv)
{
action_dir_config *base = (action_dir_config *) basev;
action_dir_config *add = (action_dir_config *) addv;
action_dir_config *new = (action_dir_config *) ap_palloc(p,
sizeof(action_dir_config));
int i;
new->action_types = ap_overlay_tables(p, add->action_types,
base->action_types);
for (i = 0; i < METHODS; ++i) {
new->scripted[i] = add->scripted[i] ? add->scripted[i]
: base->scripted[i];
}
return new;
}
static const char *add_action(cmd_parms *cmd, action_dir_config * m, char *type,
char *script)
{
ap_table_setn(m->action_types, type, script);
return NULL;
}
static const char *set_script(cmd_parms *cmd, action_dir_config * m,
char *method, char *script)
{
int methnum;
methnum = ap_method_number_of(method);
if (methnum == M_TRACE)
return "TRACE not allowed for Script";
else if (methnum == M_INVALID)
return "Unknown method type for Script";
else
m->scripted[methnum] = script;
return NULL;
}
static const command_rec action_cmds[] =
{
{"Action", add_action, NULL, OR_FILEINFO, TAKE2,
"a media type followed by a script name"},
{"Script", set_script, NULL, ACCESS_CONF | RSRC_CONF, TAKE2,
"a method followed by a script name"},
{NULL}
};
static int action_handler(request_rec *r)
{
action_dir_config *conf = (action_dir_config *)
ap_get_module_config(r->per_dir_config, &action_module);
const char *t, *action = r->handler ? r->handler : r->content_type;
const char *script;
int i;
/* Set allowed stuff */
for (i = 0; i < METHODS; ++i) {
if (conf->scripted[i])
r->allowed |= (1 << i);
}
/* First, check for the method-handling scripts */
if (r->method_number == M_GET) {
if (r->args)
script = conf->scripted[M_GET];
else
script = NULL;
}
else {
script = conf->scripted[r->method_number];
}
/* Check for looping, which can happen if the CGI script isn't */
if (script && r->prev && r->prev->prev)
return DECLINED;
/* Second, check for actions (which override the method scripts) */
if ((t = ap_table_get(conf->action_types,
action ? action : ap_default_type(r)))) {
script = t;
if (r->finfo.st_mode == 0) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"File does not exist: %s", r->filename);
return NOT_FOUND;
}
}
if (script == NULL)
return DECLINED;
ap_internal_redirect_handler(ap_pstrcat(r->pool, script, ap_escape_uri(r->pool,
r->uri), r->args ? "?" : NULL, r->args, NULL), r);
return OK;
}
static const handler_rec action_handlers[] =
{
{"*/*", action_handler},
{NULL}
};
module action_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_action_dir_config, /* dir config creater */
merge_action_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
action_cmds, /* command table */
action_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

418
modules/mappers/mod_alias.c Normal file
View File

@ -0,0 +1,418 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* http_alias.c: Stuff for dealing with directory aliases
*
* Original by Rob McCool, rewritten in succession by David Robinson
* and rst.
*
*/
#include "httpd.h"
#include "http_config.h"
typedef struct {
char *real;
char *fake;
char *handler;
regex_t *regexp;
int redir_status; /* 301, 302, 303, 410, etc */
} alias_entry;
typedef struct {
array_header *aliases;
array_header *redirects;
} alias_server_conf;
typedef struct {
array_header *redirects;
} alias_dir_conf;
module MODULE_VAR_EXPORT alias_module;
static void *create_alias_config(pool *p, server_rec *s)
{
alias_server_conf *a =
(alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf));
a->aliases = ap_make_array(p, 20, sizeof(alias_entry));
a->redirects = ap_make_array(p, 20, sizeof(alias_entry));
return a;
}
static void *create_alias_dir_config(pool *p, char *d)
{
alias_dir_conf *a =
(alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf));
a->redirects = ap_make_array(p, 2, sizeof(alias_entry));
return a;
}
static void *merge_alias_config(pool *p, void *basev, void *overridesv)
{
alias_server_conf *a =
(alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf));
alias_server_conf *base = (alias_server_conf *) basev, *overrides = (alias_server_conf *) overridesv;
a->aliases = ap_append_arrays(p, overrides->aliases, base->aliases);
a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects);
return a;
}
static void *merge_alias_dir_config(pool *p, void *basev, void *overridesv)
{
alias_dir_conf *a =
(alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf));
alias_dir_conf *base = (alias_dir_conf *) basev, *overrides = (alias_dir_conf *) overridesv;
a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects);
return a;
}
static const char *add_alias_internal(cmd_parms *cmd, void *dummy, char *f, char *r,
int use_regex)
{
server_rec *s = cmd->server;
alias_server_conf *conf =
(alias_server_conf *) ap_get_module_config(s->module_config, &alias_module);
alias_entry *new = ap_push_array(conf->aliases);
/* XX r can NOT be relative to DocumentRoot here... compat bug. */
if (use_regex) {
new->regexp = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
if (new->regexp == NULL)
return "Regular expression could not be compiled.";
}
new->fake = f;
new->real = r;
new->handler = cmd->info;
return NULL;
}
static const char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r)
{
return add_alias_internal(cmd, dummy, f, r, 0);
}
static const char *add_alias_regex(cmd_parms *cmd, void *dummy, char *f, char *r)
{
return add_alias_internal(cmd, dummy, f, r, 1);
}
static const char *add_redirect_internal(cmd_parms *cmd, alias_dir_conf * dirconf,
char *arg1, char *arg2, char *arg3,
int use_regex)
{
alias_entry *new;
server_rec *s = cmd->server;
alias_server_conf *serverconf =
(alias_server_conf *) ap_get_module_config(s->module_config, &alias_module);
int status = (int) (long) cmd->info;
regex_t *r = NULL;
char *f = arg2;
char *url = arg3;
if (!strcasecmp(arg1, "gone"))
status = HTTP_GONE;
else if (!strcasecmp(arg1, "permanent"))
status = HTTP_MOVED_PERMANENTLY;
else if (!strcasecmp(arg1, "temp"))
status = HTTP_MOVED_TEMPORARILY;
else if (!strcasecmp(arg1, "seeother"))
status = HTTP_SEE_OTHER;
else if (ap_isdigit(*arg1))
status = atoi(arg1);
else {
f = arg1;
url = arg2;
}
if (use_regex) {
r = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
if (r == NULL)
return "Regular expression could not be compiled.";
}
if (ap_is_HTTP_REDIRECT(status)) {
if (!url)
return "URL to redirect to is missing";
if (!use_regex && !ap_is_url(url))
return "Redirect to non-URL";
}
else {
if (url)
return "Redirect URL not valid for this status";
}
if (cmd->path)
new = ap_push_array(dirconf->redirects);
else
new = ap_push_array(serverconf->redirects);
new->fake = f;
new->real = url;
new->regexp = r;
new->redir_status = status;
return NULL;
}
static const char *add_redirect(cmd_parms *cmd, alias_dir_conf * dirconf, char *arg1,
char *arg2, char *arg3)
{
return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 0);
}
static const char *add_redirect_regex(cmd_parms *cmd, alias_dir_conf * dirconf,
char *arg1, char *arg2, char *arg3)
{
return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 1);
}
static const command_rec alias_cmds[] =
{
{"Alias", add_alias, NULL, RSRC_CONF, TAKE2,
"a fakename and a realname"},
{"ScriptAlias", add_alias, "cgi-script", RSRC_CONF, TAKE2,
"a fakename and a realname"},
{"Redirect", add_redirect, (void *) HTTP_MOVED_TEMPORARILY,
OR_FILEINFO, TAKE23,
"an optional status, then document to be redirected and destination URL"},
{"AliasMatch", add_alias_regex, NULL, RSRC_CONF, TAKE2,
"a regular expression and a filename"},
{"ScriptAliasMatch", add_alias_regex, "cgi-script", RSRC_CONF, TAKE2,
"a regular expression and a filename"},
{"RedirectMatch", add_redirect_regex, (void *) HTTP_MOVED_TEMPORARILY,
OR_FILEINFO, TAKE23,
"an optional status, then a regular expression and destination URL"},
{"RedirectTemp", add_redirect, (void *) HTTP_MOVED_TEMPORARILY,
OR_FILEINFO, TAKE2,
"a document to be redirected, then the destination URL"},
{"RedirectPermanent", add_redirect, (void *) HTTP_MOVED_PERMANENTLY,
OR_FILEINFO, TAKE2,
"a document to be redirected, then the destination URL"},
{NULL}
};
static int alias_matches(const char *uri, const char *alias_fakename)
{
const char *end_fakename = alias_fakename + strlen(alias_fakename);
const char *aliasp = alias_fakename, *urip = uri;
while (aliasp < end_fakename) {
if (*aliasp == '/') {
/* any number of '/' in the alias matches any number in
* the supplied URI, but there must be at least one...
*/
if (*urip != '/')
return 0;
while (*aliasp == '/')
++aliasp;
while (*urip == '/')
++urip;
}
else {
/* Other characters are compared literally */
if (*urip++ != *aliasp++)
return 0;
}
}
/* Check last alias path component matched all the way */
if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
return 0;
/* Return number of characters from URI which matched (may be
* greater than length of alias, since we may have matched
* doubled slashes)
*/
return urip - uri;
}
static char *try_alias_list(request_rec *r, array_header *aliases, int doesc, int *status)
{
alias_entry *entries = (alias_entry *) aliases->elts;
regmatch_t regm[10];
char *found = NULL;
int i;
for (i = 0; i < aliases->nelts; ++i) {
alias_entry *p = &entries[i];
int l;
if (p->regexp) {
if (!ap_regexec(p->regexp, r->uri, p->regexp->re_nsub + 1, regm, 0)) {
if (p->real) {
found = ap_pregsub(r->pool, p->real, r->uri,
p->regexp->re_nsub + 1, regm);
if (found && doesc) {
found = ap_escape_uri(r->pool, found);
}
}
else {
/* need something non-null */
found = ap_pstrdup(r->pool, "");
}
}
}
else {
l = alias_matches(r->uri, p->fake);
if (l > 0) {
if (doesc) {
char *escurl;
escurl = ap_os_escape_path(r->pool, r->uri + l, 1);
found = ap_pstrcat(r->pool, p->real, escurl, NULL);
}
else
found = ap_pstrcat(r->pool, p->real, r->uri + l, NULL);
}
}
if (found) {
if (p->handler) { /* Set handler, and leave a note for mod_cgi */
r->handler = p->handler;
ap_table_setn(r->notes, "alias-forced-type", r->handler);
}
*status = p->redir_status;
return found;
}
}
return NULL;
}
static int translate_alias_redir(request_rec *r)
{
void *sconf = r->server->module_config;
alias_server_conf *serverconf =
(alias_server_conf *) ap_get_module_config(sconf, &alias_module);
char *ret;
int status;
if (r->uri[0] != '/' && r->uri[0] != '\0')
return DECLINED;
if ((ret = try_alias_list(r, serverconf->redirects, 1, &status)) != NULL) {
if (ap_is_HTTP_REDIRECT(status)) {
/* include QUERY_STRING if any */
if (r->args) {
ret = ap_pstrcat(r->pool, ret, "?", r->args, NULL);
}
ap_table_setn(r->headers_out, "Location", ret);
}
return status;
}
if ((ret = try_alias_list(r, serverconf->aliases, 0, &status)) != NULL) {
r->filename = ret;
return OK;
}
return DECLINED;
}
static int fixup_redir(request_rec *r)
{
void *dconf = r->per_dir_config;
alias_dir_conf *dirconf =
(alias_dir_conf *) ap_get_module_config(dconf, &alias_module);
char *ret;
int status;
/* It may have changed since last time, so try again */
if ((ret = try_alias_list(r, dirconf->redirects, 1, &status)) != NULL) {
if (ap_is_HTTP_REDIRECT(status))
ap_table_setn(r->headers_out, "Location", ret);
return status;
}
return DECLINED;
}
module MODULE_VAR_EXPORT alias_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_alias_dir_config, /* dir config creater */
merge_alias_dir_config, /* dir merger --- default is to override */
create_alias_config, /* server config */
merge_alias_config, /* merge server configs */
alias_cmds, /* command table */
NULL, /* handlers */
translate_alias_redir, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
fixup_redir, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

246
modules/mappers/mod_dir.c Normal file
View File

@ -0,0 +1,246 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_dir.c: handle default index files, and trailing-/ redirects
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_request.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
module MODULE_VAR_EXPORT dir_module;
typedef struct dir_config_struct {
array_header *index_names;
} dir_config_rec;
#define DIR_CMD_PERMS OR_INDEXES
static const char *add_index(cmd_parms *cmd, void *dummy, char *arg)
{
dir_config_rec *d = dummy;
if (!d->index_names) {
d->index_names = ap_make_array(cmd->pool, 2, sizeof(char *));
}
*(char **)ap_push_array(d->index_names) = arg;
return NULL;
}
static const command_rec dir_cmds[] =
{
{"DirectoryIndex", add_index, NULL,
DIR_CMD_PERMS, ITERATE,
"a list of file names"},
{NULL}
};
static void *create_dir_config(pool *p, char *dummy)
{
dir_config_rec *new =
(dir_config_rec *) ap_pcalloc(p, sizeof(dir_config_rec));
new->index_names = NULL;
return (void *) new;
}
static void *merge_dir_configs(pool *p, void *basev, void *addv)
{
dir_config_rec *new = (dir_config_rec *) ap_pcalloc(p, sizeof(dir_config_rec));
dir_config_rec *base = (dir_config_rec *) basev;
dir_config_rec *add = (dir_config_rec *) addv;
new->index_names = add->index_names ? add->index_names : base->index_names;
return new;
}
static int handle_dir(request_rec *r)
{
dir_config_rec *d =
(dir_config_rec *) ap_get_module_config(r->per_dir_config,
&dir_module);
char *dummy_ptr[1];
char **names_ptr;
int num_names;
int error_notfound = 0;
if (r->uri[0] == '\0' || r->uri[strlen(r->uri) - 1] != '/') {
char *ifile;
if (r->args != NULL)
ifile = ap_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri),
"/", "?", r->args, NULL);
else
ifile = ap_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri),
"/", NULL);
ap_table_setn(r->headers_out, "Location",
ap_construct_url(r->pool, ifile, r));
return HTTP_MOVED_PERMANENTLY;
}
/* KLUDGE --- make the sub_req lookups happen in the right directory.
* Fixing this in the sub_req_lookup functions themselves is difficult,
* and would probably break virtual includes...
*/
if (r->filename[strlen(r->filename) - 1] != '/') {
r->filename = ap_pstrcat(r->pool, r->filename, "/", NULL);
}
if (d->index_names) {
names_ptr = (char **)d->index_names->elts;
num_names = d->index_names->nelts;
}
else {
dummy_ptr[0] = DEFAULT_INDEX;
names_ptr = dummy_ptr;
num_names = 1;
}
for (; num_names; ++names_ptr, --num_names) {
char *name_ptr = *names_ptr;
request_rec *rr = ap_sub_req_lookup_uri(name_ptr, r);
if (rr->status == HTTP_OK && S_ISREG(rr->finfo.st_mode)) {
char *new_uri = ap_escape_uri(r->pool, rr->uri);
if (rr->args != NULL)
new_uri = ap_pstrcat(r->pool, new_uri, "?", rr->args, NULL);
else if (r->args != NULL)
new_uri = ap_pstrcat(r->pool, new_uri, "?", r->args, NULL);
ap_destroy_sub_req(rr);
ap_internal_redirect(new_uri, r);
return OK;
}
/* If the request returned a redirect, propagate it to the client */
if (ap_is_HTTP_REDIRECT(rr->status) ||
(rr->status == HTTP_NOT_ACCEPTABLE && num_names == 1)) {
ap_pool_join(r->pool, rr->pool);
error_notfound = rr->status;
r->notes = ap_overlay_tables(r->pool, r->notes, rr->notes);
r->headers_out = ap_overlay_tables(r->pool, r->headers_out,
rr->headers_out);
r->err_headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
rr->err_headers_out);
return error_notfound;
}
/* If the request returned something other than 404 (or 200),
* it means the module encountered some sort of problem. To be
* secure, we should return the error, rather than create
* along a (possibly unsafe) directory index.
*
* So we store the error, and if none of the listed files
* exist, we return the last error response we got, instead
* of a directory listing.
*/
if (rr->status && rr->status != HTTP_NOT_FOUND && rr->status != HTTP_OK)
error_notfound = rr->status;
ap_destroy_sub_req(rr);
}
if (error_notfound)
return error_notfound;
if (r->method_number != M_GET)
return DECLINED;
/* nothing for us to do, pass on through */
return DECLINED;
}
static const handler_rec dir_handlers[] =
{
{DIR_MAGIC_TYPE, handle_dir},
{NULL}
};
module MODULE_VAR_EXPORT dir_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_dir_config, /* dir config creater */
merge_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
dir_cmds, /* command table */
dir_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

919
modules/mappers/mod_imap.c Normal file
View File

@ -0,0 +1,919 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* This imagemap module started as a port of the original imagemap.c
* written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
* This version includes the mapping algorithms found in version 1.3
* of imagemap.c.
*
* Contributors to this code include:
*
* Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
*
* Eric Haines, erich@eye.com
* "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
*
* Randy Terbush, randy@zyzzyva.com
* port to Apache module format, "base_uri" and support for relative URLs
*
* James H. Cloos, Jr., cloos@jhcloos.com
* Added point datatype, using code in NCSA's version 1.8 imagemap.c
* program, as distributed with version 1.4.1 of their server.
* The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
*
* Nathan Kurz, nate@tripod.com
* Rewrite/reorganization. New handling of default, base and relative URLs.
* New Configuration directives:
* ImapMenu {none, formatted, semiformatted, unformatted}
* ImapDefault {error, nocontent, referer, menu, URL}
* ImapBase {map, referer, URL}
* Support for creating non-graphical menu added. (backwards compatible):
* Old: directive URL [x,y ...]
* New: directive URL "Menu text" [x,y ...]
* or: directive URL x,y ... "Menu text"
* Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
*
* Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
*/
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
#define MAXVERTS 100
#define X 0
#define Y 1
#define IMAP_MENU_DEFAULT "formatted"
#define IMAP_DEFAULT_DEFAULT "nocontent"
#define IMAP_BASE_DEFAULT "map"
#ifdef SUNOS4
double strtod(); /* SunOS needed this */
#endif
module MODULE_VAR_EXPORT imap_module;
typedef struct {
char *imap_menu;
char *imap_default;
char *imap_base;
} imap_conf_rec;
static void *create_imap_dir_config(pool *p, char *dummy)
{
imap_conf_rec *icr =
(imap_conf_rec *) ap_palloc(p, sizeof(imap_conf_rec));
icr->imap_menu = NULL;
icr->imap_default = NULL;
icr->imap_base = NULL;
return icr;
}
static void *merge_imap_dir_configs(pool *p, void *basev, void *addv)
{
imap_conf_rec *new = (imap_conf_rec *) ap_pcalloc(p, sizeof(imap_conf_rec));
imap_conf_rec *base = (imap_conf_rec *) basev;
imap_conf_rec *add = (imap_conf_rec *) addv;
new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
new->imap_default = add->imap_default ? add->imap_default
: base->imap_default;
new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
return new;
}
static const command_rec imap_cmds[] =
{
{"ImapMenu", ap_set_string_slot,
(void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
"the type of menu generated: none, formatted, semiformatted, unformatted"},
{"ImapDefault", ap_set_string_slot,
(void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
"the action taken if no match: error, nocontent, referer, menu, URL"},
{"ImapBase", ap_set_string_slot,
(void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
"the base for all URL's: map, referer, URL (or start of)"},
{NULL}
};
static int pointinrect(const double point[2], double coords[MAXVERTS][2])
{
double max[2], min[2];
if (coords[0][X] > coords[1][X]) {
max[0] = coords[0][X];
min[0] = coords[1][X];
}
else {
max[0] = coords[1][X];
min[0] = coords[0][X];
}
if (coords[0][Y] > coords[1][Y]) {
max[1] = coords[0][Y];
min[1] = coords[1][Y];
}
else {
max[1] = coords[1][Y];
min[1] = coords[0][Y];
}
return ((point[X] >= min[0] && point[X] <= max[0]) &&
(point[Y] >= min[1] && point[Y] <= max[1]));
}
static int pointincircle(const double point[2], double coords[MAXVERTS][2])
{
double radius1, radius2;
radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
+ ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
+ ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
return (radius2 <= radius1);
}
#define fmin(a,b) (((a)>(b))?(b):(a))
#define fmax(a,b) (((a)>(b))?(a):(b))
static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
{
int i, numverts, crossings = 0;
double x = point[X], y = point[Y];
for (numverts = 0; pgon[numverts][X] != -1 && numverts < MAXVERTS;
numverts++) {
/* just counting the vertexes */
}
for (i = 0; i < numverts; i++) {
double x1=pgon[i][X];
double y1=pgon[i][Y];
double x2=pgon[(i + 1) % numverts][X];
double y2=pgon[(i + 1) % numverts][Y];
double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1);
if ((y1 >= y) != (y2 >= y)) {
crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0;
}
if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2)
&& fmin(y1,y2) <= y && y <= fmax(y1,y2)) {
return 1;
}
}
return crossings & 0x01;
}
static int is_closer(const double point[2], double coords[MAXVERTS][2],
double *closest)
{
double dist_squared = ((point[X] - coords[0][X])
* (point[X] - coords[0][X]))
+ ((point[Y] - coords[0][Y])
* (point[Y] - coords[0][Y]));
if (point[X] < 0 || point[Y] < 0) {
return (0); /* don't mess around with negative coordinates */
}
if (*closest < 0 || dist_squared < *closest) {
*closest = dist_squared;
return (1); /* if this is the first point or is the closest yet
set 'closest' equal to this distance^2 */
}
return (0); /* if it's not the first or closest */
}
static double get_x_coord(const char *args)
{
char *endptr; /* we want it non-null */
double x_coord = -1; /* -1 is returned if no coordinate is given */
if (args == NULL) {
return (-1); /* in case we aren't passed anything */
}
while (*args && !ap_isdigit(*args) && *args != ',') {
args++; /* jump to the first digit, but not past
a comma or end */
}
x_coord = strtod(args, &endptr);
if (endptr > args) { /* if a conversion was made */
return (x_coord);
}
return (-1); /* else if no conversion was made,
or if no args was given */
}
static double get_y_coord(const char *args)
{
char *endptr; /* we want it non-null */
char *start_of_y = NULL;
double y_coord = -1; /* -1 is returned on error */
if (args == NULL) {
return (-1); /* in case we aren't passed anything */
}
start_of_y = strchr(args, ','); /* the comma */
if (start_of_y) {
start_of_y++; /* start looking at the character after
the comma */
while (*start_of_y && !ap_isdigit(*start_of_y)) {
start_of_y++; /* jump to the first digit, but not
past the end */
}
y_coord = strtod(start_of_y, &endptr);
if (endptr > start_of_y) {
return (y_coord);
}
}
return (-1); /* if no conversion was made, or
no comma was found in args */
}
/* See if string has a "quoted part", and if so set *quoted_part to
* the first character of the quoted part, then hammer a \0 onto the
* trailing quote, and set *string to point at the first character
* past the second quote.
*
* Otherwise set *quoted_part to NULL, and leave *string alone.
*/
static void read_quoted(char **string, char **quoted_part)
{
char *strp = *string;
/* assume there's no quoted part */
*quoted_part = NULL;
while (ap_isspace(*strp)) {
strp++; /* go along string until non-whitespace */
}
if (*strp == '"') { /* if that character is a double quote */
strp++; /* step over it */
*quoted_part = strp; /* note where the quoted part begins */
while (*strp && *strp != '"') {
++strp; /* skip the quoted portion */
}
*strp = '\0'; /* end the string with a NUL */
strp++; /* step over the last double quote */
*string = strp;
}
}
/*
* returns the mapped URL or NULL.
*/
static char *imap_url(request_rec *r, const char *base, const char *value)
{
/* translates a value into a URL. */
int slen, clen;
char *string_pos = NULL;
const char *string_pos_const = NULL;
char *directory = NULL;
const char *referer = NULL;
char *my_base;
if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
return ap_construct_url(r->pool, r->uri, r);
}
if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
return ap_pstrdup(r->pool, value); /* these are handled elsewhere,
so just copy them */
}
if (!strcasecmp(value, "referer")) {
referer = ap_table_get(r->headers_in, "Referer");
if (referer && *referer) {
return ap_pstrdup(r->pool, referer);
}
else {
/* XXX: This used to do *value = '\0'; ... which is totally bogus
* because it hammers the passed in value, which can be a string
* constant, or part of a config, or whatever. Total garbage.
* This works around that without changing the rest of this
* code much
*/
value = ""; /* if 'referer' but no referring page,
null the value */
}
}
string_pos_const = value;
while (ap_isalpha(*string_pos_const)) {
string_pos_const++; /* go along the URL from the map
until a non-letter */
}
if (*string_pos_const == ':') {
/* if letters and then a colon (like http:) */
/* it's an absolute URL, so use it! */
return ap_pstrdup(r->pool, value);
}
if (!base || !*base) {
if (value && *value) {
return ap_pstrdup(r->pool, value); /* no base: use what is given */
}
/* no base, no value: pick a simple default */
return ap_construct_url(r->pool, "/", r);
}
/* must be a relative URL to be combined with base */
if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3)
|| !strcmp(value, ".."))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"invalid base directive in map file: %s", r->uri);
return NULL;
}
my_base = ap_pstrdup(r->pool, base);
string_pos = my_base;
while (*string_pos) {
if (*string_pos == '/' && *(string_pos + 1) == '/') {
string_pos += 2; /* if there are two slashes, jump over them */
continue;
}
if (*string_pos == '/') { /* the first single slash */
if (value[0] == '/') {
*string_pos = '\0';
} /* if the URL from the map starts from root,
end the base URL string at the first single
slash */
else {
directory = string_pos; /* save the start of
the directory portion */
string_pos = strrchr(string_pos, '/'); /* now reuse
string_pos */
string_pos++; /* step over that last slash */
*string_pos = '\0';
} /* but if the map url is relative, leave the
slash on the base (if there is one) */
break;
}
string_pos++; /* until we get to the end of my_base without
finding a slash by itself */
}
while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
if (directory && (slen = strlen(directory))) {
/* for each '..', knock a directory off the end
by ending the string right at the last slash.
But only consider the directory portion: don't eat
into the server name. And only try if a directory
portion was found */
clen = slen - 1;
while ((slen - clen) == 1) {
if ((string_pos = strrchr(directory, '/'))) {
*string_pos = '\0';
}
clen = strlen(directory);
if (clen == 0) {
break;
}
}
value += 2; /* jump over the '..' that we found in the
value */
}
else if (directory) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"invalid directory name in map file: %s", r->uri);
return NULL;
}
if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) {
value++; /* step over the '/' if there are more '..'
to do. This way, we leave the starting
'/' on value after the last '..', but get
rid of it otherwise */
}
} /* by this point, value does not start
with '..' */
if (value && *value) {
return ap_pstrcat(r->pool, my_base, value, NULL);
}
return my_base;
}
static int imap_reply(request_rec *r, char *redirect)
{
if (!strcasecmp(redirect, "error")) {
return SERVER_ERROR; /* they actually requested an error! */
}
if (!strcasecmp(redirect, "nocontent")) {
return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
}
if (redirect && *redirect) {
ap_table_setn(r->headers_out, "Location", redirect);
return REDIRECT; /* must be a URL, so redirect to it */
}
return SERVER_ERROR;
}
static void menu_header(request_rec *r, char *menu)
{
r->content_type = "text/html";
ap_send_http_header(r);
ap_hard_timeout("send menu", r); /* killed in menu_footer */
ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ", r->uri,
"</title>\n</head><body>\n", NULL);
if (!strcasecmp(menu, "formatted")) {
ap_rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
}
return;
}
static void menu_blank(request_rec *r, char *menu)
{
if (!strcasecmp(menu, "formatted")) {
ap_rputs("\n", r);
}
if (!strcasecmp(menu, "semiformatted")) {
ap_rputs("<br>\n", r);
}
if (!strcasecmp(menu, "unformatted")) {
ap_rputs("\n", r);
}
return;
}
static void menu_comment(request_rec *r, char *menu, char *comment)
{
if (!strcasecmp(menu, "formatted")) {
ap_rputs("\n", r); /* print just a newline if 'formatted' */
}
if (!strcasecmp(menu, "semiformatted") && *comment) {
ap_rvputs(r, comment, "\n", NULL);
}
if (!strcasecmp(menu, "unformatted") && *comment) {
ap_rvputs(r, comment, "\n", NULL);
}
return; /* comments are ignored in the
'formatted' form */
}
static void menu_default(request_rec *r, char *menu, char *href, char *text)
{
if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
return; /* don't print such lines, these aren't
really href's */
}
if (!strcasecmp(menu, "formatted")) {
ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
"</a></pre>\n", NULL);
}
if (!strcasecmp(menu, "semiformatted")) {
ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
"</a></pre>\n", NULL);
}
if (!strcasecmp(menu, "unformatted")) {
ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
}
return;
}
static void menu_directive(request_rec *r, char *menu, char *href, char *text)
{
if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
return; /* don't print such lines, as this isn't
really an href */
}
if (!strcasecmp(menu, "formatted")) {
ap_rvputs(r, "<pre> <a href=\"", href, "\">", text,
"</a></pre>\n", NULL);
}
if (!strcasecmp(menu, "semiformatted")) {
ap_rvputs(r, "<pre> <a href=\"", href, "\">", text,
"</a></pre>\n", NULL);
}
if (!strcasecmp(menu, "unformatted")) {
ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
}
return;
}
static void menu_footer(request_rec *r)
{
ap_rputs("\n\n</body>\n</html>\n", r); /* finish the menu */
ap_kill_timeout(r);
}
static int imap_handler(request_rec *r)
{
char input[MAX_STRING_LEN];
char *directive;
char *value;
char *href_text;
char *base;
char *redirect;
char *mapdflt;
char *closest = NULL;
double closest_yet = -1;
double testpoint[2];
double pointarray[MAXVERTS + 1][2];
int vertex;
char *string_pos;
int showmenu = 0;
imap_conf_rec *icr = ap_get_module_config(r->per_dir_config, &imap_module);
char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
char *imap_default = icr->imap_default
? icr->imap_default : IMAP_DEFAULT_DEFAULT;
char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
configfile_t *imap;
if (r->method_number != M_GET) {
return DECLINED;
}
imap = ap_pcfg_openfile(r->pool, r->filename);
if (!imap) {
return NOT_FOUND;
}
base = imap_url(r, NULL, imap_base); /* set base according
to default */
if (!base) {
return HTTP_INTERNAL_SERVER_ERROR;
}
mapdflt = imap_url(r, NULL, imap_default); /* and default to
global default */
if (!mapdflt) {
return HTTP_INTERNAL_SERVER_ERROR;
}
testpoint[X] = get_x_coord(r->args);
testpoint[Y] = get_y_coord(r->args);
if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
(testpoint[X] == 0 && testpoint[Y] == 0)) {
/* if either is -1 or if both are zero (new Lynx) */
/* we don't have valid coordinates */
testpoint[X] = -1;
testpoint[Y] = -1;
if (strncasecmp(imap_menu, "none", 2)) {
showmenu = 1; /* show the menu _unless_ ImapMenu is
'none' or 'no' */
}
}
if (showmenu) { /* send start of imagemap menu if
we're going to */
menu_header(r, imap_menu);
}
while (!ap_cfg_getline(input, sizeof(input), imap)) {
if (!input[0]) {
if (showmenu) {
menu_blank(r, imap_menu);
}
continue;
}
if (input[0] == '#') {
if (showmenu) {
menu_comment(r, imap_menu, input + 1);
}
continue;
} /* blank lines and comments are ignored
if we aren't printing a menu */
/* find the first two space delimited fields, recall that
* ap_cfg_getline has removed leading/trailing whitespace.
*
* note that we're tokenizing as we go... if we were to use the
* ap_getword() class of functions we would end up allocating extra
* memory for every line of the map file
*/
string_pos = input;
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
directive = string_pos;
while (*string_pos && !ap_isspace(*string_pos)) { /* past directive */
++string_pos;
}
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
*string_pos++ = '\0';
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
while(*string_pos && ap_isspace(*string_pos)) { /* past whitespace */
++string_pos;
}
value = string_pos;
while (*string_pos && !ap_isspace(*string_pos)) { /* past value */
++string_pos;
}
if (ap_isspace(*string_pos)) {
*string_pos++ = '\0';
}
else {
/* end of input, don't advance past it */
*string_pos = '\0';
}
if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */
base = imap_url(r, NULL, value);
if (!base) {
goto menu_bail;
}
continue; /* base is never printed to a menu */
}
read_quoted(&string_pos, &href_text);
if (!strcasecmp(directive, "default")) { /* default */
mapdflt = imap_url(r, NULL, value);
if (!mapdflt) {
goto menu_bail;
}
if (showmenu) { /* print the default if there's a menu */
redirect = imap_url(r, base, mapdflt);
if (!redirect) {
goto menu_bail;
}
menu_default(r, imap_menu, redirect,
href_text ? href_text : mapdflt);
}
continue;
}
vertex = 0;
while (vertex < MAXVERTS &&
sscanf(string_pos, "%lf%*[, ]%lf",
&pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
/* Now skip what we just read... we can't use ANSIism %n */
while (ap_isspace(*string_pos)) { /* past whitespace */
string_pos++;
}
while (ap_isdigit(*string_pos)) { /* and the 1st number */
string_pos++;
}
string_pos++; /* skip the ',' */
while (ap_isspace(*string_pos)) { /* past any more whitespace */
string_pos++;
}
while (ap_isdigit(*string_pos)) { /* 2nd number */
string_pos++;
}
vertex++;
} /* so long as there are more vertices to
read, and we have room, read them in.
We start where we left off of the last
sscanf, not at the beginning. */
pointarray[vertex][X] = -1; /* signals the end of vertices */
if (showmenu) {
if (!href_text) {
read_quoted(&string_pos, &href_text); /* href text could
be here instead */
}
redirect = imap_url(r, base, value);
if (!redirect) {
goto menu_bail;
}
menu_directive(r, imap_menu, redirect,
href_text ? href_text : value);
continue;
}
/* note that we don't make it past here if we are making a menu */
if (testpoint[X] == -1 || pointarray[0][X] == -1) {
continue; /* don't try the following tests if testpoints
are invalid, or if there are no
coordinates */
}
if (!strcasecmp(directive, "poly")) { /* poly */
if (pointinpoly(testpoint, pointarray)) {
ap_cfg_closefile(imap);
redirect = imap_url(r, base, value);
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
if (!strcasecmp(directive, "circle")) { /* circle */
if (pointincircle(testpoint, pointarray)) {
ap_cfg_closefile(imap);
redirect = imap_url(r, base, value);
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
if (!strcasecmp(directive, "rect")) { /* rect */
if (pointinrect(testpoint, pointarray)) {
ap_cfg_closefile(imap);
redirect = imap_url(r, base, value);
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
if (!strcasecmp(directive, "point")) { /* point */
if (is_closer(testpoint, pointarray, &closest_yet)) {
closest = ap_pstrdup(r->pool, value);
}
continue;
} /* move on to next line whether it's
closest or not */
} /* nothing matched, so we get another line! */
ap_cfg_closefile(imap); /* we are done with the map file; close it */
if (showmenu) {
menu_footer(r); /* finish the menu and we are done */
return OK;
}
if (closest) { /* if a 'point' directive has been seen */
redirect = imap_url(r, base, closest);
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
if (mapdflt) { /* a default should be defined, even if
only 'nocontent' */
redirect = imap_url(r, base, mapdflt);
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
return HTTP_INTERNAL_SERVER_ERROR; /* If we make it this far,
we failed. They lose! */
need_2_fields:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"map file %s, line %d syntax error: requires at "
"least two fields", r->uri, imap->line_number);
/* fall through */
menu_bail:
ap_cfg_closefile(imap);
if (showmenu) {
/* There's not much else we can do ... we've already sent the headers
* to the client.
*/
ap_rputs("\n\n[an internal server error occured]\n", r);
menu_footer(r);
return OK;
}
return HTTP_INTERNAL_SERVER_ERROR;
}
static const handler_rec imap_handlers[] =
{
{IMAP_MAGIC_TYPE, imap_handler},
{"imap-file", imap_handler},
{NULL}
};
module MODULE_VAR_EXPORT imap_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_imap_dir_config, /* dir config creater */
merge_imap_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
imap_cmds, /* command table */
imap_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,497 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#ifndef _MOD_REWRITE_H
#define _MOD_REWRITE_H 1
/*
** _ _ _
** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
** |_____|
**
** URL Rewriting Module
**
** This module uses a rule-based rewriting engine (based on a
** regular-expression parser) to rewrite requested URLs on the fly.
**
** It supports an unlimited number of additional rule conditions (which can
** operate on a lot of variables, even on HTTP headers) for granular
** matching and even external database lookups (either via plain text
** tables, DBM hash files or even external processes) for advanced URL
** substitution.
**
** It operates on the full URLs (including the PATH_INFO part) both in
** per-server context (httpd.conf) and per-dir context (.htaccess) and even
** can generate QUERY_STRING parts on result. The rewriting result finally
** can lead to internal subprocessing, external request redirection or even
** to internal proxy throughput.
**
** This module was originally written in April 1996 and
** gifted exclusively to the The Apache Group in July 1997 by
**
** Ralf S. Engelschall
** rse@engelschall.com
** www.engelschall.com
*/
/* Include from the underlaying Unix system ... */
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Include from the Apache server ... */
#define CORE_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_conf_globals.h"
#include "http_request.h"
#include "http_core.h"
#include "http_log.h"
#include "http_vhost.h"
/*
* The key in the r->notes table wherein we store our accumulated
* Vary values, and the one used for per-condition checks in a chain.
*/
#define VARY_KEY "rewrite-Vary"
#define VARY_KEY_THIS "rewrite-Vary-this"
/* The NDBM support:
* We support only NDBM files.
* But we have to stat the file for the mtime,
* so we also need to know the file extension
*/
#ifndef NO_DBM_REWRITEMAP
#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
&& __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
#include <db1/ndbm.h>
#else
#include <ndbm.h>
#endif
#if defined(DBM_SUFFIX)
#define NDBM_FILE_SUFFIX DBM_SUFFIX
#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM))
#define NDBM_FILE_SUFFIX ".db"
#else
#define NDBM_FILE_SUFFIX ".pag"
#endif
#endif
/* The locking support:
* Try to determine whether we should use fcntl() or flock().
* Would be better ap_config.h could provide this... :-(
*/
#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
#define USE_FCNTL 1
#include <fcntl.h>
#endif
#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
#define USE_FLOCK 1
#include <sys/file.h>
#endif
#if !defined(USE_FCNTL) && !defined(USE_FLOCK)
#define USE_FLOCK 1
#if !defined(MPE) && !defined(WIN32) && !defined(__TANDEM)
#include <sys/file.h>
#endif
#ifndef LOCK_UN
#undef USE_FLOCK
#define USE_FCNTL 1
#include <fcntl.h>
#endif
#endif
#ifdef AIX
#undef USE_FLOCK
#define USE_FCNTL 1
#include <fcntl.h>
#endif
#ifdef WIN32
#undef USE_FCNTL
#define USE_LOCKING
#include <sys/locking.h>
#endif
/*
**
** Some defines
**
*/
#define ENVVAR_SCRIPT_URL "SCRIPT_URL"
#define ENVVAR_SCRIPT_URI "SCRIPT_URI"
#ifndef SUPPORT_DBM_REWRITEMAP
#define SUPPORT_DBM_REWRITEMAP 0
#endif
#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
#define CONDFLAG_NONE 1<<0
#define CONDFLAG_NOCASE 1<<1
#define CONDFLAG_NOTMATCH 1<<2
#define CONDFLAG_ORNEXT 1<<3
#define RULEFLAG_NONE 1<<0
#define RULEFLAG_FORCEREDIRECT 1<<1
#define RULEFLAG_LASTRULE 1<<2
#define RULEFLAG_NEWROUND 1<<3
#define RULEFLAG_CHAIN 1<<4
#define RULEFLAG_IGNOREONSUBREQ 1<<5
#define RULEFLAG_NOTMATCH 1<<6
#define RULEFLAG_PROXY 1<<7
#define RULEFLAG_PASSTHROUGH 1<<8
#define RULEFLAG_FORBIDDEN 1<<9
#define RULEFLAG_GONE 1<<10
#define RULEFLAG_QSAPPEND 1<<11
#define RULEFLAG_NOCASE 1<<12
#define MAPTYPE_TXT 1<<0
#define MAPTYPE_DBM 1<<1
#define MAPTYPE_PRG 1<<2
#define MAPTYPE_INT 1<<3
#define MAPTYPE_RND 1<<4
#define ENGINE_DISABLED 1<<0
#define ENGINE_ENABLED 1<<1
#define OPTION_NONE 1<<0
#define OPTION_INHERIT 1<<1
#define CACHEMODE_TS 1<<0
#define CACHEMODE_TTL 1<<1
#define CACHE_TLB_ROWS 1024
#define CACHE_TLB_COLS 4
#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif
#ifndef NO
#define NO FALSE
#define YES TRUE
#endif
#ifndef RAND_MAX
#define RAND_MAX 32767
#endif
#ifndef LONG_STRING_LEN
#define LONG_STRING_LEN 2048
#endif
#define MAX_ENV_FLAGS 15
#define MAX_NMATCH 10
/*
**
** our private data structures we handle with
**
*/
/* the list structures for holding the mapfile information
* and the rewrite rules
*/
typedef struct {
char *name; /* the name of the map */
char *datafile; /* filename for map data files */
char *checkfile; /* filename to check for map existence */
int type; /* the type of the map */
int fpin; /* in file pointer for program maps */
int fpout; /* out file pointer for program maps */
int fperr; /* err file pointer for program maps */
char *(*func)(request_rec *, /* function pointer for internal maps */
char *);
} rewritemap_entry;
typedef struct {
char *input; /* Input string of RewriteCond */
char *pattern; /* the RegExp pattern string */
regex_t *regexp;
int flags; /* Flags which control the match */
} rewritecond_entry;
typedef struct {
array_header *rewriteconds; /* the corresponding RewriteCond entries */
char *pattern; /* the RegExp pattern string */
regex_t *regexp; /* the RegExp pattern compilation */
char *output; /* the Substitution string */
int flags; /* Flags which control the substitution */
char *forced_mimetype; /* forced MIME type of substitution */
int forced_responsecode; /* forced HTTP redirect response status */
char *env[MAX_ENV_FLAGS+1]; /* added environment variables */
int skip; /* number of next rules to skip */
} rewriterule_entry;
/* the per-server or per-virtual-server configuration
* statically generated once on startup for every server
*/
typedef struct {
int state; /* the RewriteEngine state */
int options; /* the RewriteOption state */
char *rewritelogfile; /* the RewriteLog filename */
int rewritelogfp; /* the RewriteLog open filepointer */
int rewriteloglevel; /* the RewriteLog level of verbosity */
array_header *rewritemaps; /* the RewriteMap entries */
array_header *rewriteconds; /* the RewriteCond entries (temporary) */
array_header *rewriterules; /* the RewriteRule entries */
server_rec *server; /* the corresponding server indicator */
} rewrite_server_conf;
/* the per-directory configuration
* generated on-the-fly by Apache server for current request
*/
typedef struct {
int state; /* the RewriteEngine state */
int options; /* the RewriteOption state */
array_header *rewriteconds; /* the RewriteCond entries (temporary) */
array_header *rewriterules; /* the RewriteRule entries */
char *directory; /* the directory where it applies */
char *baseurl; /* the base-URL where it applies */
} rewrite_perdir_conf;
/* the cache structures,
* a 4-way hash table with LRU functionality
*/
typedef struct cacheentry {
time_t time;
char *key;
char *value;
} cacheentry;
typedef struct tlbentry {
int t[CACHE_TLB_COLS];
} cachetlbentry;
typedef struct cachelist {
char *resource;
array_header *entries;
array_header *tlb;
} cachelist;
typedef struct cache {
pool *pool;
array_header *lists;
} cache;
/* the regex structure for the
* substitution of backreferences
*/
typedef struct backrefinfo {
char *source;
int nsub;
regmatch_t regmatch[10];
} backrefinfo;
/*
**
** forward declarations
**
*/
/* config structure handling */
static void *config_server_create(pool *p, server_rec *s);
static void *config_server_merge (pool *p, void *basev, void *overridesv);
static void *config_perdir_create(pool *p, char *path);
static void *config_perdir_merge (pool *p, void *basev, void *overridesv);
/* config directive handling */
static const char *cmd_rewriteengine(cmd_parms *cmd,
rewrite_perdir_conf *dconf, int flag);
static const char *cmd_rewriteoptions(cmd_parms *cmd,
rewrite_perdir_conf *dconf,
char *option);
static const char *cmd_rewriteoptions_setoption(pool *p, int *options,
char *name);
static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, char *a1);
static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1);
static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, char *a1,
char *a2);
static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, char *a1);
static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf,
char *a1);
static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf,
char *str);
static const char *cmd_rewritecond_parseflagfield(pool *p,
rewritecond_entry *new,
char *str);
static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg,
char *key, char *val);
static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
char *str);
static const char *cmd_rewriterule_parseflagfield(pool *p,
rewriterule_entry *new,
char *str);
static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg,
char *key, char *val);
/* initialisation */
static void init_module(server_rec *s, pool *p);
static void init_child(server_rec *s, pool *p);
/* runtime hooks */
static int hook_uri2file (request_rec *r);
static int hook_mimetype (request_rec *r);
static int hook_fixup (request_rec *r);
static int handler_redirect(request_rec *r);
/* rewriting engine */
static int apply_rewrite_list(request_rec *r, array_header *rewriterules,
char *perdir);
static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p,
char *perdir);
static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p,
char *perdir, backrefinfo *briRR,
backrefinfo *briRC);
/* URI transformation function */
static void splitout_queryargs(request_rec *r, int qsappend);
static void fully_qualify_uri(request_rec *r);
static void reduce_uri(request_rec *r);
static void expand_backref_inbuffer(pool *p, char *buf, int nbuf,
backrefinfo *bri, char c);
static char *expand_tildepaths(request_rec *r, char *uri);
static void expand_map_lookups(request_rec *r, char *uri, int uri_len);
/* rewrite map support functions */
static char *lookup_map(request_rec *r, char *name, char *key);
static char *lookup_map_txtfile(request_rec *r, char *file, char *key);
#ifndef NO_DBM_REWRITEMAP
static char *lookup_map_dbmfile(request_rec *r, char *file, char *key);
#endif
static char *lookup_map_program(request_rec *r, int fpin,
int fpout, char *key);
static char *lookup_map_internal(request_rec *r,
char *(*func)(request_rec *r, char *key),
char *key);
static char *rewrite_mapfunc_toupper(request_rec *r, char *key);
static char *rewrite_mapfunc_tolower(request_rec *r, char *key);
static char *rewrite_mapfunc_escape(request_rec *r, char *key);
static char *rewrite_mapfunc_unescape(request_rec *r, char *key);
static char *select_random_value_part(request_rec *r, char *value);
static void rewrite_rand_init(void);
static int rewrite_rand(int l, int h);
/* rewriting logfile support */
static void open_rewritelog(server_rec *s, pool *p);
static void rewritelog(request_rec *r, int level, const char *text, ...)
__attribute__((format(printf,3,4)));
static char *current_logtime(request_rec *r);
/* rewriting lockfile support */
static void rewritelock_create(server_rec *s, pool *p);
static void rewritelock_open(server_rec *s, pool *p);
static void rewritelock_remove(void *data);
static void rewritelock_alloc(request_rec *r);
static void rewritelock_free(request_rec *r);
/* program map support */
static void run_rewritemap_programs(server_rec *s, pool *p);
static int rewritemap_program_child(void *cmd, child_info *pinfo);
/* env variable support */
static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len);
static char *expand_variables(request_rec *r, char *str);
static char *lookup_variable(request_rec *r, char *var);
static char *lookup_header(request_rec *r, const char *name);
/* caching functions */
static cache *init_cache(pool *p);
static char *get_cache_string(cache *c, char *res, int mode, time_t mtime,
char *key);
static void set_cache_string(cache *c, char *res, int mode, time_t mtime,
char *key, char *value);
static cacheentry *retrieve_cache_string(cache *c, char *res, char *key);
static void store_cache_string(cache *c, char *res, cacheentry *ce);
/* misc functions */
static char *subst_prefix_path(request_rec *r, char *input, char *match,
char *subst);
static int parseargline(char *str, char **a1, char **a2, char **a3);
static int prefix_stat(const char *path, struct stat *sb);
static void add_env_variable(request_rec *r, char *s);
/* File locking */
static void fd_lock(request_rec *r, int fd);
static void fd_unlock(request_rec *r, int fd);
/* Lexicographic Comparison */
static int compare_lexicography(char *cpNum1, char *cpNum2);
#endif /* _MOD_REWRITE_H */
/*EOF*/

360
modules/mappers/mod_so.c Normal file
View File

@ -0,0 +1,360 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* This module is used to load Apache modules at runtime. This means that the
* server functionality can be extended without recompiling and even without
* taking the server down at all. Only a HUP or USR1 signal needs to be send
* to the server to reload the dynamically loaded modules.
*
* To use, you'll first need to build your module as a shared library, then
* update your configuration (httpd.conf) to get the Apache core to load the
* module at start-up.
*
* The easiest way to build a module as a shared library is to use the
* `SharedModule' command in the Configuration file, instead of `AddModule'.
* You should also change the file extension from `.o' to `.so'. So, for
* example, to build the status module as a shared library edit Configuration
* and change
* AddModule modules/standard/mod_status.o
* to
* SharedModule modules/standard/mod_status.so
*
* Run Configure and make. Now Apache's httpd binary will _not_ include
* mod_status. Instead a shared object called mod_status.so will be build, in
* the modules/standard directory. You can build most of the modules as shared
* libraries like this.
*
* To use the shared module, move the .so file(s) into an appropriate
* directory. You might like to create a directory called "modules" under you
* server root for this (e.g. /usr/local/httpd/modules).
*
* Then edit your conf/httpd.conf file, and add LoadModule lines. For
* example
* LoadModule status_module modules/mod_status.so
*
* The first argument is the module's structure name (look at the end of the
* module source to find this). The second option is the path to the module
* file, relative to the server root. Put these directives right at the top
* of your httpd.conf file.
*
* Now you can start Apache. A message will be logged at "debug" level to your
* error_log to confirm that the module(s) are loaded (use "LogLevel debug"
* directive to get these log messages).
*
* If you edit the LoadModule directives while the server is live you can get
* Apache to re-load the modules by sending it a HUP or USR1 signal as normal.
* You can use this to dynamically change the capability of your server
* without bringing it down.
*
* Because currently there is only limited built-in support in the Configure
* script for creating the shared library files (`.so'), please consult your
* vendors cc(1), ld(1) and dlopen(3) manpages to find out the appropriate
* compiler and linker flags and insert them manually into the Configuration
* file under CFLAGS_SHLIB, LDFLAGS_SHLIB and LDFLAGS_SHLIB_EXPORT.
*
* If you still have problems figuring out the flags both try the paper
* http://developer.netscape.com/library/documentation/enterprise
* /unix/svrplug.htm#1013807
* or install a Perl 5 interpreter on your platform and then run the command
*
* $ perl -V:usedl -V:ccdlflags -V:cccdlflags -V:lddlflags
*
* This gives you what type of dynamic loading Perl 5 uses on your platform
* and which compiler and linker flags Perl 5 uses to create the shared object
* files.
*
* Another location where you can find useful hints is the `ltconfig' script
* of the GNU libtool 1.2 package. Search for your platform name inside the
* various "case" constructs.
*
*/
#define CORE_PRIVATE
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
module MODULE_VAR_EXPORT so_module;
/*
* Server configuration to keep track of actually
* loaded modules and the corresponding module name.
*/
typedef struct moduleinfo {
char *name;
module *modp;
} moduleinfo;
typedef struct so_server_conf {
array_header *loaded_modules;
} so_server_conf;
static void *so_sconf_create(pool *p, server_rec *s)
{
so_server_conf *soc;
soc = (so_server_conf *)ap_pcalloc(p, sizeof(so_server_conf));
soc->loaded_modules = ap_make_array(p, DYNAMIC_MODULE_LIMIT,
sizeof(moduleinfo));
#ifndef NO_DLOPEN
ap_os_dso_init();
#endif
return (void *)soc;
}
#ifndef NO_DLOPEN
/*
* This is the cleanup for a loaded shared object. It unloads the module.
* This is called as a cleanup function from the core.
*/
static void unload_module(moduleinfo *modi)
{
/* only unload if module information is still existing */
if (modi->modp == NULL)
return;
/* remove the module pointer from the core structure */
ap_remove_loaded_module(modi->modp);
/* unload the module space itself */
ap_os_dso_unload((ap_os_dso_handle_t)modi->modp->dynamic_load_handle);
/* destroy the module information */
modi->modp = NULL;
modi->name = NULL;
}
/*
* This is the cleanup routine for files loaded by
* load_file(). Unfortunately we don't keep a record of the filename
* that was loaded, so we can't report the unload for debug purposes
* or include the filename in error message.
*/
static void unload_file(void *handle)
{
ap_os_dso_unload((ap_os_dso_handle_t)handle);
}
/*
* This is called for the directive LoadModule and actually loads
* a shared object file into the address space of the server process.
*/
static const char *load_module(cmd_parms *cmd, void *dummy,
char *modname, char *filename)
{
ap_os_dso_handle_t modhandle;
module *modp;
const char *szModuleFile=ap_server_root_relative(cmd->pool, filename);
so_server_conf *sconf;
moduleinfo *modi;
moduleinfo *modie;
int i;
/*
* check for already existing module
* If it already exists, we have nothing to do
*/
sconf = (so_server_conf *)ap_get_module_config(cmd->server->module_config,
&so_module);
modie = (moduleinfo *)sconf->loaded_modules->elts;
for (i = 0; i < sconf->loaded_modules->nelts; i++) {
modi = &modie[i];
if (modi->name != NULL && strcmp(modi->name, modname) == 0)
return NULL;
}
modi = ap_push_array(sconf->loaded_modules);
modi->name = modname;
/*
* Load the file into the Apache address space
*/
if (!(modhandle = ap_os_dso_load(szModuleFile))) {
const char *my_error = ap_os_dso_error();
return ap_pstrcat (cmd->pool, "Cannot load ", szModuleFile,
" into server: ",
my_error ? my_error : "(reason unknown)",
NULL);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
"loaded module %s", modname);
/*
* Retrieve the pointer to the module structure through the module name:
* First with the hidden variant (prefix `AP_') and then with the plain
* symbol name.
*/
if (!(modp = (module *)(ap_os_dso_sym(modhandle, modname)))) {
return ap_pstrcat(cmd->pool, "Can't locate API module structure `", modname,
"' in file ", szModuleFile, ": ", ap_os_dso_error(), NULL);
}
modi->modp = modp;
modp->dynamic_load_handle = (void *)modhandle;
/*
* Make sure the found module structure is really a module structure
*
*/
if (modp->magic != MODULE_MAGIC_COOKIE) {
return ap_pstrcat(cmd->pool, "API module structure `", modname,
"' in file ", szModuleFile, " is garbled -"
" perhaps this is not an Apache module DSO?", NULL);
}
/*
* Add this module to the Apache core structures
*/
ap_add_loaded_module(modp);
/*
* Register a cleanup in the config pool (normally pconf). When
* we do a restart (or shutdown) this cleanup will cause the
* shared object to be unloaded.
*/
ap_register_cleanup(cmd->pool, modi,
(void (*)(void*))unload_module, ap_null_cleanup);
/*
* Finally we need to run the configuration process for the module
*/
ap_single_module_configure(cmd->pool, cmd->server, modp);
return NULL;
}
/*
* This implements the LoadFile directive and loads an arbitrary
* shared object file into the adress space of the server process.
*/
static const char *load_file(cmd_parms *cmd, void *dummy, char *filename)
{
ap_os_dso_handle_t handle;
char *file;
file = ap_server_root_relative(cmd->pool, filename);
if (!(handle = ap_os_dso_load(file))) {
const char *my_error = ap_os_dso_error();
return ap_pstrcat (cmd->pool, "Cannot load ", filename,
" into server:",
my_error ? my_error : "(reason unknown)",
NULL);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
"loaded file %s", filename);
ap_register_cleanup(cmd->pool, (void *)handle, unload_file, ap_null_cleanup);
return NULL;
}
#else /* not NO_DLOPEN */
static const char *load_file(cmd_parms *cmd, void *dummy, char *filename)
{
fprintf(stderr, "WARNING: LoadFile not supported on this platform\n");
return NULL;
}
static const char *load_module(cmd_parms *cmd, void *dummy,
char *modname, char *filename)
{
fprintf(stderr, "WARNING: LoadModule not supported on this platform\n");
return NULL;
}
#endif /* NO_DLOPEN */
static const command_rec so_cmds[] = {
{ "LoadModule", load_module, NULL, RSRC_CONF, TAKE2,
"a module name and the name of a shared object file to load it from"},
{ "LoadFile", load_file, NULL, RSRC_CONF, ITERATE,
"shared object file or library to load into the server at runtime"},
{ NULL }
};
module MODULE_VAR_EXPORT so_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* create per-dir config */
NULL, /* merge per-dir config */
so_sconf_create, /* server config */
NULL, /* merge server config */
so_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixer_upper */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,558 @@
#define WANT_BASENAME_MATCH
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
/* mod_speling.c - by Alexei Kosut <akosut@organic.com> June, 1996
*
* This module is transparent, and simple. It attempts to correct
* misspellings of URLs that users might have entered, namely by checking
* capitalizations. If it finds a match, it sends a redirect.
*
* 08-Aug-1997 <Martin.Kraemer@Mch.SNI.De>
* o Upgraded module interface to apache_1.3a2-dev API (more NULL's in
* speling_module).
* o Integrated tcsh's "spelling correction" routine which allows one
* misspelling (character insertion/omission/typo/transposition).
* Rewrote it to ignore case as well. This ought to catch the majority
* of misspelled requests.
* o Commented out the second pass where files' suffixes are stripped.
* Given the better hit rate of the first pass, this rather ugly
* (request index.html, receive index.db ?!?!) solution can be
* omitted.
* o wrote a "kind of" html page for mod_speling
*
* Activate it with "CheckSpelling On"
*/
MODULE_VAR_EXPORT module speling_module;
typedef struct {
int enabled;
} spconfig;
/*
* Create a configuration specific to this module for a server or directory
* location, and fill it with the default settings.
*
* The API says that in the absence of a merge function, the record for the
* closest ancestor is used exclusively. That's what we want, so we don't
* bother to have such a function.
*/
static void *mkconfig(pool *p)
{
spconfig *cfg = ap_pcalloc(p, sizeof(spconfig));
cfg->enabled = 0;
return cfg;
}
/*
* Respond to a callback to create configuration record for a server or
* vhost environment.
*/
static void *create_mconfig_for_server(pool *p, server_rec *s)
{
return mkconfig(p);
}
/*
* Respond to a callback to create a config record for a specific directory.
*/
static void *create_mconfig_for_directory(pool *p, char *dir)
{
return mkconfig(p);
}
/*
* Handler for the CheckSpelling directive, which is FLAG.
*/
static const char *set_speling(cmd_parms *cmd, void *mconfig, int arg)
{
spconfig *cfg = (spconfig *) mconfig;
cfg->enabled = arg;
return NULL;
}
/*
* Define the directives specific to this module. This structure is referenced
* later by the 'module' structure.
*/
static const command_rec speling_cmds[] =
{
{ "CheckSpelling", set_speling, NULL, OR_OPTIONS, FLAG,
"whether or not to fix miscapitalized/misspelled requests" },
{ NULL }
};
typedef enum {
SP_IDENTICAL = 0,
SP_MISCAPITALIZED = 1,
SP_TRANSPOSITION = 2,
SP_MISSINGCHAR = 3,
SP_EXTRACHAR = 4,
SP_SIMPLETYPO = 5,
SP_VERYDIFFERENT = 6
} sp_reason;
static const char *sp_reason_str[] =
{
"identical",
"miscapitalized",
"transposed characters",
"character missing",
"extra character",
"mistyped character",
"common basename",
};
typedef struct {
const char *name;
sp_reason quality;
} misspelled_file;
/*
* spdist() is taken from Kernighan & Pike,
* _The_UNIX_Programming_Environment_
* and adapted somewhat to correspond better to psychological reality.
* (Note the changes to the return values)
*
* According to Pollock and Zamora, CACM April 1984 (V. 27, No. 4),
* page 363, the correct order for this is:
* OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION
* thus, it was exactly backwards in the old version. -- PWP
*
* This routine was taken out of tcsh's spelling correction code
* (tcsh-6.07.04) and re-converted to apache data types ("char" type
* instead of tcsh's NLS'ed "Char"). Plus it now ignores the case
* during comparisons, so is a "approximate strcasecmp()".
* NOTE that is still allows only _one_ real "typo",
* it does NOT try to correct multiple errors.
*/
static sp_reason spdist(const char *s, const char *t)
{
for (; ap_tolower(*s) == ap_tolower(*t); t++, s++) {
if (*t == '\0') {
return SP_MISCAPITALIZED; /* exact match (sans case) */
}
}
if (*s) {
if (*t) {
if (s[1] && t[1] && ap_tolower(*s) == ap_tolower(t[1])
&& ap_tolower(*t) == ap_tolower(s[1])
&& strcasecmp(s + 2, t + 2) == 0) {
return SP_TRANSPOSITION; /* transposition */
}
if (strcasecmp(s + 1, t + 1) == 0) {
return SP_SIMPLETYPO; /* 1 char mismatch */
}
}
if (strcasecmp(s + 1, t) == 0) {
return SP_EXTRACHAR; /* extra character */
}
}
if (*t && strcasecmp(s, t + 1) == 0) {
return SP_MISSINGCHAR; /* missing character */
}
return SP_VERYDIFFERENT; /* distance too large to fix. */
}
static int sort_by_quality(const void *left, const void *rite)
{
return (int) (((misspelled_file *) left)->quality)
- (int) (((misspelled_file *) rite)->quality);
}
static int check_speling(request_rec *r)
{
spconfig *cfg;
char *good, *bad, *postgood, *url;
int filoc, dotloc, urlen, pglen;
DIR *dirp;
struct DIR_TYPE *dir_entry;
array_header *candidates = NULL;
cfg = ap_get_module_config(r->per_dir_config, &speling_module);
if (!cfg->enabled) {
return DECLINED;
}
/* We only want to worry about GETs */
if (r->method_number != M_GET) {
return DECLINED;
}
/* We've already got a file of some kind or another */
if (r->proxyreq || (r->finfo.st_mode != 0)) {
return DECLINED;
}
/* This is a sub request - don't mess with it */
if (r->main) {
return DECLINED;
}
/*
* The request should end up looking like this:
* r->uri: /correct-url/mispelling/more
* r->filename: /correct-file/mispelling r->path_info: /more
*
* So we do this in steps. First break r->filename into two pieces
*/
filoc = ap_rind(r->filename, '/');
/*
* Don't do anything if the request doesn't contain a slash, or
* requests "/"
*/
if (filoc == -1 || strcmp(r->uri, "/") == 0) {
return DECLINED;
}
/* good = /correct-file */
good = ap_pstrndup(r->pool, r->filename, filoc);
/* bad = mispelling */
bad = ap_pstrdup(r->pool, r->filename + filoc + 1);
/* postgood = mispelling/more */
postgood = ap_pstrcat(r->pool, bad, r->path_info, NULL);
urlen = strlen(r->uri);
pglen = strlen(postgood);
/* Check to see if the URL pieces add up */
if (strcmp(postgood, r->uri + (urlen - pglen))) {
return DECLINED;
}
/* url = /correct-url */
url = ap_pstrndup(r->pool, r->uri, (urlen - pglen));
/* Now open the directory and do ourselves a check... */
dirp = ap_popendir(r->pool, good);
if (dirp == NULL) { /* Oops, not a directory... */
return DECLINED;
}
candidates = ap_make_array(r->pool, 2, sizeof(misspelled_file));
dotloc = ap_ind(bad, '.');
if (dotloc == -1) {
dotloc = strlen(bad);
}
while ((dir_entry = readdir(dirp)) != NULL) {
sp_reason q;
/*
* If we end up with a "fixed" URL which is identical to the
* requested one, we must have found a broken symlink or some such.
* Do _not_ try to redirect this, it causes a loop!
*/
if (strcmp(bad, dir_entry->d_name) == 0) {
ap_pclosedir(r->pool, dirp);
return OK;
}
/*
* miscapitalization errors are checked first (like, e.g., lower case
* file, upper case request)
*/
else if (strcasecmp(bad, dir_entry->d_name) == 0) {
misspelled_file *sp_new;
sp_new = (misspelled_file *) ap_push_array(candidates);
sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name);
sp_new->quality = SP_MISCAPITALIZED;
}
/*
* simple typing errors are checked next (like, e.g.,
* missing/extra/transposed char)
*/
else if ((q = spdist(bad, dir_entry->d_name)) != SP_VERYDIFFERENT) {
misspelled_file *sp_new;
sp_new = (misspelled_file *) ap_push_array(candidates);
sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name);
sp_new->quality = q;
}
/*
* The spdist() should have found the majority of the misspelled
* requests. It is of questionable use to continue looking for
* files with the same base name, but potentially of totally wrong
* type (index.html <-> index.db).
* I would propose to not set the WANT_BASENAME_MATCH define.
* 08-Aug-1997 <Martin.Kraemer@Mch.SNI.De>
*
* However, Alexei replied giving some reasons to add it anyway:
* > Oh, by the way, I remembered why having the
* > extension-stripping-and-matching stuff is a good idea:
* >
* > If you're using MultiViews, and have a file named foobar.html,
* > which you refer to as "foobar", and someone tried to access
* > "Foobar", mod_speling won't find it, because it won't find
* > anything matching that spelling. With the extension-munging,
* > it would locate "foobar.html". Not perfect, but I ran into
* > that problem when I first wrote the module.
*/
else {
#ifdef WANT_BASENAME_MATCH
/*
* Okay... we didn't find anything. Now we take out the hard-core
* power tools. There are several cases here. Someone might have
* entered a wrong extension (.htm instead of .html or vice
* versa) or the document could be negotiated. At any rate, now
* we just compare stuff before the first dot. If it matches, we
* figure we got us a match. This can result in wrong things if
* there are files of different content types but the same prefix
* (e.g. foo.gif and foo.html) This code will pick the first one
* it finds. Better than a Not Found, though.
*/
int entloc = ap_ind(dir_entry->d_name, '.');
if (entloc == -1) {
entloc = strlen(dir_entry->d_name);
}
if ((dotloc == entloc)
&& !strncasecmp(bad, dir_entry->d_name, dotloc)) {
misspelled_file *sp_new;
sp_new = (misspelled_file *) ap_push_array(candidates);
sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name);
sp_new->quality = SP_VERYDIFFERENT;
}
#endif
}
}
ap_pclosedir(r->pool, dirp);
if (candidates->nelts != 0) {
/* Wow... we found us a mispelling. Construct a fixed url */
char *nuri;
const char *ref;
misspelled_file *variant = (misspelled_file *) candidates->elts;
int i;
ref = ap_table_get(r->headers_in, "Referer");
qsort((void *) candidates->elts, candidates->nelts,
sizeof(misspelled_file), sort_by_quality);
/*
* Conditions for immediate redirection:
* a) the first candidate was not found by stripping the suffix
* AND b) there exists only one candidate OR the best match is not
* ambiguous
* then return a redirection right away.
*/
if (variant[0].quality != SP_VERYDIFFERENT
&& (candidates->nelts == 1
|| variant[0].quality != variant[1].quality)) {
nuri = ap_pstrcat(r->pool, url, variant[0].name, r->path_info,
r->parsed_uri.query ? "?" : "",
r->parsed_uri.query ? r->parsed_uri.query : "",
NULL);
ap_table_setn(r->headers_out, "Location",
ap_construct_url(r->pool, nuri, r));
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r,
ref ? "Fixed spelling: %s to %s from %s"
: "Fixed spelling: %s to %s",
r->uri, nuri, ref);
return HTTP_MOVED_PERMANENTLY;
}
/*
* Otherwise, a "[300] Multiple Choices" list with the variants is
* returned.
*/
else {
pool *p;
table *notes;
pool *sub_pool;
array_header *t;
array_header *v;
if (r->main == NULL) {
p = r->pool;
notes = r->notes;
}
else {
p = r->main->pool;
notes = r->main->notes;
}
sub_pool = ap_make_sub_pool(p);
t = ap_make_array(sub_pool, candidates->nelts * 8 + 8,
sizeof(char *));
v = ap_make_array(sub_pool, candidates->nelts * 5,
sizeof(char *));
/* Generate the response text. */
*(const char **)ap_push_array(t) =
"The document name you requested (<code>";
*(const char **)ap_push_array(t) = r->uri;
*(const char **)ap_push_array(t) =
"</code>) could not be found on this server.\n"
"However, we found documents with names similar "
"to the one you requested.<p>"
"Available documents:\n<ul>\n";
for (i = 0; i < candidates->nelts; ++i) {
char *vuri;
const char *reason;
reason = sp_reason_str[(int) (variant[i].quality)];
/* The format isn't very neat... */
vuri = ap_pstrcat(sub_pool, url, variant[i].name, r->path_info,
(r->parsed_uri.query != NULL) ? "?" : "",
(r->parsed_uri.query != NULL)
? r->parsed_uri.query : "",
NULL);
*(const char **)ap_push_array(v) = "\"";
*(const char **)ap_push_array(v) = vuri;
*(const char **)ap_push_array(v) = "\";\"";
*(const char **)ap_push_array(v) = reason;
*(const char **)ap_push_array(v) = "\"";
*(const char **)ap_push_array(t) = "<li><a href=\"";
*(const char **)ap_push_array(t) = vuri;
*(const char **)ap_push_array(t) = "\">";
*(const char **)ap_push_array(t) = vuri;
*(const char **)ap_push_array(t) = "</a> (";
*(const char **)ap_push_array(t) = reason;
*(const char **)ap_push_array(t) = ")\n";
/*
* when we have printed the "close matches" and there are
* more "distant matches" (matched by stripping the suffix),
* then we insert an additional separator text to suggest
* that the user LOOK CLOSELY whether these are really the
* files she wanted.
*/
if (i > 0 && i < candidates->nelts - 1
&& variant[i].quality != SP_VERYDIFFERENT
&& variant[i + 1].quality == SP_VERYDIFFERENT) {
*(const char **)ap_push_array(t) =
"</ul>\nFurthermore, the following related "
"documents were found:\n<ul>\n";
}
}
*(const char **)ap_push_array(t) = "</ul>\n";
/* If we know there was a referring page, add a note: */
if (ref != NULL) {
*(const char **)ap_push_array(t) =
"Please consider informing the owner of the "
"<a href=\"";
*(const char **)ap_push_array(t) = ref;
*(const char **)ap_push_array(t) = "\">referring page</a> "
"about the broken link.\n";
}
/* Pass our table to http_protocol.c (see mod_negotiation): */
ap_table_setn(notes, "variant-list", ap_array_pstrcat(p, t, 0));
ap_table_mergen(r->subprocess_env, "VARIANTS",
ap_array_pstrcat(p, v, ','));
ap_destroy_pool(sub_pool);
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r,
ref ? "Spelling fix: %s: %d candidates from %s"
: "Spelling fix: %s: %d candidates",
r->uri, candidates->nelts, ref);
return HTTP_MULTIPLE_CHOICES;
}
}
return OK;
}
module MODULE_VAR_EXPORT speling_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_mconfig_for_directory, /* create per-dir config */
NULL, /* merge per-dir config */
create_mconfig_for_server, /* server config */
NULL, /* merge server config */
speling_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
check_speling, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,349 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_userdir... implement the UserDir command. Broken away from the
* Alias stuff for a couple of good and not-so-good reasons:
*
* 1) It shows a real minimal working example of how to do something like
* this.
* 2) I know people who are actually interested in changing this *particular*
* aspect of server functionality without changing the rest of it. That's
* what this whole modular arrangement is supposed to be good at...
*
* Modified by Alexei Kosut to support the following constructs
* (server running at www.foo.com, request for /~bar/one/two.html)
*
* UserDir public_html -> ~bar/public_html/one/two.html
* UserDir /usr/web -> /usr/web/bar/one/two.html
* UserDir /home/ * /www -> /home/bar/www/one/two.html
* NOTE: theses ^ ^ space only added allow it to work in a comment, ignore
* UserDir http://x/users -> (302) http://x/users/bar/one/two.html
* UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html
* NOTE: here also ^ ^
*
* In addition, you can use multiple entries, to specify alternate
* user directories (a la Directory Index). For example:
*
* UserDir public_html /usr/web http://www.xyz.com/users
*
* Modified by Ken Coar to provide for the following:
*
* UserDir disable[d] username ...
* UserDir enable[d] username ...
*
* If "disabled" has no other arguments, *all* ~<username> references are
* disabled, except those explicitly turned on with the "enabled" keyword.
*/
#include "httpd.h"
#include "http_config.h"
module userdir_module;
typedef struct userdir_config {
int globally_disabled;
char *userdir;
table *enabled_users;
table *disabled_users;
} userdir_config;
/*
* Server config for this module: global disablement flag, a list of usernames
* ineligible for UserDir access, a list of those immune to global (but not
* explicit) disablement, and the replacement string for all others.
*/
static void *create_userdir_config(pool *p, server_rec *s)
{
userdir_config
* newcfg = (userdir_config *) ap_pcalloc(p, sizeof(userdir_config));
newcfg->globally_disabled = 0;
newcfg->userdir = DEFAULT_USER_DIR;
newcfg->enabled_users = ap_make_table(p, 4);
newcfg->disabled_users = ap_make_table(p, 4);
return (void *) newcfg;
}
#define O_DEFAULT 0
#define O_ENABLE 1
#define O_DISABLE 2
static const char *set_user_dir(cmd_parms *cmd, void *dummy, char *arg)
{
userdir_config
* s_cfg = (userdir_config *) ap_get_module_config
(
cmd->server->module_config,
&userdir_module
);
char *username;
const char
*usernames = arg;
char *kw = ap_getword_conf(cmd->pool, &usernames);
table *usertable;
/*
* Let's do the comparisons once.
*/
if ((!strcasecmp(kw, "disable")) || (!strcasecmp(kw, "disabled"))) {
/*
* If there are no usernames specified, this is a global disable - we
* need do no more at this point than record the fact.
*/
if (strlen(usernames) == 0) {
s_cfg->globally_disabled = 1;
return NULL;
}
usertable = s_cfg->disabled_users;
}
else if ((!strcasecmp(kw, "enable")) || (!strcasecmp(kw, "enabled"))) {
/*
* The "disable" keyword can stand alone or take a list of names, but
* the "enable" keyword requires the list. Whinge if it doesn't have
* it.
*/
if (strlen(usernames) == 0) {
return "UserDir \"enable\" keyword requires a list of usernames";
}
usertable = s_cfg->enabled_users;
}
else {
/*
* If the first (only?) value isn't one of our keywords, just copy
* the string to the userdir string.
*/
s_cfg->userdir = ap_pstrdup(cmd->pool, arg);
return NULL;
}
/*
* Now we just take each word in turn from the command line and add it to
* the appropriate table.
*/
while (*usernames) {
username = ap_getword_conf(cmd->pool, &usernames);
ap_table_setn(usertable, username, kw);
}
return NULL;
}
static const command_rec userdir_cmds[] = {
{"UserDir", set_user_dir, NULL, RSRC_CONF, RAW_ARGS,
"the public subdirectory in users' home directories, or 'disabled', or 'disabled username username...', or 'enabled username username...'"},
{NULL}
};
static int translate_userdir(request_rec *r)
{
void *server_conf = r->server->module_config;
const userdir_config *s_cfg =
(userdir_config *) ap_get_module_config(server_conf, &userdir_module);
char *name = r->uri;
const char *userdirs = s_cfg->userdir;
const char *w, *dname;
char *redirect;
char *x = NULL;
struct stat statbuf;
/*
* If the URI doesn't match our basic pattern, we've nothing to do with
* it.
*/
if (
(s_cfg->userdir == NULL) ||
(name[0] != '/') ||
(name[1] != '~')
) {
return DECLINED;
}
dname = name + 2;
w = ap_getword(r->pool, &dname, '/');
/*
* The 'dname' funny business involves backing it up to capture the '/'
* delimiting the "/~user" part from the rest of the URL, in case there
* was one (the case where there wasn't being just "GET /~user HTTP/1.0",
* for which we don't want to tack on a '/' onto the filename).
*/
if (dname[-1] == '/') {
--dname;
}
/*
* If there's no username, it's not for us. Ignore . and .. as well.
*/
if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0')))) {
return DECLINED;
}
/*
* Nor if there's an username but it's in the disabled list.
*/
if (ap_table_get(s_cfg->disabled_users, w) != NULL) {
return DECLINED;
}
/*
* If there's a global interdiction on UserDirs, check to see if this
* name is one of the Blessed.
*/
if (
s_cfg->globally_disabled &&
(ap_table_get(s_cfg->enabled_users, w) == NULL)
) {
return DECLINED;
}
/*
* Special cases all checked, onward to normal substitution processing.
*/
while (*userdirs) {
const char *userdir = ap_getword_conf(r->pool, &userdirs);
char *filename = NULL;
if (strchr(userdir, '*'))
x = ap_getword(r->pool, &userdir, '*');
if (userdir[0] == '\0' || ap_os_is_path_absolute(userdir)) {
if (x) {
#ifdef HAVE_DRIVE_LETTERS
/*
* Crummy hack. Need to figure out whether we have been
* redirected to a URL or to a file on some drive. Since I
* know of no protocols that are a single letter, if the : is
* the second character, I will assume a file was specified
*/
if (strchr(x + 2, ':'))
#else
if (strchr(x, ':'))
#endif /* WIN32 */
{
redirect = ap_pstrcat(r->pool, x, w, userdir, dname, NULL);
ap_table_setn(r->headers_out, "Location", redirect);
return REDIRECT;
}
else
filename = ap_pstrcat(r->pool, x, w, userdir, NULL);
}
else
filename = ap_pstrcat(r->pool, userdir, "/", w, NULL);
}
else if (strchr(userdir, ':')) {
redirect = ap_pstrcat(r->pool, userdir, "/", w, dname, NULL);
ap_table_setn(r->headers_out, "Location", redirect);
return REDIRECT;
}
else {
#ifdef WIN32
/* Need to figure out home dirs on NT */
return DECLINED;
#else /* WIN32 */
struct passwd *pw;
if ((pw = getpwnam(w))) {
#ifdef OS2
/* Need to manually add user name for OS/2 */
filename = ap_pstrcat(r->pool, pw->pw_dir, w, "/", userdir, NULL);
#else
filename = ap_pstrcat(r->pool, pw->pw_dir, "/", userdir, NULL);
#endif
}
#endif /* WIN32 */
}
/*
* Now see if it exists, or we're at the last entry. If we are at the
* last entry, then use the filename generated (if there is one)
* anyway, in the hope that some handler might handle it. This can be
* used, for example, to run a CGI script for the user.
*/
if (filename && (!*userdirs || stat(filename, &statbuf) != -1)) {
r->filename = ap_pstrcat(r->pool, filename, dname, NULL);
/* when statbuf contains info on r->filename we can save a syscall
* by copying it to r->finfo
*/
if (*userdirs && dname[0] == 0)
r->finfo = statbuf;
return OK;
}
}
return DECLINED;
}
module userdir_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_userdir_config, /* server config */
NULL, /* merge server config */
userdir_cmds, /* command table */
NULL, /* handlers */
translate_userdir, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,482 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_vhost_alias.c: support for dynamically configured mass virtual hosting
*
* Copyright (c) 1998-1999 Demon Internet Ltd.
*
* This software was submitted by Demon Internet to the Apache Group
* in May 1999. Future revisions and derivatives of this source code
* must acknowledge Demon Internet as the original contributor of
* this module. All other licensing and usage conditions are those
* of the Apache Group.
*
* Originally written by Tony Finch <fanf@demon.net> <dot@dotat.at>.
*
* Implementation ideas were taken from mod_alias.c. The overall
* concept is derived from the OVERRIDE_DOC_ROOT/OVERRIDE_CGIDIR
* patch to Apache 1.3b3 and a similar feature in Demon's thttpd,
* both written by James Grinter <jrg@blodwen.demon.co.uk>.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
module MODULE_VAR_EXPORT vhost_alias_module;
/*
* basic configuration things
* we abbreviate "mod_vhost_alias" to "mva" for shorter names
*/
typedef enum {
VHOST_ALIAS_UNSET, VHOST_ALIAS_NONE, VHOST_ALIAS_NAME, VHOST_ALIAS_IP
} mva_mode_e;
/*
* Per-server module config record.
*/
typedef struct mva_sconf_t {
char *doc_root;
char *cgi_root;
mva_mode_e doc_root_mode;
mva_mode_e cgi_root_mode;
} mva_sconf_t;
static void *mva_create_server_config(pool *p, server_rec *s)
{
mva_sconf_t *conf;
conf = (mva_sconf_t *) ap_pcalloc(p, sizeof(mva_sconf_t));
conf->doc_root = NULL;
conf->cgi_root = NULL;
conf->doc_root_mode = VHOST_ALIAS_UNSET;
conf->cgi_root_mode = VHOST_ALIAS_UNSET;
return conf;
}
static void *mva_merge_server_config(pool *p, void *parentv, void *childv)
{
mva_sconf_t *parent = (mva_sconf_t *) parentv;
mva_sconf_t *child = (mva_sconf_t *) childv;
mva_sconf_t *conf;
conf = (mva_sconf_t *) ap_pcalloc(p, sizeof(*conf));
if (child->doc_root_mode == VHOST_ALIAS_UNSET) {
conf->doc_root_mode = parent->doc_root_mode;
conf->doc_root = parent->doc_root;
}
else {
conf->doc_root_mode = child->doc_root_mode;
conf->doc_root = child->doc_root;
}
if (child->cgi_root_mode == VHOST_ALIAS_UNSET) {
conf->cgi_root_mode = parent->cgi_root_mode;
conf->cgi_root = parent->cgi_root;
}
else {
conf->cgi_root_mode = child->cgi_root_mode;
conf->cgi_root = child->cgi_root;
}
return conf;
}
/*
* These are just here to tell us what vhost_alias_set should do.
* We don't put anything into them; we just use the cell addresses.
*/
static int vhost_alias_set_doc_root_ip,
vhost_alias_set_cgi_root_ip,
vhost_alias_set_doc_root_name,
vhost_alias_set_cgi_root_name;
static const char *vhost_alias_set(cmd_parms *cmd, void *dummy, char *map)
{
mva_sconf_t *conf;
mva_mode_e mode, *pmode;
char **pmap;
char *p;
conf = (mva_sconf_t *) ap_get_module_config(cmd->server->module_config,
&vhost_alias_module);
/* there ought to be a better way of doing this */
if (&vhost_alias_set_doc_root_ip == cmd->info) {
mode = VHOST_ALIAS_IP;
pmap = &conf->doc_root;
pmode = &conf->doc_root_mode;
}
else if (&vhost_alias_set_cgi_root_ip == cmd->info) {
mode = VHOST_ALIAS_IP;
pmap = &conf->cgi_root;
pmode = &conf->cgi_root_mode;
}
else if (&vhost_alias_set_doc_root_name == cmd->info) {
mode = VHOST_ALIAS_NAME;
pmap = &conf->doc_root;
pmode = &conf->doc_root_mode;
}
else if (&vhost_alias_set_cgi_root_name == cmd->info) {
mode = VHOST_ALIAS_NAME;
pmap = &conf->cgi_root;
pmode = &conf->cgi_root_mode;
}
else {
return "INTERNAL ERROR: unknown command info";
}
if (*map != '/') {
if (strcasecmp(map, "none")) {
return "format string must start with '/' or be 'none'";
}
*pmap = NULL;
*pmode = VHOST_ALIAS_NONE;
return NULL;
}
/* sanity check */
p = map;
while (*p != '\0') {
if (*p++ != '%') {
continue;
}
/* we just found a '%' */
if (*p == 'p' || *p == '%') {
++p;
continue;
}
/* optional dash */
if (*p == '-') {
++p;
}
/* digit N */
if (ap_isdigit(*p)) {
++p;
}
else {
return "syntax error in format string";
}
/* optional plus */
if (*p == '+') {
++p;
}
/* do we end here? */
if (*p != '.') {
continue;
}
++p;
/* optional dash */
if (*p == '-') {
++p;
}
/* digit M */
if (ap_isdigit(*p)) {
++p;
}
else {
return "syntax error in format string";
}
/* optional plus */
if (*p == '+') {
++p;
}
}
*pmap = map;
*pmode = mode;
return NULL;
}
static const command_rec mva_commands[] =
{
{"VirtualScriptAlias", vhost_alias_set, &vhost_alias_set_cgi_root_name,
RSRC_CONF, TAKE1, "how to create a ScriptAlias based on the host"},
{"VirtualDocumentRoot", vhost_alias_set, &vhost_alias_set_doc_root_name,
RSRC_CONF, TAKE1, "how to create the DocumentRoot based on the host"},
{"VirtualScriptAliasIP", vhost_alias_set, &vhost_alias_set_cgi_root_ip,
RSRC_CONF, TAKE1, "how to create a ScriptAlias based on the host"},
{"VirtualDocumentRootIP", vhost_alias_set, &vhost_alias_set_doc_root_ip,
RSRC_CONF, TAKE1, "how to create the DocumentRoot based on the host"},
{ NULL }
};
/*
* This really wants to be a nested function
* but C is too feeble to support them.
*/
static ap_inline void vhost_alias_checkspace(request_rec *r, char *buf,
char **pdest, int size)
{
/* XXX: what if size > HUGE_STRING_LEN? */
if (*pdest + size > buf + HUGE_STRING_LEN) {
**pdest = '\0';
if (r->filename) {
r->filename = ap_pstrcat(r->pool, r->filename, buf, NULL);
}
else {
r->filename = ap_pstrdup(r->pool, buf);
}
*pdest = buf;
}
}
static void vhost_alias_interpolate(request_rec *r, const char *name,
const char *map, const char *uri)
{
/* 0..9 9..0 */
enum { MAXDOTS = 19 };
const char *dots[MAXDOTS+1];
int ndots;
char buf[HUGE_STRING_LEN];
char *dest, last;
int N, M, Np, Mp, Nd, Md;
const char *start, *end;
const char *p;
ndots = 0;
dots[ndots++] = name-1; /* slightly naughty */
for (p = name; *p; ++p){
if (*p == '.' && ndots < MAXDOTS) {
dots[ndots++] = p;
}
}
dots[ndots] = p;
r->filename = NULL;
dest = buf;
last = '\0';
while (*map) {
if (*map != '%') {
/* normal characters */
vhost_alias_checkspace(r, buf, &dest, 1);
last = *dest++ = *map++;
continue;
}
/* we are in a format specifier */
++map;
/* can't be a slash */
last = '\0';
/* %% -> % */
if (*map == '%') {
++map;
vhost_alias_checkspace(r, buf, &dest, 1);
*dest++ = '%';
continue;
}
/* port number */
if (*map == 'p') {
++map;
/* no. of decimal digits in a short plus one */
vhost_alias_checkspace(r, buf, &dest, 7);
dest += ap_snprintf(dest, 7, "%d", ap_get_server_port(r));
continue;
}
/* deal with %-N+.-M+ -- syntax is already checked */
N = M = 0; /* value */
Np = Mp = 0; /* is there a plus? */
Nd = Md = 0; /* is there a dash? */
if (*map == '-') ++map, Nd = 1;
N = *map++ - '0';
if (*map == '+') ++map, Np = 1;
if (*map == '.') {
++map;
if (*map == '-') {
++map, Md = 1;
}
M = *map++ - '0';
if (*map == '+') {
++map, Mp = 1;
}
}
/* note that N and M are one-based indices, not zero-based */
start = dots[0]+1; /* ptr to the first character */
end = dots[ndots]; /* ptr to the character after the last one */
if (N != 0) {
if (N > ndots) {
start = "_";
end = start+1;
}
else if (!Nd) {
start = dots[N-1]+1;
if (!Np) {
end = dots[N];
}
}
else {
if (!Np) {
start = dots[ndots-N]+1;
}
end = dots[ndots-N+1];
}
}
if (M != 0) {
if (M > end - start) {
start = "_";
end = start+1;
}
else if (!Md) {
start = start+M-1;
if (!Mp) {
end = start+1;
}
}
else {
if (!Mp) {
start = end-M;
}
end = end-M+1;
}
}
vhost_alias_checkspace(r, buf, &dest, end - start);
for (p = start; p < end; ++p) {
*dest++ = ap_tolower(*p);
}
}
*dest = '\0';
/* no double slashes */
if (last == '/') {
++uri;
}
if (r->filename) {
r->filename = ap_pstrcat(r->pool, r->filename, buf, uri, NULL);
}
else {
r->filename = ap_pstrcat(r->pool, buf, uri, NULL);
}
}
static int mva_translate(request_rec *r)
{
mva_sconf_t *conf;
const char *name, *map, *uri;
mva_mode_e mode;
int cgi;
conf = (mva_sconf_t *) ap_get_module_config(r->server->module_config,
&vhost_alias_module);
if (!strncmp(r->uri, "/cgi-bin/", 9)) {
mode = conf->cgi_root_mode;
map = conf->cgi_root;
uri = r->uri + 8;
/*
* can't force cgi immediately because we might not handle this
* call if the mode is wrong
*/
cgi = 1;
}
else if (r->uri[0] == '/') {
mode = conf->doc_root_mode;
map = conf->doc_root;
uri = r->uri;
cgi = 0;
}
else {
return DECLINED;
}
if (mode == VHOST_ALIAS_NAME) {
name = ap_get_server_name(r);
}
else if (mode == VHOST_ALIAS_IP) {
name = r->connection->local_ip;
}
else {
return DECLINED;
}
vhost_alias_interpolate(r, name, map, uri);
if (cgi) {
/* see is_scriptaliased() in mod_cgi */
r->handler = "cgi-script";
ap_table_setn(r->notes, "alias-forced-type", r->handler);
}
return OK;
}
module MODULE_VAR_EXPORT vhost_alias_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
mva_create_server_config, /* server config */
mva_merge_server_config, /* merge server configs */
mva_commands, /* command table */
NULL, /* handlers */
mva_translate, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,395 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_cern_meta.c
* version 0.1.0
* status beta
*
* Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 25.Jan.96
*
* *** IMPORTANT ***
* This version of mod_cern_meta.c controls Meta File behaviour on a
* per-directory basis. Previous versions of the module defined behaviour
* on a per-server basis. The upshot is that you'll need to revisit your
* configuration files in order to make use of the new module.
* ***
*
* Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP
* headers that can be output in addition to the normal range of
* headers for each file accessed. They appear rather like the Apache
* .asis files, and are able to provide a crude way of influencing
* the Expires: header, as well as providing other curiosities.
* There are many ways to manage meta information, this one was
* chosen because there is already a large number of CERN users
* who can exploit this module. It should be noted that there are probably
* more sensitive ways of managing the Expires: header specifically.
*
* The module obeys the following directives, which can appear
* in the server's .conf files and in .htaccess files.
*
* MetaFiles <on|off>
*
* turns on|off meta file processing for any directory.
* Default value is off
*
* # turn on MetaFiles in this directory
* MetaFiles on
*
* MetaDir <directory name>
*
* specifies the name of the directory in which Apache can find
* meta information files. The directory is usually a 'hidden'
* subdirectory of the directory that contains the file being
* accessed. eg:
*
* # .meta files are in the *same* directory as the
* # file being accessed
* MetaDir .
*
* the default is to look in a '.web' subdirectory. This is the
* same as for CERN 3.+ webservers and behaviour is the same as
* for the directive:
*
* MetaDir .web
*
* MetaSuffix <meta file suffix>
*
* specifies the file name suffix for the file containing the
* meta information. eg:
*
* # our meta files are suffixed with '.cern_meta'
* MetaSuffix .cern_meta
*
* the default is to look for files with the suffix '.meta'. This
* behaviour is the same as for the directive:
*
* MetaSuffix .meta
*
* When accessing the file
*
* DOCUMENT_ROOT/somedir/index.html
*
* this module will look for the file
*
* DOCUMENT_ROOT/somedir/.web/index.html.meta
*
* and will use its contents to generate additional MIME header
* information.
*
* For more information on the CERN Meta file semantics see:
*
* http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir
*
* Change-log:
* 29.Jan.96 pfopen/pfclose instead of fopen/fclose
* DECLINE when real file not found, we may be checking each
* of the index.html/index.shtml/index.htm variants and don't
* need to report missing ones as spurious errors.
* 31.Jan.96 log_error reports about a malformed .meta file, rather
* than a script error.
* 20.Jun.96 MetaFiles <on|off> default off, added, so that module
* can be configured per-directory. Prior to this the module
* was running for each request anywhere on the server, naughty..
* 29.Jun.96 All directives made per-directory.
*/
#include "httpd.h"
#include "http_config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "util_script.h"
#include "http_log.h"
#include "http_request.h"
#define DIR_CMD_PERMS OR_INDEXES
#define DEFAULT_METADIR ".web"
#define DEFAULT_METASUFFIX ".meta"
#define DEFAULT_METAFILES 0
module MODULE_VAR_EXPORT cern_meta_module;
typedef struct {
char *metadir;
char *metasuffix;
char *metafiles;
} cern_meta_dir_config;
static void *create_cern_meta_dir_config(pool *p, char *dummy)
{
cern_meta_dir_config *new =
(cern_meta_dir_config *) ap_palloc(p, sizeof(cern_meta_dir_config));
new->metadir = NULL;
new->metasuffix = NULL;
new->metafiles = DEFAULT_METAFILES;
return new;
}
static void *merge_cern_meta_dir_configs(pool *p, void *basev, void *addv)
{
cern_meta_dir_config *base = (cern_meta_dir_config *) basev;
cern_meta_dir_config *add = (cern_meta_dir_config *) addv;
cern_meta_dir_config *new =
(cern_meta_dir_config *) ap_palloc(p, sizeof(cern_meta_dir_config));
new->metadir = add->metadir ? add->metadir : base->metadir;
new->metasuffix = add->metasuffix ? add->metasuffix : base->metasuffix;
new->metafiles = add->metafiles;
return new;
}
static const char *set_metadir(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg)
{
dconf->metadir = arg;
return NULL;
}
static const char *set_metasuffix(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg)
{
dconf->metasuffix = arg;
return NULL;
}
static const char *set_metafiles(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg)
{
dconf->metafiles = arg;
return NULL;
}
static const command_rec cern_meta_cmds[] =
{
{"MetaFiles", set_metafiles, NULL, DIR_CMD_PERMS, FLAG,
"Limited to 'on' or 'off'"},
{"MetaDir", set_metadir, NULL, DIR_CMD_PERMS, TAKE1,
"the name of the directory containing meta files"},
{"MetaSuffix", set_metasuffix, NULL, DIR_CMD_PERMS, TAKE1,
"the filename suffix for meta files"},
{NULL}
};
/* XXX: this is very similar to ap_scan_script_header_err_core...
* are the differences deliberate, or just a result of bit rot?
*/
static int scan_meta_file(request_rec *r, FILE *f)
{
char w[MAX_STRING_LEN];
char *l;
int p;
table *tmp_headers;
tmp_headers = ap_make_table(r->pool, 5);
while (fgets(w, MAX_STRING_LEN - 1, f) != NULL) {
/* Delete terminal (CR?)LF */
p = strlen(w);
if (p > 0 && w[p - 1] == '\n') {
if (p > 1 && w[p - 2] == '\015')
w[p - 2] = '\0';
else
w[p - 1] = '\0';
}
if (w[0] == '\0') {
return OK;
}
/* if we see a bogus header don't ignore it. Shout and scream */
if (!(l = strchr(w, ':'))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"malformed header in meta file: %s", r->filename);
return SERVER_ERROR;
}
*l++ = '\0';
while (*l && ap_isspace(*l))
++l;
if (!strcasecmp(w, "Content-type")) {
char *tmp;
/* Nuke trailing whitespace */
char *endp = l + strlen(l) - 1;
while (endp > l && ap_isspace(*endp))
*endp-- = '\0';
tmp = ap_pstrdup(r->pool, l);
ap_content_type_tolower(tmp);
r->content_type = tmp;
}
else if (!strcasecmp(w, "Status")) {
sscanf(l, "%d", &r->status);
r->status_line = ap_pstrdup(r->pool, l);
}
else {
ap_table_set(tmp_headers, w, l);
}
}
ap_overlap_tables(r->headers_out, tmp_headers, AP_OVERLAP_TABLES_SET);
return OK;
}
static int add_cern_meta_data(request_rec *r)
{
char *metafilename;
char *last_slash;
char *real_file;
char *scrap_book;
FILE *f;
cern_meta_dir_config *dconf;
int rv;
request_rec *rr;
dconf = ap_get_module_config(r->per_dir_config, &cern_meta_module);
if (!dconf->metafiles) {
return DECLINED;
};
/* if ./.web/$1.meta exists then output 'asis' */
if (r->finfo.st_mode == 0) {
return DECLINED;
};
/* is this a directory? */
if (S_ISDIR(r->finfo.st_mode) || r->uri[strlen(r->uri) - 1] == '/') {
return DECLINED;
};
/* what directory is this file in? */
scrap_book = ap_pstrdup(r->pool, r->filename);
/* skip leading slash, recovered in later processing */
scrap_book++;
last_slash = strrchr(scrap_book, '/');
if (last_slash != NULL) {
/* skip over last slash */
real_file = last_slash;
real_file++;
*last_slash = '\0';
}
else {
/* no last slash, buh?! */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"internal error in mod_cern_meta: %s", r->filename);
/* should really barf, but hey, let's be friends... */
return DECLINED;
};
metafilename = ap_pstrcat(r->pool, "/", scrap_book, "/",
dconf->metadir ? dconf->metadir : DEFAULT_METADIR,
"/", real_file,
dconf->metasuffix ? dconf->metasuffix : DEFAULT_METASUFFIX,
NULL);
/* XXX: it sucks to require this subrequest to complete, because this
* means people must leave their meta files accessible to the world.
* A better solution might be a "safe open" feature of pfopen to avoid
* pipes, symlinks, and crap like that.
*/
rr = ap_sub_req_lookup_file(metafilename, r);
if (rr->status != HTTP_OK) {
ap_destroy_sub_req(rr);
return DECLINED;
}
ap_destroy_sub_req(rr);
f = ap_pfopen(r->pool, metafilename, "r");
if (f == NULL) {
if (errno == ENOENT) {
return DECLINED;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"meta file permissions deny server access: %s", metafilename);
return FORBIDDEN;
};
/* read the headers in */
rv = scan_meta_file(r, f);
ap_pfclose(r->pool, f);
return rv;
}
module MODULE_VAR_EXPORT cern_meta_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_cern_meta_dir_config, /* dir config creater */
merge_cern_meta_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
cern_meta_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
add_cern_meta_data, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

270
modules/metadata/mod_env.c Normal file
View File

@ -0,0 +1,270 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_env.c
* version 0.0.5
* status beta
* Pass environment variables to CGI/SSI scripts.
*
* Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 06.Dec.95
*
* Change log:
* 08.Dec.95 Now allows PassEnv directive to appear more than once in
* conf files.
* 10.Dec.95 optimisation. getenv() only called at startup and used
* to build a fast-to-access table. table used to build
* per-server environment for each request.
* robustness. better able to handle errors in configuration
* files:
* 1) PassEnv directive present, but no environment variable listed
* 2) PassEnv FOO present, but $FOO not present in environment
* 3) no PassEnv directive present
* 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv:
* SetEnv Var sets Var to the empty string
* SetEnv Var Val sets Var to the value Val
* Values containing whitespace should be quoted, eg:
* SetEnv Var "this is some text"
* Environment variables take their value from the last instance
* of PassEnv / SetEnv to be reached in the configuration file.
* For example, the sequence:
* PassEnv FOO
* SetEnv FOO override
* Causes FOO to take the value 'override'.
* 23.Feb.96 Added UnsetEnv directive to allow environment variables
* to be removed.
* Virtual hosts now 'inherit' parent server environment which
* they're able to overwrite with their own directives or
* selectively ignore with UnsetEnv.
* *** IMPORTANT - the way that virtual hosts inherit their ***
* *** environment variables from the default server's ***
* *** configuration has changed. You should test your ***
* *** configuration carefully before accepting this ***
* *** version of the module in a live webserver which used ***
* *** older versions of the module. ***
*/
#include "httpd.h"
#include "http_config.h"
typedef struct {
table *vars;
char *unsetenv;
int vars_present;
} env_dir_config_rec;
module MODULE_VAR_EXPORT env_module;
static void *create_env_dir_config(pool *p, char *dummy)
{
env_dir_config_rec *new =
(env_dir_config_rec *) ap_palloc(p, sizeof(env_dir_config_rec));
new->vars = ap_make_table(p, 50);
new->unsetenv = "";
new->vars_present = 0;
return (void *) new;
}
static void *merge_env_dir_configs(pool *p, void *basev, void *addv)
{
env_dir_config_rec *base = (env_dir_config_rec *) basev;
env_dir_config_rec *add = (env_dir_config_rec *) addv;
env_dir_config_rec *new =
(env_dir_config_rec *) ap_palloc(p, sizeof(env_dir_config_rec));
table *new_table;
table_entry *elts;
array_header *arr;
int i;
const char *uenv, *unset;
/*
* new_table = copy_table( p, base->vars );
* foreach $element ( @add->vars ) {
* table_set( new_table, $element.key, $element.val );
* };
* foreach $unsetenv ( @UNSETENV ) {
* table_unset( new_table, $unsetenv );
* }
*/
new_table = ap_copy_table(p, base->vars);
arr = ap_table_elts(add->vars);
elts = (table_entry *)arr->elts;
for (i = 0; i < arr->nelts; ++i) {
ap_table_setn(new_table, elts[i].key, elts[i].val);
}
unset = add->unsetenv;
uenv = ap_getword_conf(p, &unset);
while (uenv[0] != '\0') {
ap_table_unset(new_table, uenv);
uenv = ap_getword_conf(p, &unset);
}
new->vars = new_table;
new->vars_present = base->vars_present || add->vars_present;
return new;
}
static const char *add_env_module_vars_passed(cmd_parms *cmd,
env_dir_config_rec *sconf,
const char *arg)
{
table *vars = sconf->vars;
char *env_var;
char *name_ptr;
while (*arg) {
name_ptr = ap_getword_conf(cmd->pool, &arg);
env_var = getenv(name_ptr);
if (env_var != NULL) {
sconf->vars_present = 1;
ap_table_setn(vars, name_ptr, ap_pstrdup(cmd->pool, env_var));
}
}
return NULL;
}
static const char *add_env_module_vars_set(cmd_parms *cmd,
env_dir_config_rec *sconf,
const char *arg)
{
table *vars = sconf->vars;
char *name, *value;
name = ap_getword_conf(cmd->pool, &arg);
value = ap_getword_conf(cmd->pool, &arg);
/* name is mandatory, value is optional. no value means
* set the variable to an empty string
*/
if ((*name == '\0') || (*arg != '\0')) {
return "SetEnv takes one or two arguments. An environment variable name and an optional value to pass to CGI.";
}
sconf->vars_present = 1;
ap_table_setn(vars, name, value);
return NULL;
}
static const char *add_env_module_vars_unset(cmd_parms *cmd,
env_dir_config_rec *sconf,
char *arg)
{
sconf->unsetenv = sconf->unsetenv ?
ap_pstrcat(cmd->pool, sconf->unsetenv, " ", arg, NULL) :
arg;
return NULL;
}
static const command_rec env_module_cmds[] =
{
{"PassEnv", add_env_module_vars_passed, NULL,
OR_FILEINFO, RAW_ARGS, "a list of environment variables to pass to CGI."},
{"SetEnv", add_env_module_vars_set, NULL,
OR_FILEINFO, RAW_ARGS, "an environment variable name and a value to pass to CGI."},
{"UnsetEnv", add_env_module_vars_unset, NULL,
OR_FILEINFO, RAW_ARGS, "a list of variables to remove from the CGI environment."},
{NULL},
};
static int fixup_env_module(request_rec *r)
{
table *e = r->subprocess_env;
env_dir_config_rec *sconf = ap_get_module_config(r->per_dir_config,
&env_module);
table *vars = sconf->vars;
if (!sconf->vars_present)
return DECLINED;
r->subprocess_env = ap_overlay_tables(r->pool, e, vars);
return OK;
}
module MODULE_VAR_EXPORT env_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_env_dir_config, /* dir config creater */
merge_env_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
env_module_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
fixup_env_module, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,510 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_expires.c
* version 0.0.11
* status beta
*
* Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 26.Jan.96
*
* This module allows you to control the form of the Expires: header
* that Apache issues for each access. Directives can appear in
* configuration files or in .htaccess files so expiry semantics can
* be defined on a per-directory basis.
*
* DIRECTIVE SYNTAX
*
* Valid directives are:
*
* ExpiresActive on | off
* ExpiresDefault <code><seconds>
* ExpiresByType type/encoding <code><seconds>
*
* Valid values for <code> are:
*
* 'M' expires header shows file modification date + <seconds>
* 'A' expires header shows access time + <seconds>
*
* [I'm not sure which of these is best under different
* circumstances, I guess it's for other people to explore.
* The effects may be indistinguishable for a number of cases]
*
* <seconds> should be an integer value [acceptable to atoi()]
*
* There is NO space between the <code> and <seconds>.
*
* For example, a directory which contains information which changes
* frequently might contain:
*
* # reports generated by cron every hour. don't let caches
* # hold onto stale information
* ExpiresDefault M3600
*
* Another example, our html pages can change all the time, the gifs
* tend not to change often:
*
* # pages are hot (1 week), images are cold (1 month)
* ExpiresByType text/html A604800
* ExpiresByType image/gif A2592000
*
* Expires can be turned on for all URLs on the server by placing the
* following directive in a conf file:
*
* ExpiresActive on
*
* ExpiresActive can also appear in .htaccess files, enabling the
* behaviour to be turned on or off for each chosen directory.
*
* # turn off Expires behaviour in this directory
* # and subdirectories
* ExpiresActive off
*
* Directives defined for a directory are valid in subdirectories
* unless explicitly overridden by new directives in the subdirectory
* .htaccess files.
*
* ALTERNATIVE DIRECTIVE SYNTAX
*
* Directives can also be defined in a more readable syntax of the form:
*
* ExpiresDefault "<base> [plus] {<num> <type>}*"
* ExpiresByType type/encoding "<base> [plus] {<num> <type>}*"
*
* where <base> is one of:
* access
* now equivalent to 'access'
* modification
*
* where the 'plus' keyword is optional
*
* where <num> should be an integer value [acceptable to atoi()]
*
* where <type> is one of:
* years
* months
* weeks
* days
* hours
* minutes
* seconds
*
* For example, any of the following directives can be used to make
* documents expire 1 month after being accessed, by default:
*
* ExpiresDefault "access plus 1 month"
* ExpiresDefault "access plus 4 weeks"
* ExpiresDefault "access plus 30 days"
*
* The expiry time can be fine-tuned by adding several '<num> <type>'
* clauses:
*
* ExpiresByType text/html "access plus 1 month 15 days 2 hours"
* ExpiresByType image/gif "modification plus 5 hours 3 minutes"
*
* ---
*
* Change-log:
* 29.Jan.96 Hardened the add_* functions. Server will now bail out
* if bad directives are given in the conf files.
* 02.Feb.96 Returns DECLINED if not 'ExpiresActive on', giving other
* expires-aware modules a chance to play with the same
* directives. [Michael Rutman]
* 03.Feb.96 Call tzset() before localtime(). Trying to get the module
* to work properly in non GMT timezones.
* 12.Feb.96 Modified directive syntax to allow more readable commands:
* ExpiresDefault "now plus 10 days 20 seconds"
* ExpiresDefault "access plus 30 days"
* ExpiresDefault "modification plus 1 year 10 months 30 days"
* 13.Feb.96 Fix call to table_get() with NULL 2nd parameter [Rob Hartill]
* 19.Feb.96 Call gm_timestr_822() to get time formatted correctly, can't
* rely on presence of HTTP_TIME_FORMAT in Apache 1.1+.
* 21.Feb.96 This version (0.0.9) reverses assumptions made in 0.0.8
* about star/star handlers. Reverting to 0.0.7 behaviour.
* 08.Jun.96 allows ExpiresDefault to be used with responses that use
* the DefaultType by not DECLINING, but instead skipping
* the table_get check and then looking for an ExpiresDefault.
* [Rob Hartill]
* 04.Nov.96 'const' definitions added.
*
* TODO
* add support for Cache-Control: max-age=20 from the HTTP/1.1
* proposal (in this case, a ttl of 20 seconds) [ask roy]
* add per-file expiry and explicit expiry times - duplicates some
* of the mod_cern_meta.c functionality. eg:
* ExpiresExplicit index.html "modification plus 30 days"
*
* BUGS
* Hi, welcome to the internet.
*/
#include <ctype.h>
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
typedef struct {
int active;
char *expiresdefault;
table *expiresbytype;
} expires_dir_config;
/* from mod_dir, why is this alias used?
*/
#define DIR_CMD_PERMS OR_INDEXES
#define ACTIVE_ON 1
#define ACTIVE_OFF 0
#define ACTIVE_DONTCARE 2
module MODULE_VAR_EXPORT expires_module;
static void *create_dir_expires_config(pool *p, char *dummy)
{
expires_dir_config *new =
(expires_dir_config *) ap_pcalloc(p, sizeof(expires_dir_config));
new->active = ACTIVE_DONTCARE;
new->expiresdefault = "";
new->expiresbytype = ap_make_table(p, 4);
return (void *) new;
}
static const char *set_expiresactive(cmd_parms *cmd, expires_dir_config * dir_config, int arg)
{
/* if we're here at all it's because someone explicitly
* set the active flag
*/
dir_config->active = ACTIVE_ON;
if (arg == 0) {
dir_config->active = ACTIVE_OFF;
};
return NULL;
}
/* check_code() parse 'code' and return NULL or an error response
* string. If we return NULL then real_code contains code converted
* to the cnnnn format.
*/
static char *check_code(pool *p, const char *code, char **real_code)
{
char *word;
char base = 'X';
int modifier = 0;
int num = 0;
int factor = 0;
/* 0.0.4 compatibility?
*/
if ((code[0] == 'A') || (code[0] == 'M')) {
*real_code = (char *)code;
return NULL;
};
/* <base> [plus] {<num> <type>}*
*/
/* <base>
*/
word = ap_getword_conf(p, &code);
if (!strncasecmp(word, "now", 1) ||
!strncasecmp(word, "access", 1)) {
base = 'A';
}
else if (!strncasecmp(word, "modification", 1)) {
base = 'M';
}
else {
return ap_pstrcat(p, "bad expires code, unrecognised <base> '",
word, "'", NULL);
};
/* [plus]
*/
word = ap_getword_conf(p, &code);
if (!strncasecmp(word, "plus", 1)) {
word = ap_getword_conf(p, &code);
};
/* {<num> <type>}*
*/
while (word[0]) {
/* <num>
*/
if (ap_isdigit(word[0])) {
num = atoi(word);
}
else {
return ap_pstrcat(p, "bad expires code, numeric value expected <num> '",
word, "'", NULL);
};
/* <type>
*/
word = ap_getword_conf(p, &code);
if (word[0]) {
/* do nothing */
}
else {
return ap_pstrcat(p, "bad expires code, missing <type>", NULL);
};
factor = 0;
if (!strncasecmp(word, "years", 1)) {
factor = 60 * 60 * 24 * 365;
}
else if (!strncasecmp(word, "months", 2)) {
factor = 60 * 60 * 24 * 30;
}
else if (!strncasecmp(word, "weeks", 1)) {
factor = 60 * 60 * 24 * 7;
}
else if (!strncasecmp(word, "days", 1)) {
factor = 60 * 60 * 24;
}
else if (!strncasecmp(word, "hours", 1)) {
factor = 60 * 60;
}
else if (!strncasecmp(word, "minutes", 2)) {
factor = 60;
}
else if (!strncasecmp(word, "seconds", 1)) {
factor = 1;
}
else {
return ap_pstrcat(p, "bad expires code, unrecognised <type>",
"'", word, "'", NULL);
};
modifier = modifier + factor * num;
/* next <num>
*/
word = ap_getword_conf(p, &code);
};
*real_code = ap_psprintf(p, "%c%d", base, modifier);
return NULL;
}
static const char *set_expiresbytype(cmd_parms *cmd, expires_dir_config * dir_config, char *mime, char *code)
{
char *response, *real_code;
if ((response = check_code(cmd->pool, code, &real_code)) == NULL) {
ap_table_setn(dir_config->expiresbytype, mime, real_code);
return NULL;
};
return ap_pstrcat(cmd->pool,
"'ExpiresByType ", mime, " ", code, "': ", response, NULL);
}
static const char *set_expiresdefault(cmd_parms *cmd, expires_dir_config * dir_config, char *code)
{
char *response, *real_code;
if ((response = check_code(cmd->pool, code, &real_code)) == NULL) {
dir_config->expiresdefault = real_code;
return NULL;
};
return ap_pstrcat(cmd->pool,
"'ExpiresDefault ", code, "': ", response, NULL);
}
static const command_rec expires_cmds[] =
{
{"ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, FLAG,
"Limited to 'on' or 'off'"},
{"ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, TAKE2,
"a MIME type followed by an expiry date code"},
{"ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, TAKE1,
"an expiry date code"},
{NULL}
};
static void *merge_expires_dir_configs(pool *p, void *basev, void *addv)
{
expires_dir_config *new = (expires_dir_config *) ap_pcalloc(p, sizeof(expires_dir_config));
expires_dir_config *base = (expires_dir_config *) basev;
expires_dir_config *add = (expires_dir_config *) addv;
if (add->active == ACTIVE_DONTCARE) {
new->active = base->active;
}
else {
new->active = add->active;
};
if (add->expiresdefault != '\0') {
new->expiresdefault = add->expiresdefault;
};
new->expiresbytype = ap_overlay_tables(p, add->expiresbytype,
base->expiresbytype);
return new;
}
static int add_expires(request_rec *r)
{
expires_dir_config *conf;
char *code;
time_t base;
time_t additional;
time_t expires;
char age[20];
if (ap_is_HTTP_ERROR(r->status)) /* Don't add Expires headers to errors */
return DECLINED;
if (r->main != NULL) /* Say no to subrequests */
return DECLINED;
conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config, &expires_module);
if (conf == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"internal error: %s", r->filename);
return SERVER_ERROR;
};
if (conf->active != ACTIVE_ON)
return DECLINED;
/* we perhaps could use the default_type(r) in its place but that
* may be 2nd guesing the desired configuration... calling table_get
* with a NULL key will SEGV us
*
* I still don't know *why* r->content_type would ever be NULL, this
* is possibly a result of fixups being called in many different
* places. Fixups is probably the wrong place to be doing all this
* work... Bah.
*
* Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault.
*/
if (r->content_type == NULL)
code = NULL;
else
code = (char *) ap_table_get(conf->expiresbytype, r->content_type);
if (code == NULL) {
/* no expires defined for that type, is there a default? */
code = conf->expiresdefault;
if (code[0] == '\0')
return OK;
};
/* we have our code */
switch (code[0]) {
case 'M':
if (r->finfo.st_mode == 0) {
/* file doesn't exist on disk, so we can't do anything based on
* modification time. Note that this does _not_ log an error.
*/
return DECLINED;
}
base = r->finfo.st_mtime;
additional = atoi(&code[1]);
break;
case 'A':
/* there's been some discussion and it's possible that
* 'access time' will be stored in request structure
*/
base = r->request_time;
additional = atoi(&code[1]);
break;
default:
/* expecting the add_* routines to be case-hardened this
* is just a reminder that module is beta
*/
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"internal error: bad expires code: %s", r->filename);
return SERVER_ERROR;
};
expires = base + additional;
ap_snprintf(age, sizeof(age), "max-age=%d", (int) expires - (int) r->request_time);
ap_table_setn(r->headers_out, "Cache-Control", ap_pstrdup(r->pool, age));
tzset(); /* redundant? called implicitly by localtime, at least
* under FreeBSD
*/
ap_table_setn(r->headers_out, "Expires", ap_gm_timestr_822(r->pool, expires));
return OK;
}
module MODULE_VAR_EXPORT expires_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_dir_expires_config, /* dir config creater */
merge_expires_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
expires_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
add_expires, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

View File

@ -0,0 +1,265 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_headers.c: Add/append/remove HTTP response headers
* Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
*
* New directive, Header, can be used to add/replace/remove HTTP headers.
* Valid in both per-server and per-dir configurations.
*
* Syntax is:
*
* Header action header value
*
* Where action is one of:
* set - set this header, replacing any old value
* add - add this header, possible resulting in two or more
* headers with the same name
* append - append this text onto any existing header of this same
* unset - remove this header
*
* Where action is unset, the third argument (value) should not be given.
* The header name can include the colon, or not.
*
* The Header directive can only be used where allowed by the FileInfo
* override.
*
* When the request is processed, the header directives are processed in
* this order: firstly, the main server, then the virtual server handling
* this request (if any), then any <Directory> sections (working downwards
* from the root dir), then an <Location> sections (working down from
* shortest URL component), the any <File> sections. This order is
* important if any 'set' or 'unset' actions are used. For example,
* the following two directives have different effect if applied in
* the reverse order:
*
* Header append Author "John P. Doe"
* Header unset Author
*
* Examples:
*
* To set the "Author" header, use
* Header add Author "John P. Doe"
*
* To remove a header:
* Header unset Author
*
*/
#include "httpd.h"
#include "http_config.h"
typedef enum {
hdr_add = 'a', /* add header (could mean multiple hdrs) */
hdr_set = 's', /* set (replace old value) */
hdr_append = 'm', /* append (merge into any old value) */
hdr_unset = 'u' /* unset header */
} hdr_actions;
typedef struct {
hdr_actions action;
char *header;
char *value;
} header_entry;
/*
* headers_conf is our per-module configuration. This is used as both
* a per-dir and per-server config
*/
typedef struct {
array_header *headers;
} headers_conf;
module MODULE_VAR_EXPORT headers_module;
static void *create_headers_config(pool *p, server_rec *s)
{
headers_conf *a =
(headers_conf *) ap_pcalloc(p, sizeof(headers_conf));
a->headers = ap_make_array(p, 2, sizeof(header_entry));
return a;
}
static void *create_headers_dir_config(pool *p, char *d)
{
return (headers_conf *) create_headers_config(p, NULL);
}
static void *merge_headers_config(pool *p, void *basev, void *overridesv)
{
headers_conf *a =
(headers_conf *) ap_pcalloc(p, sizeof(headers_conf));
headers_conf *base = (headers_conf *) basev, *overrides = (headers_conf *) overridesv;
a->headers = ap_append_arrays(p, base->headers, overrides->headers);
return a;
}
static const char *header_cmd(cmd_parms *cmd, headers_conf * dirconf, char *action, char *hdr, char *value)
{
header_entry *new;
server_rec *s = cmd->server;
headers_conf *serverconf =
(headers_conf *) ap_get_module_config(s->module_config, &headers_module);
char *colon;
if (cmd->path) {
new = (header_entry *) ap_push_array(dirconf->headers);
}
else {
new = (header_entry *) ap_push_array(serverconf->headers);
}
if (!strcasecmp(action, "set"))
new->action = hdr_set;
else if (!strcasecmp(action, "add"))
new->action = hdr_add;
else if (!strcasecmp(action, "append"))
new->action = hdr_append;
else if (!strcasecmp(action, "unset"))
new->action = hdr_unset;
else
return "first argument must be add, set, append or unset.";
if (new->action == hdr_unset) {
if (value)
return "Header unset takes two arguments";
}
else if (!value)
return "Header requires three arguments";
if ((colon = strchr(hdr, ':')))
*colon = '\0';
new->header = hdr;
new->value = value;
return NULL;
}
static const command_rec headers_cmds[] =
{
{"Header", header_cmd, NULL, OR_FILEINFO, TAKE23,
"an action, header and value"},
{NULL}
};
static void do_headers_fixup(request_rec *r, array_header *headers)
{
int i;
for (i = 0; i < headers->nelts; ++i) {
header_entry *hdr = &((header_entry *) (headers->elts))[i];
switch (hdr->action) {
case hdr_add:
ap_table_addn(r->headers_out, hdr->header, hdr->value);
break;
case hdr_append:
ap_table_mergen(r->headers_out, hdr->header, hdr->value);
break;
case hdr_set:
ap_table_setn(r->headers_out, hdr->header, hdr->value);
break;
case hdr_unset:
ap_table_unset(r->headers_out, hdr->header);
break;
}
}
}
static int fixup_headers(request_rec *r)
{
void *sconf = r->server->module_config;
headers_conf *serverconf =
(headers_conf *) ap_get_module_config(sconf, &headers_module);
void *dconf = r->per_dir_config;
headers_conf *dirconf =
(headers_conf *) ap_get_module_config(dconf, &headers_module);
do_headers_fixup(r, serverconf->headers);
do_headers_fixup(r, dirconf->headers);
return DECLINED;
}
module MODULE_VAR_EXPORT headers_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_headers_dir_config, /* dir config creater */
merge_headers_config, /* dir merger --- default is to override */
create_headers_config, /* server config */
merge_headers_config, /* merge server configs */
headers_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
fixup_headers, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,423 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_setenvif.c
* Set environment variables based on matching request headers or
* attributes against regex strings
*
* Paul Sutton <paul@ukweb.com> 27 Oct 1996
* Based on mod_browser by Alexei Kosut <akosut@organic.com>
*/
/*
* Used to set environment variables based on the incoming request headers,
* or some selected other attributes of the request (e.g., the remote host
* name).
*
* Usage:
*
* SetEnvIf name regex var ...
*
* where name is either a HTTP request header name, or one of the
* special values (see below). The 'value' of the header (or the
* value of the special value from below) are compared against the
* regex argument. If this is a simple string, a simple sub-string
* match is performed. Otherwise, a request expression match is
* done. If the value matches the string or regular expression, the
* environment variables listed as var ... are set. Each var can
* be in one of three formats: var, which sets the named variable
* (the value value "1"); var=value, which sets the variable to
* the given value; or !var, which unsets the variable is it has
* been previously set.
*
* Normally the strings are compared with regard to case. To ignore
* case, use the directive SetEnvIfNoCase instead.
*
* Special values for 'name' are:
*
* remote_host Remote host name (if available)
* remote_addr Remote IP address
* remote_user Remote authenticated user (if any)
* request_method Request method (GET, POST, etc)
* request_uri Requested URI
*
* Examples:
*
* To set the enviroment variable LOCALHOST if the client is the local
* machine:
*
* SetEnvIf remote_addr 127.0.0.1 LOCALHOST
*
* To set LOCAL if the client is the local host, or within our company's
* domain (192.168.10):
*
* SetEnvIf remote_addr 192.168.10. LOCAL
* SetEnvIf remote_addr 127.0.0.1 LOCALHOST
*
* This could be written as:
*
* SetEnvIf remote_addr (127.0.0.1|192.168.10.) LOCAL
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
enum special {
SPECIAL_NOT,
SPECIAL_REMOTE_ADDR,
SPECIAL_REMOTE_HOST,
SPECIAL_REMOTE_USER,
SPECIAL_REQUEST_URI,
SPECIAL_REQUEST_METHOD,
SPECIAL_REQUEST_PROTOCOL
};
typedef struct {
char *name; /* header name */
char *regex; /* regex to match against */
regex_t *preg; /* compiled regex */
table *features; /* env vars to set (or unset) */
ENUM_BITFIELD( /* is it a "special" header ? */
enum special,
special_type,4);
unsigned icase : 1; /* ignoring case? */
} sei_entry;
typedef struct {
array_header *conditionals;
} sei_cfg_rec;
module MODULE_VAR_EXPORT setenvif_module;
static void *create_setenvif_config(pool *p, server_rec *dummy)
{
sei_cfg_rec *new = (sei_cfg_rec *) ap_palloc(p, sizeof(sei_cfg_rec));
new->conditionals = ap_make_array(p, 20, sizeof(sei_entry));
return (void *) new;
}
static void *merge_setenvif_config(pool *p, void *basev, void *overridesv)
{
sei_cfg_rec *a = ap_pcalloc(p, sizeof(sei_cfg_rec));
sei_cfg_rec *base = basev, *overrides = overridesv;
a->conditionals = ap_append_arrays(p, base->conditionals,
overrides->conditionals);
return a;
}
/* any non-NULL magic constant will do... used to indicate if REG_ICASE should
* be used
*/
#define ICASE_MAGIC ((void *)(&setenvif_module))
static const char *add_setenvif_core(cmd_parms *cmd, void *mconfig,
char *fname, const char *args)
{
char *regex;
const char *feature;
sei_cfg_rec *sconf = ap_get_module_config(cmd->server->module_config,
&setenvif_module);
sei_entry *new, *entries = (sei_entry *) sconf->conditionals->elts;
char *var;
int i;
int beenhere = 0;
unsigned icase;
/* get regex */
regex = ap_getword_conf(cmd->pool, &args);
if (!*regex) {
return ap_pstrcat(cmd->pool, "Missing regular expression for ",
cmd->cmd->name, NULL);
}
/*
* If we've already got a sei_entry with the same name we want to
* just copy the name pointer... so that later on we can compare
* two header names just by comparing the pointers.
*/
for (i = 0; i < sconf->conditionals->nelts; ++i) {
new = &entries[i];
if (!strcasecmp(new->name, fname)) {
fname = new->name;
break;
}
}
/* if the last entry has an idential headername and regex then
* merge with it
*/
i = sconf->conditionals->nelts - 1;
icase = cmd->info == ICASE_MAGIC;
if (i < 0
|| entries[i].name != fname
|| entries[i].icase != icase
|| strcmp(entries[i].regex, regex)) {
/* no match, create a new entry */
new = ap_push_array(sconf->conditionals);
new->name = fname;
new->regex = regex;
new->icase = icase;
new->preg = ap_pregcomp(cmd->pool, regex,
(REG_EXTENDED | REG_NOSUB
| (icase ? REG_ICASE : 0)));
if (new->preg == NULL) {
return ap_pstrcat(cmd->pool, cmd->cmd->name,
" regex could not be compiled.", NULL);
}
new->features = ap_make_table(cmd->pool, 2);
if (!strcasecmp(fname, "remote_addr")) {
new->special_type = SPECIAL_REMOTE_ADDR;
}
else if (!strcasecmp(fname, "remote_host")) {
new->special_type = SPECIAL_REMOTE_HOST;
}
else if (!strcasecmp(fname, "remote_user")) {
new->special_type = SPECIAL_REMOTE_USER;
}
else if (!strcasecmp(fname, "request_uri")) {
new->special_type = SPECIAL_REQUEST_URI;
}
else if (!strcasecmp(fname, "request_method")) {
new->special_type = SPECIAL_REQUEST_METHOD;
}
else if (!strcasecmp(fname, "request_protocol")) {
new->special_type = SPECIAL_REQUEST_PROTOCOL;
}
else {
new->special_type = SPECIAL_NOT;
}
}
else {
new = &entries[i];
}
for ( ; ; ) {
feature = ap_getword_conf(cmd->pool, &args);
if (!*feature) {
break;
}
beenhere++;
var = ap_getword(cmd->pool, &feature, '=');
if (*feature) {
ap_table_setn(new->features, var, feature);
}
else if (*var == '!') {
ap_table_setn(new->features, var + 1, "!");
}
else {
ap_table_setn(new->features, var, "1");
}
}
if (!beenhere) {
return ap_pstrcat(cmd->pool, "Missing envariable expression for ",
cmd->cmd->name, NULL);
}
return NULL;
}
static const char *add_setenvif(cmd_parms *cmd, void *mconfig,
const char *args)
{
char *fname;
/* get header name */
fname = ap_getword_conf(cmd->pool, &args);
if (!*fname) {
return ap_pstrcat(cmd->pool, "Missing header-field name for ",
cmd->cmd->name, NULL);
}
return add_setenvif_core(cmd, mconfig, fname, args);
}
/*
* This routine handles the BrowserMatch* directives. It simply turns around
* and feeds them, with the appropriate embellishments, to the general-purpose
* command handler.
*/
static const char *add_browser(cmd_parms *cmd, void *mconfig, const char *args)
{
return add_setenvif_core(cmd, mconfig, "User-Agent", args);
}
static const command_rec setenvif_module_cmds[] =
{
{ "SetEnvIf", add_setenvif, NULL,
RSRC_CONF, RAW_ARGS, "A header-name, regex and a list of variables." },
{ "SetEnvIfNoCase", add_setenvif, ICASE_MAGIC,
RSRC_CONF, RAW_ARGS, "a header-name, regex and a list of variables." },
{ "BrowserMatch", add_browser, NULL,
RSRC_CONF, RAW_ARGS, "A browser regex and a list of variables." },
{ "BrowserMatchNoCase", add_browser, ICASE_MAGIC,
RSRC_CONF, RAW_ARGS, "A browser regex and a list of variables." },
{ NULL },
};
static int match_headers(request_rec *r)
{
server_rec *s = r->server;
sei_cfg_rec *sconf;
sei_entry *entries;
table_entry *elts;
const char *val;
int i, j;
char *last_name;
sconf = (sei_cfg_rec *) ap_get_module_config(s->module_config,
&setenvif_module);
entries = (sei_entry *) sconf->conditionals->elts;
last_name = NULL;
val = NULL;
for (i = 0; i < sconf->conditionals->nelts; ++i) {
sei_entry *b = &entries[i];
/* Optimize the case where a bunch of directives in a row use the
* same header. Remember we don't need to strcmp the two header
* names because we made sure the pointers were equal during
* configuration.
*/
if (b->name != last_name) {
last_name = b->name;
switch (b->special_type) {
case SPECIAL_REMOTE_ADDR:
val = r->connection->remote_ip;
break;
case SPECIAL_REMOTE_HOST:
val = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME);
break;
case SPECIAL_REMOTE_USER:
val = r->connection->user;
break;
case SPECIAL_REQUEST_URI:
val = r->uri;
break;
case SPECIAL_REQUEST_METHOD:
val = r->method;
break;
case SPECIAL_REQUEST_PROTOCOL:
val = r->protocol;
break;
case SPECIAL_NOT:
val = ap_table_get(r->headers_in, b->name);
if (val == NULL) {
val = ap_table_get(r->subprocess_env, b->name);
}
break;
}
}
/*
* A NULL value indicates that the header field or special entity
* wasn't present or is undefined. Represent that as an empty string
* so that REs like "^$" will work and allow envariable setting
* based on missing or empty field.
*/
if (val == NULL) {
val = "";
}
if (!ap_regexec(b->preg, val, 0, NULL, 0)) {
array_header *arr = ap_table_elts(b->features);
elts = (table_entry *) arr->elts;
for (j = 0; j < arr->nelts; ++j) {
if (!strcmp(elts[j].val, "!")) {
ap_table_unset(r->subprocess_env, elts[j].key);
}
else {
ap_table_setn(r->subprocess_env, elts[j].key, elts[j].val);
}
}
}
}
return DECLINED;
}
module MODULE_VAR_EXPORT setenvif_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_setenvif_config, /* server config */
merge_setenvif_config, /* merge server configs */
setenvif_module_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* input header parse */
NULL, /* child (process) initialization */
NULL, /* child (process) rundown */
match_headers /* post_read_request */
};

View File

@ -0,0 +1,400 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* mod_unique_id.c: generate a unique identifier for each request
*
* Original author: Dean Gaudet <dgaudet@arctic.org>
* UUencoding modified by: Alvaro Martinez Echevarria <alvaro@lander.es>
*/
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "multithread.h"
#ifdef MULTITHREAD
#error sorry this module does not support multithreaded servers yet
#endif
typedef struct {
unsigned int stamp;
unsigned int in_addr;
unsigned int pid;
unsigned short counter;
} unique_id_rec;
/* Comments:
*
* We want an identifier which is unique across all hits, everywhere.
* "everywhere" includes multiple httpd instances on the same machine, or on
* multiple machines. Essentially "everywhere" should include all possible
* httpds across all servers at a particular "site". We make some assumptions
* that if the site has a cluster of machines then their time is relatively
* synchronized. We also assume that the first address returned by a
* gethostbyname (gethostname()) is unique across all the machines at the
* "site".
*
* We also further assume that pids fit in 32-bits. If something uses more
* than 32-bits, the fix is trivial, but it requires the unrolled uuencoding
* loop to be extended. * A similar fix is needed to support multithreaded
* servers, using a pid/tid combo.
*
* Together, the in_addr and pid are assumed to absolutely uniquely identify
* this one child from all other currently running children on all servers
* (including this physical server if it is running multiple httpds) from each
* other.
*
* The stamp and counter are used to distinguish all hits for a particular
* (in_addr,pid) pair. The stamp is updated using r->request_time,
* saving cpu cycles. The counter is never reset, and is used to permit up to
* 64k requests in a single second by a single child.
*
* The 112-bits of unique_id_rec are encoded using the alphabet
* [A-Za-z0-9@-], resulting in 19 bytes of printable characters. That is then
* stuffed into the environment variable UNIQUE_ID so that it is available to
* other modules. The alphabet choice differs from normal base64 encoding
* [A-Za-z0-9+/] because + and / are special characters in URLs and we want to
* make it easy to use UNIQUE_ID in URLs.
*
* Note that UNIQUE_ID should be considered an opaque token by other
* applications. No attempt should be made to dissect its internal components.
* It is an abstraction that may change in the future as the needs of this
* module change.
*
* It is highly desirable that identifiers exist for "eternity". But future
* needs (such as much faster webservers, moving to 64-bit pids, or moving to a
* multithreaded server) may dictate a need to change the contents of
* unique_id_rec. Such a future implementation should ensure that the first
* field is still a time_t stamp. By doing that, it is possible for a site to
* have a "flag second" in which they stop all of their old-format servers,
* wait one entire second, and then start all of their new-servers. This
* procedure will ensure that the new space of identifiers is completely unique
* from the old space. (Since the first four unencoded bytes always differ.)
*/
/*
* Sun Jun 7 05:43:49 CEST 1998 -- Alvaro
* More comments:
* 1) The UUencoding prodecure is now done in a general way, avoiding the problems
* with sizes and paddings that can arise depending on the architecture. Now the
* offsets and sizes of the elements of the unique_id_rec structure are calculated
* in unique_id_global_init; and then used to duplicate the structure without the
* paddings that might exist. The multithreaded server fix should be now very easy:
* just add a new "tid" field to the unique_id_rec structure, and increase by one
* UNIQUE_ID_REC_MAX.
* 2) unique_id_rec.stamp has been changed from "time_t" to "unsigned int", because
* its size is 64bits on some platforms (linux/alpha), and this caused problems with
* htonl/ntohl. Well, this shouldn't be a problem till year 2106.
*/
static unsigned global_in_addr;
static APACHE_TLS unique_id_rec cur_unique_id;
/*
* Number of elements in the structure unique_id_rec.
*/
#define UNIQUE_ID_REC_MAX 4
static unsigned short unique_id_rec_offset[UNIQUE_ID_REC_MAX],
unique_id_rec_size[UNIQUE_ID_REC_MAX],
unique_id_rec_total_size,
unique_id_rec_size_uu;
static void unique_id_global_init(server_rec *s, pool *p)
{
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif
char str[MAXHOSTNAMELEN + 1];
struct hostent *hent;
#ifndef NO_GETTIMEOFDAY
struct timeval tv;
#endif
/*
* Calculate the sizes and offsets in cur_unique_id.
*/
unique_id_rec_offset[0] = XtOffsetOf(unique_id_rec, stamp);
unique_id_rec_size[0] = sizeof(cur_unique_id.stamp);
unique_id_rec_offset[1] = XtOffsetOf(unique_id_rec, in_addr);
unique_id_rec_size[1] = sizeof(cur_unique_id.in_addr);
unique_id_rec_offset[2] = XtOffsetOf(unique_id_rec, pid);
unique_id_rec_size[2] = sizeof(cur_unique_id.pid);
unique_id_rec_offset[3] = XtOffsetOf(unique_id_rec, counter);
unique_id_rec_size[3] = sizeof(cur_unique_id.counter);
unique_id_rec_total_size = unique_id_rec_size[0] + unique_id_rec_size[1] +
unique_id_rec_size[2] + unique_id_rec_size[3];
/*
* Calculate the size of the structure when encoded.
*/
unique_id_rec_size_uu = (unique_id_rec_total_size*8+5)/6;
/*
* Now get the global in_addr. Note that it is not sufficient to use one
* of the addresses from the main_server, since those aren't as likely to
* be unique as the physical address of the machine
*/
if (gethostname(str, sizeof(str) - 1) != 0) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s,
"gethostname: mod_unique_id requires the hostname of the server");
exit(1);
}
str[sizeof(str) - 1] = '\0';
if ((hent = gethostbyname(str)) == NULL) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s,
"mod_unique_id: unable to gethostbyname(\"%s\")", str);
exit(1);
}
global_in_addr = ((struct in_addr *) hent->h_addr_list[0])->s_addr;
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s,
"mod_unique_id: using ip addr %s",
inet_ntoa(*(struct in_addr *) hent->h_addr_list[0]));
/*
* If the server is pummelled with restart requests we could possibly end
* up in a situation where we're starting again during the same second
* that has been used in previous identifiers. Avoid that situation.
*
* In truth, for this to actually happen not only would it have to restart
* in the same second, but it would have to somehow get the same pids as
* one of the other servers that was running in that second. Which would
* mean a 64k wraparound on pids ... not very likely at all.
*
* But protecting against it is relatively cheap. We just sleep into the
* next second.
*/
#ifdef NO_GETTIMEOFDAY
sleep(1);
#else
if (gettimeofday(&tv, NULL) == -1) {
sleep(1);
}
else if (tv.tv_usec) {
tv.tv_sec = 0;
tv.tv_usec = 1000000 - tv.tv_usec;
select(0, NULL, NULL, NULL, &tv);
}
#endif
}
static void unique_id_child_init(server_rec *s, pool *p)
{
pid_t pid;
#ifndef NO_GETTIMEOFDAY
struct timeval tv;
#endif
/*
* Note that we use the pid because it's possible that on the same
* physical machine there are multiple servers (i.e. using Listen). But
* it's guaranteed that none of them will share the same pids between
* children.
*
* XXX: for multithread this needs to use a pid/tid combo and probably
* needs to be expanded to 32 bits
*/
pid = getpid();
cur_unique_id.pid = pid;
/*
* Test our assumption that the pid is 32-bits. It's possible that
* 64-bit machines will declare pid_t to be 64 bits but only use 32
* of them. It would have been really nice to test this during
* global_init ... but oh well.
*/
if (cur_unique_id.pid != pid) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, s,
"oh no! pids are greater than 32-bits! I'm broken!");
}
cur_unique_id.in_addr = global_in_addr;
/*
* If we use 0 as the initial counter we have a little less protection
* against restart problems, and a little less protection against a clock
* going backwards in time.
*/
#ifndef NO_GETTIMEOFDAY
if (gettimeofday(&tv, NULL) == -1) {
cur_unique_id.counter = 0;
}
else {
/* Some systems have very low variance on the low end of their
* system counter, defend against that.
*/
cur_unique_id.counter = tv.tv_usec / 10;
}
#else
cur_unique_id.counter = 0;
#endif
/*
* We must always use network ordering for these bytes, so that
* identifiers are comparable between machines of different byte
* orderings. Note in_addr is already in network order.
*/
cur_unique_id.pid = htonl(cur_unique_id.pid);
cur_unique_id.counter = htons(cur_unique_id.counter);
}
/* NOTE: This is *NOT* the same encoding used by base64encode ... the last two
* characters should be + and /. But those two characters have very special
* meanings in URLs, and we want to make it easy to use identifiers in
* URLs. So we replace them with @ and -.
*/
static const char uuencoder[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '@', '-',
};
static int gen_unique_id(request_rec *r)
{
char *str;
/*
* Buffer padded with two final bytes, used to copy the unique_id_red
* structure without the internal paddings that it could have.
*/
struct {
unique_id_rec foo;
unsigned char pad[2];
} paddedbuf;
unsigned char *x,*y;
unsigned short counter;
const char *e;
int i,j,k;
/* copy the unique_id if this is an internal redirect (we're never
* actually called for sub requests, so we don't need to test for
* them) */
if (r->prev && (e = ap_table_get(r->subprocess_env, "REDIRECT_UNIQUE_ID"))) {
ap_table_setn(r->subprocess_env, "UNIQUE_ID", e);
return DECLINED;
}
cur_unique_id.stamp = htonl((unsigned int)r->request_time);
/* we'll use a temporal buffer to avoid uuencoding the possible internal
* paddings of the original structure */
x = (unsigned char *) &paddedbuf;
y = (unsigned char *) &cur_unique_id;
k = 0;
for (i = 0; i < UNIQUE_ID_REC_MAX; i++) {
y = ((unsigned char *) &cur_unique_id) + unique_id_rec_offset[i];
for (j = 0; j < unique_id_rec_size[i]; j++, k++) {
x[k] = y[j];
}
}
/*
* We reset two more bytes just in case padding is needed for the uuencoding.
*/
x[k++] = '\0';
x[k++] = '\0';
/* alloc str and do the uuencoding */
str = (char *)ap_palloc(r->pool, unique_id_rec_size_uu + 1);
k = 0;
for (i = 0; i < unique_id_rec_total_size; i += 3) {
y = x + i;
str[k++] = uuencoder[y[0] >> 2];
str[k++] = uuencoder[((y[0] & 0x03) << 4) | ((y[1] & 0xf0) >> 4)];
if (k == unique_id_rec_size_uu) break;
str[k++] = uuencoder[((y[1] & 0x0f) << 2) | ((y[2] & 0xc0) >> 6)];
if (k == unique_id_rec_size_uu) break;
str[k++] = uuencoder[y[2] & 0x3f];
}
str[k++] = '\0';
/* set the environment variable */
ap_table_setn(r->subprocess_env, "UNIQUE_ID", str);
/* and increment the identifier for the next call */
counter = ntohs(cur_unique_id.counter) + 1;
cur_unique_id.counter = htons(counter);
return DECLINED;
}
module MODULE_VAR_EXPORT unique_id_module = {
STANDARD_MODULE_STUFF,
unique_id_global_init, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
NULL, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
unique_id_child_init, /* child_init */
NULL, /* child_exit */
gen_unique_id /* post_read_request */
};

View File

@ -0,0 +1,377 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/* User Tracking Module (Was mod_cookies.c)
*
* This Apache module is designed to track users paths through a site.
* It uses the client-side state ("Cookie") protocol developed by Netscape.
* It is known to work on Netscape browsers, Microsoft Internet
* Explorer and others currently being developed.
*
* Each time a page is requested we look to see if the browser is sending
* us a Cookie: header that we previously generated.
*
* If we don't find one then the user hasn't been to this site since
* starting their browser or their browser doesn't support cookies. So
* we generate a unique Cookie for the transaction and send it back to
* the browser (via a "Set-Cookie" header)
* Future requests from the same browser should keep the same Cookie line.
*
* By matching up all the requests with the same cookie you can
* work out exactly what path a user took through your site. To log
* the cookie use the " %{Cookie}n " directive in a custom access log;
*
* Example 1 : If you currently use the standard Log file format (CLF)
* and use the command "TransferLog somefilename", add the line
* LogFormat "%h %l %u %t \"%r\" %s %b %{Cookie}n"
* to your config file.
*
* Example 2 : If you used to use the old "CookieLog" directive, you
* can emulate it by adding the following command to your config file
* CustomLog filename "%{Cookie}n \"%r\" %t"
*
* Notes:
* 1. This code now logs the initial transaction (the one that created
* the cookie to start with).
* 2. This module has been designed to not interfere with other Cookies
* your site may be using; just avoid sending out cookies with
* the name "Apache=" or things will get confused.
* 3. If you want you can modify the Set-Cookie line so that the Cookie
* never expires. You would then get the same Cookie each time the
* user revisits your site.
*
* Mark Cox, mark@ukweb.com, 6 July 95
*
* This file replaces mod_cookies.c
*/
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#if !defined(WIN32) && !defined(MPE)
#include <sys/time.h>
#endif
module MODULE_VAR_EXPORT usertrack_module;
typedef struct {
int always;
time_t expires;
} cookie_log_state;
typedef struct {
int enabled;
char *cookie_name;
} cookie_dir_rec;
/* Define this to allow post-2000 cookies. Cookies use two-digit dates,
* so it might be dicey. (Netscape does it correctly, but others may not)
*/
#define MILLENIAL_COOKIES
/* Make Cookie: Now we have to generate something that is going to be
* pretty unique. We can base it on the pid, time, hostip */
#define COOKIE_NAME "Apache"
static void make_cookie(request_rec *r)
{
cookie_log_state *cls = ap_get_module_config(r->server->module_config,
&usertrack_module);
#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
clock_t mpe_times;
struct tms mpe_tms;
#elif !defined(WIN32)
struct timeval tv;
struct timezone tz = {0, 0};
#endif
/* 1024 == hardcoded constant */
char cookiebuf[1024];
char *new_cookie;
const char *rname = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME);
cookie_dir_rec *dcfg;
dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module);
#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
/* We lack gettimeofday(), so we must use time() to obtain the epoch
seconds, and then times() to obtain CPU clock ticks (milliseconds).
Combine this together to obtain a hopefully unique cookie ID. */
mpe_times = times(&mpe_tms);
ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname,
(int) getpid(),
(long) r->request_time, (long) mpe_tms.tms_utime);
#elif defined(WIN32)
/*
* We lack gettimeofday() and we lack times(). So we'll use a combination
* of time() and GetTickCount(), which returns milliseconds since Windows
* was started. It should be relatively unique.
*/
ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname,
(int) getpid(),
(long) r->request_time, (long) GetTickCount());
#else
gettimeofday(&tv, &tz);
ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%d", rname,
(int) getpid(),
(long) tv.tv_sec, (int) tv.tv_usec / 1000);
#endif
if (cls->expires) {
struct tm *tms;
time_t when = r->request_time + cls->expires;
#ifndef MILLENIAL_COOKIES
/*
* Only two-digit date string, so we can't trust "00" or more.
* Therefore, we knock it all back to just before midnight on
* 1/1/2000 (which is 946684799)
*/
if (when > 946684799)
when = 946684799;
#endif
tms = gmtime(&when);
/* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */
new_cookie = ap_psprintf(r->pool,
"%s=%s; path=/; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
dcfg->cookie_name, cookiebuf, ap_day_snames[tms->tm_wday],
tms->tm_mday, ap_month_snames[tms->tm_mon],
tms->tm_year % 100,
tms->tm_hour, tms->tm_min, tms->tm_sec);
}
else {
new_cookie = ap_psprintf(r->pool, "%s=%s; path=/",
dcfg->cookie_name, cookiebuf);
}
ap_table_setn(r->headers_out, "Set-Cookie", new_cookie);
ap_table_setn(r->notes, "cookie", ap_pstrdup(r->pool, cookiebuf)); /* log first time */
return;
}
static int spot_cookie(request_rec *r)
{
cookie_dir_rec *dcfg = ap_get_module_config(r->per_dir_config,
&usertrack_module);
const char *cookie;
char *value;
if (!dcfg->enabled) {
return DECLINED;
}
if ((cookie = ap_table_get(r->headers_in, "Cookie")))
if ((value = strstr(cookie, dcfg->cookie_name))) {
char *cookiebuf, *cookieend;
value += strlen(dcfg->cookie_name) + 1; /* Skip over the '=' */
cookiebuf = ap_pstrdup(r->pool, value);
cookieend = strchr(cookiebuf, ';');
if (cookieend)
*cookieend = '\0'; /* Ignore anything after a ; */
/* Set the cookie in a note, for logging */
ap_table_setn(r->notes, "cookie", cookiebuf);
return DECLINED; /* There's already a cookie, no new one */
}
make_cookie(r);
return OK; /* We set our cookie */
}
static void *make_cookie_log_state(pool *p, server_rec *s)
{
cookie_log_state *cls =
(cookie_log_state *) ap_palloc(p, sizeof(cookie_log_state));
cls->expires = 0;
return (void *) cls;
}
static void *make_cookie_dir(pool *p, char *d)
{
cookie_dir_rec *dcfg;
dcfg = (cookie_dir_rec *) ap_pcalloc(p, sizeof(cookie_dir_rec));
dcfg->cookie_name = COOKIE_NAME;
dcfg->enabled = 0;
return dcfg;
}
static const char *set_cookie_enable(cmd_parms *cmd, void *mconfig, int arg)
{
cookie_dir_rec *dcfg = mconfig;
dcfg->enabled = arg;
return NULL;
}
static const char *set_cookie_exp(cmd_parms *parms, void *dummy, const char *arg)
{
cookie_log_state *cls = ap_get_module_config(parms->server->module_config,
&usertrack_module);
time_t factor, modifier = 0;
time_t num = 0;
char *word;
/* The simple case first - all numbers (we assume) */
if (ap_isdigit(arg[0]) && ap_isdigit(arg[strlen(arg) - 1])) {
cls->expires = atol(arg);
return NULL;
}
/*
* The harder case - stolen from mod_expires
*
* CookieExpires "[plus] {<num> <type>}*"
*/
word = ap_getword_conf(parms->pool, &arg);
if (!strncasecmp(word, "plus", 1)) {
word = ap_getword_conf(parms->pool, &arg);
};
/* {<num> <type>}* */
while (word[0]) {
/* <num> */
if (ap_isdigit(word[0]))
num = atoi(word);
else
return "bad expires code, numeric value expected.";
/* <type> */
word = ap_getword_conf(parms->pool, &arg);
if (!word[0])
return "bad expires code, missing <type>";
factor = 0;
if (!strncasecmp(word, "years", 1))
factor = 60 * 60 * 24 * 365;
else if (!strncasecmp(word, "months", 2))
factor = 60 * 60 * 24 * 30;
else if (!strncasecmp(word, "weeks", 1))
factor = 60 * 60 * 24 * 7;
else if (!strncasecmp(word, "days", 1))
factor = 60 * 60 * 24;
else if (!strncasecmp(word, "hours", 1))
factor = 60 * 60;
else if (!strncasecmp(word, "minutes", 2))
factor = 60;
else if (!strncasecmp(word, "seconds", 1))
factor = 1;
else
return "bad expires code, unrecognized type";
modifier = modifier + factor * num;
/* next <num> */
word = ap_getword_conf(parms->pool, &arg);
}
cls->expires = modifier;
return NULL;
}
static const char *set_cookie_name(cmd_parms *cmd, void *mconfig, char *name)
{
cookie_dir_rec *dcfg = (cookie_dir_rec *) mconfig;
dcfg->cookie_name = ap_pstrdup(cmd->pool, name);
return NULL;
}
static const command_rec cookie_log_cmds[] = {
{"CookieExpires", set_cookie_exp, NULL, RSRC_CONF, TAKE1,
"an expiry date code"},
{"CookieTracking", set_cookie_enable, NULL, OR_FILEINFO, FLAG,
"whether or not to enable cookies"},
{"CookieName", set_cookie_name, NULL, OR_FILEINFO, TAKE1,
"name of the tracking cookie"},
{NULL}
};
module MODULE_VAR_EXPORT usertrack_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
make_cookie_dir, /* dir config creater */
NULL, /* dir merger --- default is to override */
make_cookie_log_state, /* server config */
NULL, /* merge server configs */
cookie_log_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
spot_cookie, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};

12
modules/proxy/.cvsignore Normal file
View File

@ -0,0 +1,12 @@
Debug
Release
ApacheModuleProxy.dsw
ApacheModuleProxy.mdp
ApacheModuleProxy.ncb
ApacheModuleProxy.opt
ApacheModuleProxy.plg
Makefile
*.lo
*.so
*.dll
*.def

55
modules/proxy/.indent.pro vendored Normal file
View File

@ -0,0 +1,55 @@
-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
-TBUFF
-TFILE
-TTRANS
-TUINT4
-T_trans
-Tallow_options_t
-Tapache_sfio
-Tarray_header
-Tbool_int
-Tbuf_area
-Tbuff_struct
-Tbuffy
-Tcmd_how
-Tcmd_parms
-Tcommand_rec
-Tcommand_struct
-Tconn_rec
-Tcore_dir_config
-Tcore_server_config
-Tdir_maker_func
-Tevent
-Tglobals_s
-Thandler_func
-Thandler_rec
-Tjoblist_s
-Tlisten_rec
-Tmerger_func
-Tmode_t
-Tmodule
-Tmodule_struct
-Tmutex
-Tn_long
-Tother_child_rec
-Toverrides_t
-Tparent_score
-Tpid_t
-Tpiped_log
-Tpool
-Trequest_rec
-Trequire_line
-Trlim_t
-Tscoreboard
-Tsemaphore
-Tserver_addr_rec
-Tserver_rec
-Tserver_rec_chain
-Tshort_score
-Ttable
-Ttable_entry
-Tthread
-Tu_wide_int
-Tvtime_t
-Twide_int
-Tproxy_server_conf

View File

@ -0,0 +1,4 @@
This is a place-holder which indicates to Configure that it shouldn't
provide the default targets when building the Makefile in this directory.
Instead it'll just prepend all the important variable definitions, and
copy the Makefile.tmpl onto the end.

898
modules/proxy/mod_proxy.c Normal file
View File

@ -0,0 +1,898 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#include "mod_proxy.h"
#define CORE_PRIVATE
#include "http_log.h"
#include "http_vhost.h"
#include "http_request.h"
/* Some WWW schemes and their default ports; this is basically /etc/services */
/* This will become global when the protocol abstraction comes */
static struct proxy_services defports[] =
{
{"http", DEFAULT_HTTP_PORT},
{"ftp", DEFAULT_FTP_PORT},
{"https", DEFAULT_HTTPS_PORT},
{"gopher", DEFAULT_GOPHER_PORT},
{"nntp", DEFAULT_NNTP_PORT},
{"wais", DEFAULT_WAIS_PORT},
{"snews", DEFAULT_SNEWS_PORT},
{"prospero", DEFAULT_PROSPERO_PORT},
{NULL, -1} /* unknown port */
};
/*
* A Web proxy module. Stages:
*
* translate_name: set filename to proxy:<URL>
* type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
* fix_ups: convert the URL stored in the filename to the
* canonical form.
* handler: handle proxy requests
*/
/* -------------------------------------------------------------- */
/* Translate the URL into a 'filename' */
static int alias_match(const char *uri, const char *alias_fakename)
{
const char *end_fakename = alias_fakename + strlen(alias_fakename);
const char *aliasp = alias_fakename, *urip = uri;
while (aliasp < end_fakename) {
if (*aliasp == '/') {
/* any number of '/' in the alias matches any number in
* the supplied URI, but there must be at least one...
*/
if (*urip != '/')
return 0;
while (*aliasp == '/')
++aliasp;
while (*urip == '/')
++urip;
}
else {
/* Other characters are compared literally */
if (*urip++ != *aliasp++)
return 0;
}
}
/* Check last alias path component matched all the way */
if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
return 0;
/* Return number of characters from URI which matched (may be
* greater than length of alias, since we may have matched
* doubled slashes)
*/
return urip - uri;
}
/* Detect if an absoluteURI should be proxied or not. Note that we
* have to do this during this phase because later phases are
* "short-circuiting"... i.e. translate_names will end when the first
* module returns OK. So for example, if the request is something like:
*
* GET http://othervhost/cgi-bin/printenv HTTP/1.0
*
* mod_alias will notice the /cgi-bin part and ScriptAlias it and
* short-circuit the proxy... just because of the ordering in the
* configuration file.
*/
static int proxy_detect(request_rec *r)
{
void *sconf = r->server->module_config;
proxy_server_conf *conf;
conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
if (conf->req && r->parsed_uri.scheme) {
/* but it might be something vhosted */
if (!(r->parsed_uri.hostname
&& !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
&& ap_matches_request_vhost(r, r->parsed_uri.hostname,
r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
r->proxyreq = 1;
r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
}
}
/* We need special treatment for CONNECT proxying: it has no scheme part */
else if (conf->req && r->method_number == M_CONNECT
&& r->parsed_uri.hostname
&& r->parsed_uri.port_str) {
r->proxyreq = 1;
r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
}
return DECLINED;
}
static int proxy_trans(request_rec *r)
{
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
int i, len;
struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves
* in proxy_detect
*/
return OK;
}
/* XXX: since r->uri has been manipulated already we're not really
* compliant with RFC1945 at this point. But this probably isn't
* an issue because this is a hybrid proxy/origin server.
*/
for (i = 0; i < conf->aliases->nelts; i++) {
len = alias_match(r->uri, ent[i].fake);
if (len > 0) {
r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
r->uri + len, NULL);
r->handler = "proxy-server";
r->proxyreq = 1;
return OK;
}
}
return DECLINED;
}
/* -------------------------------------------------------------- */
/* Fixup the filename */
/*
* Canonicalise the URL
*/
static int proxy_fixup(request_rec *r)
{
char *url, *p;
if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED;
url = &r->filename[6];
/* canonicalise each specific scheme */
if (strncasecmp(url, "http:", 5) == 0)
return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
else if (strncasecmp(url, "ftp:", 4) == 0)
return ap_proxy_ftp_canon(r, url + 4);
p = strchr(url, ':');
if (p == NULL || p == url)
return HTTP_BAD_REQUEST;
return OK; /* otherwise; we've done the best we can */
}
static void proxy_init(server_rec *r, pool *p)
{
ap_proxy_garbage_init(r, p);
}
/* Send a redirection if the request contains a hostname which is not */
/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
/* servers like Netscape's allow this and access hosts from the local */
/* domain in this case. I think it is better to redirect to a FQDN, since */
/* these will later be found in the bookmarks files. */
/* The "ProxyDomain" directive determines what domain will be appended */
static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
{
char *nuri;
const char *ref;
/* We only want to worry about GETs */
if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
return DECLINED;
/* If host does contain a dot already, or it is "localhost", decline */
if (strchr(r->parsed_uri.hostname, '.') != NULL
|| strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
return DECLINED; /* host name has a dot already */
ref = ap_table_get(r->headers_in, "Referer");
/* Reassemble the request, but insert the domain after the host name */
/* Note that the domain name always starts with a dot */
r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname,
domain, NULL);
nuri = ap_unparse_uri_components(r->pool,
&r->parsed_uri,
UNP_REVEALPASSWORD);
ap_table_set(r->headers_out, "Location", nuri);
ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
"Domain missing: %s sent to %s%s%s", r->uri,
ap_unparse_uri_components(r->pool, &r->parsed_uri,
UNP_OMITUSERINFO),
ref ? " from " : "", ref ? ref : "");
return HTTP_MOVED_PERMANENTLY;
}
/* -------------------------------------------------------------- */
/* Invoke handler */
static int proxy_handler(request_rec *r)
{
char *url, *scheme, *p;
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
array_header *proxies = conf->proxies;
struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
int i, rc;
cache_req *cr;
int direct_connect = 0;
const char *maxfwd_str;
if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED;
if (r->method_number == M_TRACE &&
(maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
int maxfwd = strtol(maxfwd_str, NULL, 10);
if (maxfwd < 1) {
int access_status;
r->proxyreq = 0;
if ((access_status = ap_send_http_trace(r)))
ap_die(access_status, r);
else
ap_finalize_request_protocol(r);
return OK;
}
ap_table_setn(r->headers_in, "Max-Forwards",
ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0));
}
if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
return rc;
url = r->filename + 6;
p = strchr(url, ':');
if (p == NULL)
return HTTP_BAD_REQUEST;
rc = ap_proxy_cache_check(r, url, &conf->cache, &cr);
if (rc != DECLINED)
return rc;
/* If the host doesn't have a domain name, add one and redirect. */
if (conf->domain != NULL) {
rc = proxy_needsdomain(r, url, conf->domain);
if (ap_is_HTTP_REDIRECT(rc))
return HTTP_MOVED_PERMANENTLY;
}
*p = '\0';
scheme = ap_pstrdup(r->pool, url);
*p = ':';
/* Check URI's destination host against NoProxy hosts */
/* Bypass ProxyRemote server lookup if configured as NoProxy */
/* we only know how to handle communication to a proxy via http */
/*if (strcasecmp(scheme, "http") == 0) */
{
int ii;
struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
direct_connect = list[ii].matcher(&list[ii], r);
}
#if DEBUGGING
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
(direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
r->uri);
#endif
}
/* firstly, try a proxy, unless a NoProxy directive is active */
if (!direct_connect)
for (i = 0; i < proxies->nelts; i++) {
p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */
if (strcmp(ents[i].scheme, "*") == 0 ||
(p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
(p != NULL &&
strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
/* CONNECT is a special method that bypasses the normal
* proxy code.
*/
if (r->method_number == M_CONNECT)
rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
ents[i].port);
/* we only know how to handle communication to a proxy via http */
else if (strcasecmp(ents[i].protocol, "http") == 0)
rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
ents[i].port);
else
rc = DECLINED;
/* an error or success */
if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
return rc;
/* we failed to talk to the upstream proxy */
}
}
/* otherwise, try it direct */
/* N.B. what if we're behind a firewall, where we must use a proxy or
* give up??
*/
/* handle the scheme */
if (r->method_number == M_CONNECT)
return ap_proxy_connect_handler(r, cr, url, NULL, 0);
if (strcasecmp(scheme, "http") == 0)
return ap_proxy_http_handler(r, cr, url, NULL, 0);
if (strcasecmp(scheme, "ftp") == 0)
return ap_proxy_ftp_handler(r, cr, url);
else
return HTTP_FORBIDDEN;
}
/* -------------------------------------------------------------- */
/* Setup configurable data */
static void *
create_proxy_config(pool *p, server_rec *s)
{
proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
ps->proxies = ap_make_array(p, 10, sizeof(struct proxy_remote));
ps->aliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
ps->raliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry));
ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry));
ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry));
ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int));
ps->domain = NULL;
ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
ps->req = 0;
ps->cache.root = NULL;
ps->cache.space = DEFAULT_CACHE_SPACE;
ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
ps->cache.gcinterval = -1;
/* at these levels, the cache can have 2^18 directories (256,000) */
ps->cache.dirlevels = 3;
ps->cache.dirlength = 1;
ps->cache.cache_completion = DEFAULT_CACHE_COMPLETION;
return ps;
}
static const char *
add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
{
server_rec *s = cmd->server;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
struct proxy_remote *new;
char *p, *q;
int port;
p = strchr(r, ':');
if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
return "ProxyRemote: Bad syntax for a remote proxy server";
q = strchr(p + 3, ':');
if (q != NULL) {
if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
*q = '\0';
}
else
port = -1;
*p = '\0';
if (strchr(f, ':') == NULL)
ap_str_tolower(f); /* lowercase scheme */
ap_str_tolower(p + 3); /* lowercase hostname */
if (port == -1) {
int i;
for (i = 0; defports[i].scheme != NULL; i++)
if (strcasecmp(defports[i].scheme, r) == 0)
break;
port = defports[i].port;
}
new = ap_push_array(conf->proxies);
new->scheme = f;
new->protocol = r;
new->hostname = p + 3;
new->port = port;
return NULL;
}
static const char *
add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
{
server_rec *s = cmd->server;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
struct proxy_alias *new;
new = ap_push_array(conf->aliases);
new->fake = f;
new->real = r;
return NULL;
}
static const char *
add_pass_reverse(cmd_parms *cmd, void *dummy, char *f, char *r)
{
server_rec *s = cmd->server;
proxy_server_conf *conf;
struct proxy_alias *new;
conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
&proxy_module);
new = ap_push_array(conf->raliases);
new->fake = f;
new->real = r;
return NULL;
}
static const char *
set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg)
{
server_rec *s = parms->server;
proxy_server_conf *conf =
ap_get_module_config(s->module_config, &proxy_module);
struct noproxy_entry *new;
struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
struct hostent hp;
int found = 0;
int i;
/* Don't duplicate entries */
for (i = 0; i < conf->noproxies->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
found = 1;
}
if (!found) {
new = ap_push_array(conf->noproxies);
new->name = arg;
/* Don't do name lookups on things that aren't dotted */
if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
else
new->addr.s_addr = 0;
}
return NULL;
}
/*
* Set the ports CONNECT can use
*/
static const char *
set_allowed_ports(cmd_parms *parms, void *dummy, char *arg)
{
server_rec *s = parms->server;
proxy_server_conf *conf =
ap_get_module_config(s->module_config, &proxy_module);
int *New;
if (!ap_isdigit(arg[0]))
return "AllowCONNECT: port number must be numeric";
New = ap_push_array(conf->allowed_connect_ports);
*New = atoi(arg);
return NULL;
}
/* Similar to set_proxy_exclude(), but defining directly connected hosts,
* which should never be accessed via the configured ProxyRemote servers
*/
static const char *
set_proxy_dirconn(cmd_parms *parms, void *dummy, char *arg)
{
server_rec *s = parms->server;
proxy_server_conf *conf =
ap_get_module_config(s->module_config, &proxy_module);
struct dirconn_entry *New;
struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
int found = 0;
int i;
/* Don't duplicate entries */
for (i = 0; i < conf->dirconn->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0)
found = 1;
}
if (!found) {
New = ap_push_array(conf->dirconn);
New->name = arg;
New->hostentry = NULL;
if (ap_proxy_is_ipaddr(New, parms->pool)) {
#if DEBUGGING
fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr));
fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask));
#endif
}
else if (ap_proxy_is_domainname(New, parms->pool)) {
ap_str_tolower(New->name);
#if DEBUGGING
fprintf(stderr, "Parsed domain %s\n", New->name);
#endif
}
else if (ap_proxy_is_hostname(New, parms->pool)) {
ap_str_tolower(New->name);
#if DEBUGGING
fprintf(stderr, "Parsed host %s\n", New->name);
#endif
}
else {
ap_proxy_is_word(New, parms->pool);
#if DEBUGGING
fprintf(stderr, "Parsed word %s\n", New->name);
#endif
}
}
return NULL;
}
static const char *
set_proxy_domain(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
if (arg[0] != '.')
return "ProxyDomain: domain name must start with a dot.";
psf->domain = arg;
return NULL;
}
static const char *
set_proxy_req(cmd_parms *parms, void *dummy, int flag)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
psf->req = flag;
return NULL;
}
static const char *
set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
if (sscanf(arg, "%d", &val) != 1)
return "CacheSize value must be an integer (kBytes)";
psf->cache.space = val;
return NULL;
}
static const char *
set_cache_root(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
psf->cache.root = arg;
return NULL;
}
static const char *
set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheLastModifiedFactor value must be a float";
psf->cache.lmfactor = val;
return NULL;
}
static const char *
set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheMaxExpire value must be a float";
psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheDefaultExpire value must be a float";
psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheGcInterval value must be a float";
psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
val = atoi(arg);
if (val < 1)
return "CacheDirLevels value must be an integer greater than 0";
if (val * psf->cache.dirlength > CACHEFILE_LEN)
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
psf->cache.dirlevels = val;
return NULL;
}
static const char *
set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
val = atoi(arg);
if (val < 1)
return "CacheDirLength value must be an integer greater than 0";
if (val * psf->cache.dirlevels > CACHEFILE_LEN)
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
psf->cache.dirlength = val;
return NULL;
}
static const char *
set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
{
server_rec *s = parms->server;
proxy_server_conf *conf =
ap_get_module_config(s->module_config, &proxy_module);
struct nocache_entry *new;
struct nocache_entry *list = (struct nocache_entry *) conf->nocaches->elts;
struct hostent hp;
int found = 0;
int i;
/* Don't duplicate entries */
for (i = 0; i < conf->nocaches->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
found = 1;
}
if (!found) {
new = ap_push_array(conf->nocaches);
new->name = arg;
/* Don't do name lookups on things that aren't dotted */
if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
else
new->addr.s_addr = 0;
}
return NULL;
}
static const char *
set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int s = atoi(arg);
if (s < 512 && s != 0) {
return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
}
psf->recv_buffer_size = s;
return NULL;
}
static const char*
set_cache_completion(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int s = atoi(arg);
if (s > 100 || s < 0) {
return "CacheForceCompletion must be <= 100 percent, "
"or 0 for system default.";
}
if (s > 0)
psf->cache.cache_completion = ((float)s / 100);
return NULL;
}
static const char*
set_via_opt(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
if (strcasecmp(arg, "Off") == 0)
psf->viaopt = via_off;
else if (strcasecmp(arg, "On") == 0)
psf->viaopt = via_on;
else if (strcasecmp(arg, "Block") == 0)
psf->viaopt = via_block;
else if (strcasecmp(arg, "Full") == 0)
psf->viaopt = via_full;
else {
return "ProxyVia must be one of: "
"off | on | full | block";
}
return NULL;
}
static const handler_rec proxy_handlers[] =
{
{"proxy-server", proxy_handler},
{NULL}
};
static const command_rec proxy_cmds[] =
{
{"ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG,
"on if the true proxy requests should be accepted"},
{"ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2,
"a scheme, partial URL or '*' and a proxy server"},
{"ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2,
"a virtual path and a URL"},
{"ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF, TAKE2,
"a virtual path and a URL for reverse proxy behaviour"},
{"ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE,
"A list of names, hosts or domains to which the proxy will not connect"},
{"ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, TAKE1,
"Receive buffer size for outgoing HTTP and FTP connections in bytes"},
{"NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, ITERATE,
"A list of domains, hosts, or subnets to which the proxy will connect directly"},
{"ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, TAKE1,
"The default intranet domain name (in absence of a domain in the URL)"},
{"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE,
"A list of ports which CONNECT may connect to"},
{"CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
"The directory to store cache files"},
{"CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
"The maximum disk space used by the cache in Kb"},
{"CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
"The maximum time in hours to cache a document"},
{"CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
"The default time in hours to cache a document"},
{"CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
"The factor used to estimate Expires date from LastModified date"},
{"CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
"The interval between garbage collections, in hours"},
{"CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
"The number of levels of subdirectories in the cache"},
{"CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
"The number of characters in subdirectory names"},
{"NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
"A list of names, hosts or domains for which caching is *not* provided"},
{"CacheForceCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1,
"Force a http cache completion after this percentage is loaded"},
{"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1,
"Configure Via: proxy header header to one of: on | off | block | full"},
{NULL}
};
module MODULE_VAR_EXPORT proxy_module =
{
STANDARD_MODULE_STUFF,
proxy_init, /* initializer */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_proxy_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
proxy_cmds, /* command table */
proxy_handlers, /* handlers */
proxy_trans, /* translate_handler */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
proxy_fixup, /* pre-run fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
proxy_detect /* post read-request */
};

133
modules/proxy/mod_proxy.dsp Normal file
View File

@ -0,0 +1,133 @@
# Microsoft Developer Studio Project File - Name="ApacheModuleProxy" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 5.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=ApacheModuleProxy - Win32 Release
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "ApacheModuleProxy.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "ApacheModuleProxy.mak"\
CFG="ApacheModuleProxy - Win32 Release"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "ApacheModuleProxy - Win32 Release" (based on\
"Win32 (x86) Dynamic-Link Library")
!MESSAGE "ApacheModuleProxy - Win32 Debug" (based on\
"Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "ApacheModuleProxy - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir ".\ApacheMo"
# PROP BASE Intermediate_Dir ".\ApacheMo"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir ".\Release"
# PROP Intermediate_Dir ".\Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386
!ELSEIF "$(CFG)" == "ApacheModuleProxy - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir ".\ApacheM0"
# PROP BASE Intermediate_Dir ".\ApacheM0"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir ".\Debug"
# PROP Intermediate_Dir ".\Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386
!ENDIF
# Begin Target
# Name "ApacheModuleProxy - Win32 Release"
# Name "ApacheModuleProxy - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
# Begin Source File
SOURCE=.\mod_proxy.c
# End Source File
# Begin Source File
SOURCE=.\proxy_cache.c
# End Source File
# Begin Source File
SOURCE=.\proxy_connect.c
# End Source File
# Begin Source File
SOURCE=.\proxy_ftp.c
# End Source File
# Begin Source File
SOURCE=.\proxy_http.c
# End Source File
# Begin Source File
SOURCE=.\proxy_util.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
# Begin Source File
SOURCE=.\mod_proxy.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

316
modules/proxy/mod_proxy.h Normal file
View File

@ -0,0 +1,316 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#ifndef MOD_PROXY_H
#define MOD_PROXY_H
/*
* Main include file for the Apache proxy
*/
/*
Note that the Explain() stuff is not yet complete.
Also note numerous FIXMEs and CHECKMEs which should be eliminated.
If TESTING is set, then garbage collection doesn't delete ... probably a good
idea when hacking.
This code is still experimental!
Things to do:
1. Make it garbage collect in the background, not while someone is waiting for
a response!
2. Check the logic thoroughly.
3. Empty directories are only removed the next time round (but this does avoid
two passes). Consider doing them the first time round.
Ben Laurie <ben@algroup.co.uk> 30 Mar 96
More things to do:
0. Code cleanup (ongoing)
1. add 230 response output for ftp now that it works
2. Make the ftp proxy transparent, also same with (future) gopher & wais
3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik)
4. Use a cache expiry database for more efficient GC (Jeremy Wohl)
5. Bulletproof GC against SIGALRM
Chuck Murcko <chuck@topsail.org> 15 April 1997
*/
#define TESTING 0
#undef EXPLAIN
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "explain.h"
extern module MODULE_VAR_EXPORT proxy_module;
/* for proxy_canonenc() */
enum enctype {
enc_path, enc_search, enc_user, enc_fpath, enc_parm
};
#define HDR_APP (0) /* append header, for proxy_add_header() */
#define HDR_REP (1) /* replace header, for proxy_add_header() */
/* number of characters in the hash */
#define HASH_LEN (22*2)
/* maximum 'CacheDirLevels*CacheDirLength' value */
#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */
#ifdef CHARSET_EBCDIC
#define CRLF "\r\n"
#else /*CHARSET_EBCDIC*/
#define CRLF "\015\012"
#endif /*CHARSET_EBCDIC*/
#define SEC_ONE_DAY 86400 /* one day, in seconds */
#define SEC_ONE_HR 3600 /* one hour, in seconds */
#define DEFAULT_FTP_DATA_PORT 20
#define DEFAULT_FTP_PORT 21
#define DEFAULT_GOPHER_PORT 70
#define DEFAULT_NNTP_PORT 119
#define DEFAULT_WAIS_PORT 210
#define DEFAULT_HTTPS_PORT 443
#define DEFAULT_SNEWS_PORT 563
#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */
/* Some WWW schemes and their default ports; this is basically /etc/services */
struct proxy_services {
const char *scheme;
int port;
};
/* static information about a remote proxy */
struct proxy_remote {
const char *scheme; /* the schemes handled by this proxy, or '*' */
const char *protocol; /* the scheme used to talk to this proxy */
const char *hostname; /* the hostname of this proxy */
int port; /* the port for this proxy */
};
struct proxy_alias {
char *real;
char *fake;
};
struct dirconn_entry {
char *name;
struct in_addr addr, mask;
struct hostent *hostentry;
int (*matcher) (struct dirconn_entry * This, request_rec *r);
};
struct noproxy_entry {
char *name;
struct in_addr addr;
};
struct nocache_entry {
char *name;
struct in_addr addr;
};
#define DEFAULT_CACHE_SPACE 5
#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR
#define DEFAULT_CACHE_LMFACTOR (0.1)
#define DEFAULT_CACHE_COMPLETION (0.9)
/* static information about the local cache */
struct cache_conf {
const char *root; /* the location of the cache directory */
off_t space; /* Maximum cache size (in 1024 bytes) */
time_t maxexpire; /* Maximum time to keep cached files in secs */
time_t defaultexpire; /* default time to keep cached file in secs */
double lmfactor; /* factor for estimating expires date */
time_t gcinterval; /* garbage collection interval, in seconds */
int dirlevels; /* Number of levels of subdirectories */
int dirlength; /* Length of subdirectory names */
float cache_completion; /* Force cache completion after this point */
};
typedef struct {
struct cache_conf cache; /* cache configuration */
array_header *proxies;
array_header *aliases;
array_header *raliases;
array_header *noproxies;
array_header *dirconn;
array_header *nocaches;
array_header *allowed_connect_ports;
char *domain; /* domain name to use in absence of a domain name in the request */
int req; /* true if proxy requests are enabled */
enum {
via_off,
via_on,
via_block,
via_full
} viaopt; /* how to deal with proxy Via: headers */
size_t recv_buffer_size;
} proxy_server_conf;
struct hdr_entry {
const char *field;
const char *value;
};
/* caching information about a request */
typedef struct {
request_rec *req; /* the request */
char *url; /* the URL requested */
char *filename; /* name of the cache file, or NULL if no cache */
char *tempfile; /* name of the temporary file, of NULL if not caching */
time_t ims; /* if-modified-since date of request; -1 if no header */
BUFF *fp; /* the cache file descriptor if the file is cached
and may be returned, or NULL if the file is
not cached (or must be reloaded) */
time_t expire; /* calculated expire date of cached entity */
time_t lmod; /* last-modified date of cached entity */
time_t date; /* the date the cached file was last touched */
int version; /* update count of the file */
off_t len; /* content length */
char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */
int status; /* the status of the cached file */
unsigned int written; /* total *content* bytes written to cache */
float cache_completion; /* specific to this request */
char *resp_line; /* the whole status like (protocol, code + message) */
table *hdrs; /* the HTTP headers of the file */
} cache_req;
/* Additional information passed to the function called by ap_table_do() */
struct tbl_do_args {
request_rec *req;
cache_req *cache;
};
/* Function prototypes */
/* proxy_cache.c */
void ap_proxy_cache_tidy(cache_req *c);
int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
cache_req **cr);
int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
const int is_HTTP1, int nocache);
void ap_proxy_garbage_coll(request_rec *r);
/* proxy_connect.c */
int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
const char *proxyhost, int proxyport);
/* proxy_ftp.c */
int ap_proxy_ftp_canon(request_rec *r, char *url);
int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url);
/* proxy_http.c */
int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme,
int def_port);
int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
const char *proxyhost, int proxyport);
/* proxy_util.c */
int ap_proxy_hex2c(const char *x);
void ap_proxy_c2hex(int ch, char *x);
char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
int isenc);
char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
char **passwordp, char **hostp, int *port);
const char *ap_proxy_date_canon(pool *p, const char *x);
table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f);
long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c);
void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs);
int ap_proxy_liststr(const char *list, const char *val);
void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength);
int ap_proxy_hex2sec(const char *x);
void ap_proxy_sec2hex(int t, char *y);
cache_req *ap_proxy_cache_error(cache_req *r);
int ap_proxyerror(request_rec *r, int statuscode, const char *message);
const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp);
int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p);
int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p);
int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p);
int ap_proxy_is_word(struct dirconn_entry *This, pool *p);
int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r);
int ap_proxy_garbage_init(server_rec *, pool *);
/* This function is called by ap_table_do() for all header lines */
int ap_proxy_send_hdr_line(void *p, const char *key, const char *value);
unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache);
#endif /*MOD_PROXY_H*/

View File

@ -0,0 +1,288 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/* CONNECT method for Apache proxy */
#include "mod_proxy.h"
#include "http_log.h"
#include "http_main.h"
#ifdef HAVE_BSTRING_H
#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
#endif
DEF_Explain
/*
* This handles Netscape CONNECT method secure proxy requests.
* A connection is opened to the specified host and data is
* passed through between the WWW site and the browser.
*
* This code is based on the INTERNET-DRAFT document
* "Tunneling SSL Through a WWW Proxy" currently at
* http://www.mcom.com/newsref/std/tunneling_ssl.html.
*
* If proxyhost and proxyport are set, we send a CONNECT to
* the specified proxy..
*
* FIXME: this is bad, because it does its own socket I/O
* instead of using the I/O in buff.c. However,
* the I/O in buff.c blocks on reads, and because
* this function doesn't know how much data will
* be sent either way (or when) it can't use blocking
* I/O. This may be very implementation-specific
* (to Linux). Any suggestions?
* FIXME: this doesn't log the number of bytes sent, but
* that may be okay, since the data is supposed to
* be transparent. In fact, this doesn't log at all
* yet. 8^)
* FIXME: doesn't check any headers initally sent from the
* client.
* FIXME: should allow authentication, but hopefully the
* generic proxy authentication is good enough.
* FIXME: no check for r->assbackwards, whatever that is.
*/
static int
allowed_port(proxy_server_conf *conf, int port)
{
int i;
int *list = (int *) conf->allowed_connect_ports->elts;
for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
if(port == list[i])
return 1;
}
return 0;
}
int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
const char *proxyhost, int proxyport)
{
struct sockaddr_in server;
struct in_addr destaddr;
struct hostent server_hp;
const char *host, *err;
char *p;
int port, sock;
char buffer[HUGE_STRING_LEN];
int nbytes, i, j;
fd_set fds;
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
/* Break the URL into host:port pairs */
host = url;
p = strchr(url, ':');
if (p == NULL)
port = DEFAULT_HTTPS_PORT;
else {
port = atoi(p + 1);
*p = '\0';
}
/* check if ProxyBlock directive on this host */
destaddr.s_addr = ap_inet_addr(host);
for (i = 0; i < conf->noproxies->nelts; i++) {
if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
|| destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked");
}
/* Check if it is an allowed port */
if (conf->allowed_connect_ports->nelts == 0) {
/* Default setting if not overridden by AllowCONNECT */
switch (port) {
case DEFAULT_HTTPS_PORT:
case DEFAULT_SNEWS_PORT:
break;
default:
return HTTP_FORBIDDEN;
}
} else if(!allowed_port(conf, port))
return HTTP_FORBIDDEN;
if (proxyhost) {
Explain2("CONNECT to remote proxy %s on port %d", proxyhost, proxyport);
}
else {
Explain2("CONNECT to %s on port %d", host, port);
}
server.sin_port = (proxyport ? htons(proxyport) : htons(port));
err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp);
if (err != NULL)
return ap_proxyerror(r,
proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR,
err);
sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket");
return HTTP_INTERNAL_SERVER_ERROR;
}
#ifdef CHECK_FD_SETSIZE
if (sock >= FD_SETSIZE) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
"proxy_connect_handler: filedescriptor (%u) "
"larger than FD_SETSIZE (%u) "
"found, you probably need to rebuild Apache with a "
"larger FD_SETSIZE", sock, FD_SETSIZE);
ap_pclosesocket(r->pool, sock);
return HTTP_INTERNAL_SERVER_ERROR;
}
#endif
j = 0;
while (server_hp.h_addr_list[j] != NULL) {
memcpy(&server.sin_addr, server_hp.h_addr_list[j],
sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
j++;
}
if (i == -1) {
ap_pclosesocket(r->pool, sock);
return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool,
"Could not connect to remote machine:<br>",
strerror(errno), NULL));
}
/* If we are connecting through a remote proxy, we need to pass
* the CONNECT request on to it.
*/
if (proxyport) {
/* FIXME: We should not be calling write() directly, but we currently
* have no alternative. Error checking ignored. Also, we force
* a HTTP/1.0 request to keep things simple.
*/
Explain0("Sending the CONNECT request to the remote proxy");
ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF,
r->uri);
write(sock, buffer, strlen(buffer));
ap_snprintf(buffer, sizeof(buffer),
"Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
write(sock, buffer, strlen(buffer));
}
else {
Explain0("Returning 200 OK Status");
ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL);
ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL);
ap_bflush(r->connection->client);
}
while (1) { /* Infinite loop until error (one side closes the connection) */
FD_ZERO(&fds);
FD_SET(sock, &fds);
FD_SET(r->connection->client->fd, &fds);
Explain0("Going to sleep (select)");
i = ap_select((r->connection->client->fd > sock ?
r->connection->client->fd + 1 :
sock + 1), &fds, NULL, NULL, NULL);
Explain1("Woke from select(), i=%d", i);
if (i) {
if (FD_ISSET(sock, &fds)) {
Explain0("sock was set");
if ((nbytes = read(sock, buffer, HUGE_STRING_LEN)) != 0) {
if (nbytes == -1)
break;
if (write(r->connection->client->fd, buffer, nbytes) == EOF)
break;
Explain1("Wrote %d bytes to client", nbytes);
}
else
break;
}
else if (FD_ISSET(r->connection->client->fd, &fds)) {
Explain0("client->fd was set");
if ((nbytes = read(r->connection->client->fd, buffer,
HUGE_STRING_LEN)) != 0) {
if (nbytes == -1)
break;
if (write(sock, buffer, nbytes) == EOF)
break;
Explain1("Wrote %d bytes to server", nbytes);
}
else
break;
}
else
break; /* Must be done waiting */
}
else
break;
}
ap_pclosesocket(r->pool, sock);
return OK;
}

1284
modules/proxy/proxy_ftp.c Normal file

File diff suppressed because it is too large Load Diff

543
modules/proxy/proxy_http.c Normal file
View File

@ -0,0 +1,543 @@
/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/* HTTP routines for Apache proxy */
#include "mod_proxy.h"
#include "http_log.h"
#include "http_main.h"
#include "http_core.h"
#include "util_date.h"
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
* def_port is the default port for this scheme.
*/
int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port)
{
char *host, *path, *search, sport[7];
const char *err;
int port;
/* do syntatic check.
* We break the URL into host, port, path, search
*/
port = def_port;
err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
if (err)
return HTTP_BAD_REQUEST;
/* now parse path/search args, according to rfc1738 */
/* N.B. if this isn't a true proxy request, then the URL _path_
* has already been decoded. True proxy requests have r->uri
* == r->unparsed_uri, and no others have that property.
*/
if (r->uri == r->unparsed_uri) {
search = strchr(url, '?');
if (search != NULL)
*(search++) = '\0';
}
else
search = r->args;
/* process path */
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
if (path == NULL)
return HTTP_BAD_REQUEST;
if (port != def_port)
ap_snprintf(sport, sizeof(sport), ":%d", port);
else
sport[0] = '\0';
r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
path, (search) ? "?" : "", (search) ? search : "", NULL);
return OK;
}
static const char *proxy_location_reverse_map(request_rec *r, const char *url)
{
void *sconf;
proxy_server_conf *conf;
struct proxy_alias *ent;
int i, l1, l2;
char *u;
sconf = r->server->module_config;
conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
l1 = strlen(url);
ent = (struct proxy_alias *)conf->raliases->elts;
for (i = 0; i < conf->raliases->nelts; i++) {
l2 = strlen(ent[i].real);
if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
u = ap_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
return ap_construct_url(r->pool, u, r);
}
}
return url;
}
/* Clear all connection-based headers from the incoming headers table */
static void clear_connection(pool *p, table *headers)
{
const char *name;
char *next = ap_pstrdup(p, ap_table_get(headers, "Connection"));
ap_table_unset(headers, "Proxy-Connection");
if (!next)
return;
while (*next) {
name = next;
while (*next && !ap_isspace(*next) && (*next != ','))
++next;
while (*next && (ap_isspace(*next) || (*next == ','))) {
*next = '\0';
++next;
}
ap_table_unset(headers, name);
}
ap_table_unset(headers, "Connection");
}
/*
* This handles http:// URLs, and other URLs using a remote proxy over http
* If proxyhost is NULL, then contact the server directly, otherwise
* go via the proxy.
* Note that if a proxy is used, then URLs other than http: can be accessed,
* also, if we have trouble which is clearly specific to the proxy, then
* we return DECLINED so that we can try another proxy. (Or the direct
* route.)
*/
int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
const char *proxyhost, int proxyport)
{
const char *strp;
char *strp2;
const char *err, *desthost;
int i, j, sock, len, backasswards;
array_header *reqhdrs_arr;
table *resp_hdrs;
table_entry *reqhdrs;
struct sockaddr_in server;
struct in_addr destaddr;
struct hostent server_hp;
BUFF *f;
char buffer[HUGE_STRING_LEN];
char portstr[32];
pool *p = r->pool;
const long int zero = 0L;
int destport = 0;
char *destportstr = NULL;
const char *urlptr = NULL;
const char *datestr;
struct tbl_do_args tdo;
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
int nocache = 0;
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
/* We break the URL into host, port, path-search */
urlptr = strstr(url, "://");
if (urlptr == NULL)
return HTTP_BAD_REQUEST;
urlptr += 3;
destport = DEFAULT_HTTP_PORT;
strp = strchr(urlptr, '/');
if (strp == NULL) {
desthost = ap_pstrdup(p, urlptr);
urlptr = "/";
}
else {
char *q = ap_palloc(p, strp - urlptr + 1);
memcpy(q, urlptr, strp - urlptr);
q[strp - urlptr] = '\0';
urlptr = strp;
desthost = q;
}
strp2 = strchr(desthost, ':');
if (strp2 != NULL) {
*(strp2++) = '\0';
if (ap_isdigit(*strp2)) {
destport = atoi(strp2);
destportstr = strp2;
}
}
/* check if ProxyBlock directive on this host */
destaddr.s_addr = ap_inet_addr(desthost);
for (i = 0; i < conf->noproxies->nelts; i++) {
if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL)
|| destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked");
}
if (proxyhost != NULL) {
server.sin_port = htons(proxyport);
err = ap_proxy_host2addr(proxyhost, &server_hp);
if (err != NULL)
return DECLINED; /* try another */
}
else {
server.sin_port = htons(destport);
err = ap_proxy_host2addr(desthost, &server_hp);
if (err != NULL)
return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
}
sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket");
return HTTP_INTERNAL_SERVER_ERROR;
}
if (conf->recv_buffer_size) {
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(const char *) &conf->recv_buffer_size, sizeof(int))
== -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
}
}
#ifdef SINIX_D_RESOLVER_BUG
{
struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
for (; ip_addr->s_addr != 0; ++ip_addr) {
memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
}
}
#else
j = 0;
while (server_hp.h_addr_list[j] != NULL) {
memcpy(&server.sin_addr, server_hp.h_addr_list[j],
sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
j++;
}
#endif
if (i == -1) {
if (proxyhost != NULL)
return DECLINED; /* try again another way */
else
return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
"Could not connect to remote machine: ",
strerror(errno), NULL));
}
clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */
f = ap_bcreate(p, B_RDWR | B_SOCKET);
ap_bpushfd(f, sock, sock);
ap_hard_timeout("proxy send", r);
ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF,
NULL);
if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
else
ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
if (conf->viaopt == via_block) {
/* Block all outgoing Via: headers */
ap_table_unset(r->headers_in, "Via");
} else if (conf->viaopt != via_off) {
/* Create a "Via:" request header entry and merge it */
i = ap_get_server_port(r);
if (ap_is_default_port(i,r)) {
strcpy(portstr,"");
} else {
ap_snprintf(portstr, sizeof portstr, ":%d", i);
}
/* Generate outgoing Via: header with/without server comment: */
ap_table_mergen(r->headers_in, "Via",
(conf->viaopt == via_full)
? ap_psprintf(p, "%d.%d %s%s (%s)",
HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num),
ap_get_server_name(r), portstr,
SERVER_BASEVERSION)
: ap_psprintf(p, "%d.%d %s%s",
HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num),
ap_get_server_name(r), portstr)
);
}
reqhdrs_arr = ap_table_elts(r->headers_in);
reqhdrs = (table_entry *) reqhdrs_arr->elts;
for (i = 0; i < reqhdrs_arr->nelts; i++) {
if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL
/* Clear out headers not to send */
|| !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */
/* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be
* suppressed if THIS server requested the authentication,
* not when a frontend proxy requested it!
*/
|| !strcasecmp(reqhdrs[i].key, "Proxy-Authorization"))
continue;
ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
}
ap_bputs(CRLF, f);
/* send the request data, if any. */
if (ap_should_client_block(r)) {
while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
ap_bwrite(f, buffer, i);
}
ap_bflush(f);
ap_kill_timeout(r);
ap_hard_timeout("proxy receive", r);
len = ap_bgets(buffer, sizeof buffer - 1, f);
if (len == -1) {
ap_bclose(f);
ap_kill_timeout(r);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"ap_bgets() - proxy receive - Error reading from remote server %s (length %d)",
proxyhost ? proxyhost : desthost, len);
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
} else if (len == 0) {
ap_bclose(f);
ap_kill_timeout(r);
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Document contains no data");
}
/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */
if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
int major, minor;
if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
major = 1;
minor = 0;
}
/* If not an HTTP/1 message or if the status line was > 8192 bytes */
if (buffer[5] != '1' || buffer[len - 1] != '\n') {
ap_bclose(f);
ap_kill_timeout(r);
return HTTP_BAD_GATEWAY;
}
backasswards = 0;
buffer[--len] = '\0';
buffer[12] = '\0';
r->status = atoi(&buffer[9]);
buffer[12] = ' ';
r->status_line = ap_pstrdup(p, &buffer[9]);
/* read the headers. */
/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
/* Also, take care with headers with multiple occurences. */
resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
if (resp_hdrs == NULL) {
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
"proxy: Bad HTTP/%d.%d header returned by %s (%s)",
major, minor, r->uri, r->method);
resp_hdrs = ap_make_table(p, 20);
nocache = 1; /* do not cache this broken file */
}
if (conf->viaopt != via_off && conf->viaopt != via_block) {
/* Create a "Via:" response header entry and merge it */
i = ap_get_server_port(r);
if (ap_is_default_port(i,r)) {
strcpy(portstr,"");
} else {
ap_snprintf(portstr, sizeof portstr, ":%d", i);
}
ap_table_mergen((table *)resp_hdrs, "Via",
(conf->viaopt == via_full)
? ap_psprintf(p, "%d.%d %s%s (%s)",
major, minor,
ap_get_server_name(r), portstr,
SERVER_BASEVERSION)
: ap_psprintf(p, "%d.%d %s%s",
major, minor,
ap_get_server_name(r), portstr)
);
}
clear_connection(p, resp_hdrs); /* Strip Connection hdrs */
}
else {
/* an http/0.9 response */
backasswards = 1;
r->status = 200;
r->status_line = "200 OK";
/* no headers */
resp_hdrs = ap_make_table(p, 20);
}
c->hdrs = resp_hdrs;
ap_kill_timeout(r);
/*
* HTTP/1.0 requires us to accept 3 types of dates, but only generate
* one type
*/
if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL)
ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL)
ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL)
ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL)
ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr));
if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL)
ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr));
/* check if NoCache directive on this host */
for (i = 0; i < conf->nocaches->nelts; i++) {
if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL)
|| destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*')
nocache = 1;
}
i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
if (i != DECLINED) {
ap_bclose(f);
return i;
}
ap_hard_timeout("proxy receive", r);
/* write status line */
if (!r->assbackwards)
ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
if (c != NULL && c->fp != NULL &&
ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error writing status line to %s", c->tempfile);
c = ap_proxy_cache_error(c);
}
/* send headers */
tdo.req = r;
tdo.cache = c;
ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
if (!r->assbackwards)
ap_rputs(CRLF, r);
if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error writing CRLF to %s", c->tempfile);
c = ap_proxy_cache_error(c);
}
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1;
/* Is it an HTTP/0.9 respose? If so, send the extra data */
if (backasswards) {
ap_bwrite(r->connection->client, buffer, len);
if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error writing extra data to %s", c->tempfile);
c = ap_proxy_cache_error(c);
}
}
ap_kill_timeout(r);
#ifdef CHARSET_EBCDIC
/* What we read/write after the header should not be modified
* (i.e., the cache copy is ASCII, not EBCDIC, even for text/html)
*/
ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
#endif
/* send body */
/* if header only, then cache will be NULL */
/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
if (!r->header_only) {
/* we need to set this for ap_proxy_send_fb()... */
c->cache_completion = conf->cache.cache_completion;
ap_proxy_send_fb(f, r, c);
}
ap_proxy_cache_tidy(c);
ap_bclose(f);
ap_proxy_garbage_coll(r);
return OK;
}

1288
modules/proxy/proxy_util.c Normal file

File diff suppressed because it is too large Load Diff

3
modules/test/.cvsignore Normal file
View File

@ -0,0 +1,3 @@
Makefile
*.lo
*.so

54
modules/test/.indent.pro vendored Normal file
View File

@ -0,0 +1,54 @@
-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
-TBUFF
-TFILE
-TTRANS
-TUINT4
-T_trans
-Tallow_options_t
-Tapache_sfio
-Tarray_header
-Tbool_int
-Tbuf_area
-Tbuff_struct
-Tbuffy
-Tcmd_how
-Tcmd_parms
-Tcommand_rec
-Tcommand_struct
-Tconn_rec
-Tcore_dir_config
-Tcore_server_config
-Tdir_maker_func
-Tevent
-Tglobals_s
-Thandler_func
-Thandler_rec
-Tjoblist_s
-Tlisten_rec
-Tmerger_func
-Tmode_t
-Tmodule
-Tmodule_struct
-Tmutex
-Tn_long
-Tother_child_rec
-Toverrides_t
-Tparent_score
-Tpid_t
-Tpiped_log
-Tpool
-Trequest_rec
-Trequire_line
-Trlim_t
-Tscoreboard
-Tsemaphore
-Tserver_addr_rec
-Tserver_rec
-Tserver_rec_chain
-Tshort_score
-Ttable
-Ttable_entry
-Tthread
-Tu_wide_int
-Tvtime_t
-Twide_int

3
modules/test/README Normal file
View File

@ -0,0 +1,3 @@
This directory is intended to house modules which are used for testing
server functionality. They're unsupported, and not guaranteed to remain
consistant between releases. You're on your own completely with these.

179
modules/test/mod_rndchunk.c Normal file
View File

@ -0,0 +1,179 @@
/* ====================================================================
* Copyright (c) 1998-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* This module is intended to be used for testing chunked encoding. It
* generates a whole whack of output using ap_bputc() and ap_bputs(). It
* also exercises start_chunk() and end_chunk() in buff.c. To use it
* you should use a tool like netcat and the src/test/check_chunked
* tool. Add something like this to your access.conf file:
*
* <Location /rndchunk>
* SetHandler rndchunk
* </Location>
*
* Then fake requests such as:
*
* GET /rndchunk?0,1000000 HTTP/1.1
* Host: localhost
*
* The first arg is the random seed, the second is the number of
* "things" to do. You should try a few seeds.
*
* You should also edit main/buff.c and change DEFAULT_BUFSIZE (and
* CHUNK_HEADER_SIZE). Small values are particularly useful for
* finding bugs. Try a few different values.
*
* -djg
*/
#include "httpd.h"
#include "http_protocol.h"
#include "http_config.h"
#include "http_main.h"
#define MAX_SEGMENT 32
#define ONE_WEIGHT (256-32)
static int send_rndchunk(request_rec *r)
{
const char *args;
char *endptr;
unsigned int seed;
unsigned int count;
int i;
char buf[MAX_SEGMENT + 1];
unsigned int len;
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;
}
ap_hard_timeout("send_rndchunk", r);
if (!r->chunked) {
ap_rputs("Not chunked!", r);
ap_kill_timeout(r);
return 0;
}
args = r->args;
if (!args) {
error:
ap_rputs("Must include args! ... of the form <code>?seed,count</code>", r);
ap_kill_timeout(r);
return 0;
}
seed = strtol(args, &endptr, 0);
if (!endptr || *endptr != ',') {
goto error;
}
++endptr;
count = strtol(endptr, &endptr, 0);
srandom(seed);
for (i = 0; i < count; ++i) {
len = random() % (MAX_SEGMENT + ONE_WEIGHT);
if (len >= MAX_SEGMENT) {
ap_rputc((i & 1) ? '0' : '1', r);
}
else if (len == 0) {
/* not a really nice thing to do, but we need to test
* beginning/ending chunks as well
*/
ap_bsetflag(r->connection->client, B_CHUNK, 0);
ap_bsetflag(r->connection->client, B_CHUNK, 1);
}
else {
memset(buf, '2' + len, len);
buf[len] = 0;
ap_rputs(buf, r);
}
}
ap_kill_timeout(r);
return 0;
}
static const handler_rec rndchunk_handlers[] =
{
{"rndchunk", send_rndchunk},
{NULL}
};
module rndchunk_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
NULL, /* command table */
rndchunk_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL /* header parser */
};

View File

@ -0,0 +1,354 @@
/* ====================================================================
* Copyright (c) 1998-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* This module is intended to test the util_uri routines by parsing a
* bunch of urls and comparing the results with what we expect to
* see.
*
* Usage:
*
* <Location /test-util-uri>
* SetHandler test-util-uri
* </Location>
*
* Then make a request to /test-util-uri. An html table of errors will
* be output... and a total count of errors.
*/
#include "httpd.h"
#include "http_protocol.h"
#include "http_config.h"
#include "http_main.h"
typedef struct {
const char *scheme;
const char *user;
const char *password;
const char *hostname;
const char *port_str;
const char *path;
const char *query;
const char *fragment;
} test_uri_t;
#define T_scheme 0x01
#define T_user 0x02
#define T_password 0x04
#define T_hostname 0x08
#define T_port_str 0x10
#define T_path 0x20
#define T_query 0x40
#define T_fragment 0x80
#define T_MAX 0x100
/* The idea is that we list here a bunch of url pieces that we want
* stitched together in every way that's valid.
*/
static const test_uri_t uri_tests[] = {
{ "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" },
{ "http", "", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" },
{ "http", "userid", "", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" },
{ "http", "userid", "passwd", "", "80", "/path/goes/here", "query-here", "frag-here" },
{ "http", "userid", "passwd", "hostname.goes.here", "", "/path/goes/here", "query-here", "frag-here" },
#if 0
/* An empty path means two different things depending on whether this is a
* relative or an absolute uri... consider <a href="#frag"> versus "GET
* http://hostname HTTP/1.1". So this is why parse_uri_components returns
* a NULL for path when it doesn't find one, instead of returning an empty
* string.
*
* We don't really need to test it explicitly since path has no explicit
* character that indicates its precense, and so we test empty paths all
* the time by varying T_path in the loop. It would just cost us extra
* code to special case the empty path string...
*/
{ "http", "userid", "passwd", "hostname.goes.here", "80", "", "query-here", "frag-here" },
#endif
{ "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "", "frag-here" },
{ "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "" },
{ "https", "user@d", "pa:swd", "hostname.goes.here.", "", "/~path/goes/here", "query&query?crud", "frag-here?baby" }
};
static char *my_stpcpy(char *d, const char *s)
{
while((*d = *s)) {
++d;
++s;
}
return d;
}
/* return the number of failures */
static unsigned iterate_pieces(request_rec *r, const test_uri_t *pieces, int row)
{
unsigned u;
pool *sub;
char *input_uri;
char *strp;
uri_components result;
unsigned expect;
int status;
unsigned failures;
failures = 0;
input_uri = ap_palloc(r->pool,
strlen(pieces->scheme) + 3
+ strlen(pieces->user) + 1
+ strlen(pieces->password) + 1
+ strlen(pieces->hostname) + 1
+ strlen(pieces->port_str) + 1
+ strlen(pieces->path) +
+ strlen(pieces->query) + 1
+ strlen(pieces->fragment) + 1
+ 1);
for (u = 0; u < T_MAX; ++u) {
strp = input_uri;
expect = 0;
/* a scheme requires a hostinfo and vice versa */
/* a hostinfo requires a hostname */
if (u & (T_scheme|T_user|T_password|T_hostname|T_port_str)) {
expect |= T_scheme;
strp = my_stpcpy(strp, pieces->scheme);
*strp++ = ':';
*strp++ = '/';
*strp++ = '/';
/* can't have password without user */
if (u & (T_user|T_password)) {
expect |= T_user;
strp = my_stpcpy(strp, pieces->user);
if (u & T_password) {
expect |= T_password;
*strp++ = ':';
strp = my_stpcpy(strp, pieces->password);
}
*strp++ = '@';
}
expect |= T_hostname;
strp = my_stpcpy(strp, pieces->hostname);
if (u & T_port_str) {
expect |= T_port_str;
*strp++ = ':';
strp = my_stpcpy(strp, pieces->port_str);
}
}
if (u & T_path) {
expect |= T_path;
strp = my_stpcpy(strp, pieces->path);
}
if (u & T_query) {
expect |= T_query;
*strp++ = '?';
strp = my_stpcpy(strp, pieces->query);
}
if (u & T_fragment) {
expect |= T_fragment;
*strp++ = '#';
strp = my_stpcpy(strp, pieces->fragment);
}
*strp = 0;
sub = ap_make_sub_pool(r->pool);
status = ap_parse_uri_components(sub, input_uri, &result);
if (status == HTTP_OK) {
#define CHECK(f) \
if ((expect & T_##f) \
&& (result.f == NULL || strcmp(result.f, pieces->f))) { \
status = HTTP_INTERNAL_SERVER_ERROR; \
} \
else if (!(expect & T_##f) && result.f != NULL) { \
status = HTTP_INTERNAL_SERVER_ERROR; \
}
CHECK(scheme)
CHECK(user)
CHECK(password)
CHECK(hostname)
CHECK(port_str)
CHECK(path)
CHECK(query)
CHECK(fragment)
#undef CHECK
}
if (status != HTTP_OK) {
ap_rprintf(r, "<tr><td>%d</td><td>0x%02x</td><td>0x%02x</td><td>%d</td><td>\"%s\"</td>", row, u, expect, status, input_uri);
#define DUMP(f) \
if (result.f) { \
ap_rvputs(r, "<td>\"", result.f, "\"<br>", NULL); \
} \
else { \
ap_rputs("<td>NULL<br>", r); \
} \
if (expect & T_##f) { \
ap_rvputs(r, "\"", pieces->f, "\"</td>", NULL); \
} \
else { \
ap_rputs("NULL</td>", r); \
}
DUMP(scheme);
DUMP(user);
DUMP(password);
DUMP(hostname);
DUMP(port_str);
DUMP(path);
DUMP(query);
DUMP(fragment);
#undef DUMP
ap_rputs("</tr>\n", r);
++failures;
}
ap_destroy_pool(sub);
}
return failures;
}
static int test_util_uri(request_rec *r)
{
unsigned total_failures;
int i;
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;
}
ap_hard_timeout("test_util_uri", r);
ap_rputs(
DOCTYPE_HTML_2_0 "
<html><body>
<p>Key:
<dl>
<dt>row
<dd>entry number in the uri_tests array
<dt>u
<dd>fields under test
<dt>expected
<dd>fields expected in the result
<dt>status
<dd>response from parse_uri_components, or 500 if unexpected results
<dt>input uri
<dd>the uri given to parse_uri_components
</dl>
<p>The remaining fields are the pieces returned from parse_uri_components, and
the values we expected for each piece (resp.).
<p>Only failures are displayed.
<p>
<table><tr><th>row</th><th>u</th><th>expect</th><th>status</th><th>input uri</th>", r);
#define HEADER(f) ap_rprintf(r, "<th>" #f "<br>0x%02x</th>", T_##f)
HEADER(scheme);
HEADER(user);
HEADER(password);
HEADER(hostname);
HEADER(port_str);
HEADER(path);
HEADER(query);
HEADER(fragment);
#undef HEADER
if (r->args) {
i = atoi(r->args);
total_failures = iterate_pieces(r, &uri_tests[i], i);
}
else {
total_failures = 0;
for (i = 0; i < sizeof(uri_tests) / sizeof(uri_tests[0]); ++i) {
total_failures += iterate_pieces(r, &uri_tests[i], i);
if (total_failures > 256) {
ap_rprintf(r, "</table>\n<b>Stopped early to save your browser "
"from certain death!</b>\nTOTAL FAILURES = %u\n",
total_failures);
return OK;
}
}
}
ap_rprintf(r, "</table>\nTOTAL FAILURES = %u\n", total_failures);
return OK;
}
static const handler_rec test_util_uri_handlers[] =
{
{"test-util-uri", test_util_uri},
{NULL}
};
module test_util_uri_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
NULL, /* command table */
test_util_uri_handlers, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL /* header parser */
};