/* ==================================================================== * 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 */ };