/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*
* (c) 2002 Theo Zourzouvillys
* See http://linux-whore.org/ for more info.
*
*/
/* We need this to be able to access the docroot. */
#define CORE_PRIVATE
#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
#include
#include
#include
#include
typedef struct shapvh_cfg
{
MYSQL * mysql;
char * mysql_host;
char * mysql_user;
char * mysql_pass;
char * mysql_dbname;
char * mysql_defaultroot;
char * mysql_defaultadmin;
char * mysql_query;
int shapvh_enabled;
int mysqlok;
int shapvh_visp;
} shapvh_cfg;
module MODULE_VAR_EXPORT shapvh_module;
static int shapvh_setup(server_rec * s)
{
shapvh_cfg * cfg = ap_get_module_config(s->module_config, &shapvh_module);
if (cfg->shapvh_enabled == 0)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "ShapVH Disbaled for '%s', but tried to connect to MySQL.", s->server_hostname);
return;
}
/* This should never ever ever happen */
if (cfg->mysql == NULL)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "MySQL handle is NULL!");
return -1;
}
cfg->mysqlok = 0;
if (!mysql_real_connect(cfg->mysql, cfg->mysql_host, cfg->mysql_user, cfg->mysql_pass, cfg->mysql_dbname, 0, NULL, 0))
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "Failed to connect to database: Error: %s\n", mysql_error(cfg->mysql));
cfg->mysqlok = 0;
return -1;
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, "ShapVH Connected to MySQL");
cfg->mysqlok = 1;
return 1;
}
static void * shapvh_create_server_config(pool *p, server_rec *s)
{
shapvh_cfg * cfg = (shapvh_cfg *) ap_pcalloc(p, sizeof(shapvh_cfg));
cfg->mysql = NULL;
cfg->mysql = ap_palloc(p, sizeof(MYSQL));
mysql_init(cfg->mysql);
return (void *) cfg;
}
static void * shapvh_merge_server_config (pool *p, void *basev, void *overridesv)
{
shapvh_cfg * new = (shapvh_cfg *) ap_pcalloc(p, sizeof(shapvh_cfg));
shapvh_cfg * base = (shapvh_cfg *) basev;
shapvh_cfg * overrides = (shapvh_cfg *) overridesv;
new->mysql = ap_palloc(p, sizeof(MYSQL));
mysql_init(new->mysql);
new->mysqlok = 0;
new->mysql_host = base->mysql_host;
new->mysql_user = base->mysql_user;
new->mysql_pass = base->mysql_pass;
new->mysql_dbname = base->mysql_dbname;
new->mysql_defaultroot = base->mysql_defaultroot;
new->mysql_defaultadmin = base->mysql_defaultadmin;
new->mysql_query = (overrides->mysql_query == NULL) ? base->mysql_query : overrides->mysql_query;
new->shapvh_enabled = (overrides->shapvh_enabled == 1) ? 1 : 0;
new->shapvh_visp = (overrides->shapvh_visp == 1) ? 1 : 0;
return new;
}
static int shapvh_translate(request_rec *r)
{
char * query, buff[HUGE_STRING_LEN], * ptr, * hptr = (char *)r->hostname;
shapvh_cfg * cfg = ap_get_module_config(r->server->module_config, &shapvh_module);
core_server_config * conf = (core_server_config *) ap_get_module_config(r->server->module_config, &core_module);
if (cfg->shapvh_enabled == 0)
return DECLINED;
else if (cfg->mysqlok == 0)
shapvh_setup(r->server);
ap_table_setn(r->subprocess_env, "SHAPVH_HOST", r->hostname);
/* Hostnames *CANT* have \'s or "'"'s in, according to RFCs. */
if (strstr(r->hostname, "'") != NULL || strstr(r->hostname, "\\") != NULL)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Invalid charecter found in hostname: %s", r->hostname);
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_uid = 1016;
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "INVALID_CHAR");
return DECLINED;
}
/* Tut tut - bad bad bad. */
if (cfg->mysql_query == NULL)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "ShapVH: MySQL query is NULL!");
return DECLINED;
}
ptr = buff;
/* If it's a virtual ISP (www.username.visp.domain) then we should extract the username and VISP */
/* it shouldn't really be hardcoded to 'www.' as the prefix, maybe i'll change it. */
if (cfg->shapvh_visp == 1)
{
/* Fast forward to just after the "www." */
while (*hptr && (*hptr == 'w' || *hptr == '.'))
{
if (*hptr == '.')
{
*hptr++;
break;
}
*hptr++;
}
/* Tut tut, bad client */
if (!*hptr)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Unknown VISP hostname: %s", r->hostname);
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_uid = 1016;
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "VISP_HOSTNAME_INVALID");
return DECLINED;
}
/* Collect the username */
while (*hptr && (isalpha(*hptr) || isdigit(*hptr)))
*ptr++ = *hptr++;
/* jump past the dot after the username */
if (*hptr) {
*hptr++;
} else {
/* moooo, bad. */
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Unknown VISP hostname: %s", r->hostname);
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_uid = 1016;
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "VISP_HOSTNAME_INVALID");
return DECLINED;
}
/* Add a terminating NULL */
*ptr = '\0';
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, "User: %s, VISP: %s", buff, hptr);
/* Create our query */
query = ap_psprintf(r->pool, cfg->mysql_query, buff, hptr);
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, "MySQL Query: %s", query);
} else {
/* otherwise, it's a plain vhost lookup. */
query = ap_psprintf(r->pool, cfg->mysql_query, r->hostname);
}
if (mysql_real_query(cfg->mysql, query, strlen(query)))
{
/* Eeek! query failed! */
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Error in query '%s' %s", query, mysql_error(cfg->mysql));
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_uid = 1016;
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "QUERY_ERROR");
return DECLINED;
} else {
/* YaY! we have data! */
MYSQL_RES * res_set;
MYSQL_ROW row;
res_set = mysql_store_result(cfg->mysql);
if ((row = mysql_fetch_row(res_set)))
{
conf->ap_document_root = ap_pstrdup(r->pool, row[0]);
r->server->server_uid = atoi(row[1]);
r->server->server_gid = atoi(row[3]);
/* r->server->server_hostname = r->hostname; // Can't do this - where do we store the memory!? */
r->server->server_admin = ap_pstrdup(r->pool, row[2]);
/* things could always go wrong. lets make sure it doesn't cause any damage if they do! */
if (r->server->server_uid < 1000 || r->server->server_gid < 1000)
{
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Invalid UID or GID Found: %d/%d, aborting.", r->server->server_uid, r->server->server_gid);
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
r->server->server_uid = 1016;
r->server->server_gid = 1001;
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "INVALID_UIDGID");
mysql_free_result(res_set);
return DECLINED;
}
} else {
conf->ap_document_root = ap_pstrdup(r->pool, cfg->mysql_defaultroot);
r->server->server_uid = 1016;
r->server->server_admin = ap_pstrdup(r->pool, cfg->mysql_defaultadmin);
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, "Host '%s' not found", r->hostname);
ap_table_setn(r->subprocess_env, "SHAPVH_ERR", "HOST_NOT_FOUND");
mysql_free_result(res_set);
return DECLINED;
}
}
/* Now, this is kinda norty - but it will allow over filename translation handlers to do their stuff, with the right docroot. */
return DECLINED;
}
/* Config stuff. */
static const char *set_host(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_host = arg;
return NULL;
}
static const char *set_user(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_user = arg;
return NULL;
}
static const char *set_pass(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_pass = arg;
return NULL;
}
static const char *set_dbname(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_dbname = arg;
return NULL;
}
static const char *set_defaultroot(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_defaultroot = arg;
return NULL;
}
static const char *set_defaultadmin(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_defaultadmin = arg;
return NULL;
}
static const char *set_shquery(cmd_parms *cmd, void *dummy, char *arg)
{
shapvh_cfg * cfg = ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->mysql_query = arg;
return NULL;
}
static const char *set_onoff(cmd_parms *cmd, void * mconfig, int flag)
{
shapvh_cfg * cfg = (shapvh_cfg *) ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->shapvh_enabled = (flag ? 1 : 0);
return NULL;
}
static const char *set_shvisp(cmd_parms *cmd, void * mconfig, int flag)
{
shapvh_cfg * cfg = (shapvh_cfg *) ap_get_module_config(cmd->server->module_config, &shapvh_module);
cfg->shapvh_visp = (flag ? 1 : 0);
return NULL;
}
static const command_rec shapvh_cmds[] = {
{ "ShapVHOn", set_onoff, NULL, RSRC_CONF, FLAG, "Turn on Shared Apache Virtualhost on this Server" },
{ "ShapVhHost", set_host, NULL, RSRC_CONF, TAKE1, "Set Hostname to connect to database"},
{ "ShapVhUser", set_user, NULL, RSRC_CONF, TAKE1, "Set Username to connect to database"},
{ "ShapVhPass", set_pass, NULL, RSRC_CONF, TAKE1, "Set Password to connect to database with"},
{ "ShapVhDbName", set_dbname, NULL, RSRC_CONF, TAKE1, "Set Database To use"},
{ "ShapVhDefaultRoot", set_defaultroot, NULL, RSRC_CONF, TAKE1, "Default root when vhost isn't foud"},
{ "ShapVhDefaultAdmin", set_defaultadmin, NULL, RSRC_CONF, TAKE1, "Default admin address when vhost isn't found."},
{ "ShapVhQuery", set_shquery, NULL, RSRC_CONF, TAKE1, "The Select query. Returns DocRoot, UID, and AdminAddress."},
{ "ShapVhVISP", set_shvisp, NULL, RSRC_CONF, FLAG, "If this virtualhost is a VISP."},
NULL,
};
module MODULE_VAR_EXPORT shapvh_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
shapvh_create_server_config,/* server config */
shapvh_merge_server_config, /* merge server configs */
shapvh_cmds, /* command table */
NULL, /* handlers */
shapvh_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 */
};