/* * $Id: mod_sqlinclude.c,v 1.15 2002/02/13 23:19:59 carlos Exp $ * * (C) 2001-2002 by Marcin Orlowski * * Homepage: http://webnet.pl/~carlos/ * * This module implements Include-like command, but * instead of including content of specified files, * it gets the data from the MySQL database, based * upon user specified query * * Source code written using VIM with tabstop = 3 * */ #define MYVERSION "1.4" #define MYNAME "SQLInclude" #define MYURL "http://freshmeat.net/projects/mod_sqlinclude/" //#define MODULE_DEBUG #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" #include /* TT: pulled from mod_rewrite.h */ #ifndef FALSE #define FALSE 0 #define TRUE !FALSE #endif module MODULE_VAR_EXPORT sqlinclude_module; #define VERBOSE_LEVEL_QUIET 0 #define VERBOSE_LEVEL_BASIC 1 #define VERBOSE_LEVEL_TALK 2 #define VERBOSE_LEVEL_DETAILS 3 #define VERBOSE_LEVEL_DEBUG 4 #define VERBOSE_LEVEL_HARDCORE 5 // SQL_VHost module config structure #define SQL_SERVER_LEN 256 #define SQL_USER_LEN 80 #define SQL_PASSWORD_LEN 80 #define SQL_DB_LEN 80 typedef struct { char sql_server[ SQL_SERVER_LEN ]; int sql_port; char sql_user[ SQL_USER_LEN ]; char sql_password[ SQL_PASSWORD_LEN ]; char sql_db[ SQL_DB_LEN ]; int UseNameColumn; int AbortOnError; int Verbose; char *SocketFile; MYSQL real_mysql; } sqli_server_conf; // void LogModuleSignature( server_rec *s ) { ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: v%s by Marcin Orlowski ", MYNAME, MYVERSION ); ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Project's home: %s", MYNAME, MYURL ); } // getstr function to 'read' from returned row instead of real file // parm is useless for us int GetLine( char *dest, size_t dest_size, char **src_ptr ) { int i; char *src = *src_ptr; // let's 'read' the src data till closest LF or \0 for( i=0; imodule_config, &sqlinclude_module ); if( conf->UseNameColumn ) snprintf( row_name, INCLUDE_NAME_LEN, "SQLI: %s", row[1] ); else snprintf( row_name, INCLUDE_NAME_LEN, "SQLI: #%ld", row_number ); if( conf->Verbose >= VERBOSE_LEVEL_DEBUG ) { ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Processing config row #%ld: '%s'", MYNAME, row_number, row_name ); } // it'd be best to call ap_pcfg_openfile() but it can't live w/o // the real file given. Maybe one day I do the trick and fool // it with something ;) Ripped from mentioned function (1.3.19): new_cfg = ap_palloc(p, sizeof(*new_cfg)); new_cfg->getstr = (void *)GetLine; // our getstring function new_cfg->param = &record_ptr; // data we gonna parse new_cfg->line_number = 0; parms.pool = p; parms.temp_pool = ptemp; parms.server = s; parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); // probably useless here new_cfg->name = row_name; parms.config_file = new_cfg; // this uses Apache core config file parser. All we have to do is to feed it // with the data, which is done using GetLine() errmsg = ap_srm_command_loop( &parms, s->lookup_defaults ); if( errmsg ) { ap_log_error( APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, s, "%s: Syntax error in row #%d, '%s', line %d", MYNAME, row_number, row_name, parms.config_file->line_number ); ap_log_error( APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, s, "%s: %s\n", MYNAME, errmsg ); // exit(1); return(0); } return(1); } /* * Fetches VHosts from the table */ static const char *cmd_sqli_include( cmd_parms *cmd, void *dummy, char *arg ) { MYSQL *mysql = NULL; MYSQL_RES *result; MYSQL_ROW row; char buf[ 1024 ]; int err, num, len; int i; // let's find our config data server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config( s->module_config, &sqlinclude_module ); // Let's sign off ;) LogModuleSignature( s ); if( conf->Verbose >= VERBOSE_LEVEL_BASIC ) { ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Attempting to SQL Include...", MYNAME ); ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Config specifies User@Server:Port = %s@%s:%ld", MYNAME, conf->sql_user, conf->sql_server, conf->sql_port ); } // connecting to DB for( i=0; i<=2; i++ ) // Try it a few times { mysql_init( &conf->real_mysql ); mysql = mysql_real_connect( &conf->real_mysql, conf->sql_server, conf->sql_user, conf->sql_password, conf->sql_db, conf->sql_port, conf->SocketFile, 0 ); if( mysql ) { if( conf->Verbose >= VERBOSE_LEVEL_BASIC ) { ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Connection to '%s' established...", MYNAME, conf->sql_server ); } break; } else { ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, s, "%s: Connection to server %s timeouted. Retrying %ld time...", MYNAME, conf->sql_server, (i+1) ); sleep(3); } } // no connection no fun... if( !mysql ) { ap_log_error( APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, s, "%s: Can't connect to '%s' server as user '%s'", MYNAME, conf->sql_server, conf->sql_user ); return( 0 ); } // time to fetch some data using supplied user query if( (mysql_query( mysql, arg ) < 0) || ( !(result = mysql_store_result( mysql )) ) ) { ap_log_error( APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, s, "%s: Querying data from SQL base: FAILED...", MYNAME ); return( 0 ); } num = mysql_num_rows(result); if( conf->Verbose >= VERBOSE_LEVEL_BASIC ) ap_log_error( APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: SQL query successed: %ld rows fetched", MYNAME, num ); // let's loop and process all the rows i = 0; while( row = mysql_fetch_row(result) ) { if( row[0] == NULL ) { num = 0; goto error_exit; } else { if( ( ProcessEntry( cmd->server, cmd->pool, cmd->temp_pool, row, i ) == 0 ) && ( conf->AbortOnError == TRUE ) ) { goto error_exit; // no further processing till this error is gone } } i++; } error_exit: mysql_free_result( result ); if( mysql != NULL ) { ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s: Done.", MYNAME ); mysql_close( mysql ); } return( NULL ); } // sets sql server name static const char *cmd_sqli_servername( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); strncpy( conf->sql_server, arg, SQL_SERVER_LEN ); return(NULL); } // sets sql server port static const char *cmd_sqli_serverport( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); conf->sql_port = atoi( arg ); return(NULL); } // sets sql user name static const char *cmd_sqli_sqluser( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); strncpy( conf->sql_user, arg, SQL_USER_LEN ); return(NULL); } // sets sql user's password static const char *cmd_sqli_sqlpassword( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); strncpy( conf->sql_password, arg, SQL_PASSWORD_LEN ); return(NULL); } // sets sql database name static const char *cmd_sqli_sqldb( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); strncpy( conf->sql_db, arg, SQL_DB_LEN ); // conf->sql_db = conf->sql_db_name; return(NULL); } // sets AbortOnError switch static const char *cmd_sqli_abortonerror( cmd_parms *cmd, void *dummy, int arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); conf->AbortOnError = arg; return(NULL); } // sets UseNameColumn switch static const char *cmd_sql_usenamecolumn( cmd_parms *cmd, void *dummy, int arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); conf->UseNameColumn = arg; return(NULL); } // sets Verbose switch static const char *cmd_sql_verbose( cmd_parms *cmd, void *dummy, char *arg ) { server_rec *s = cmd->server; sqli_server_conf *conf = (sqli_server_conf *) ap_get_module_config(s->module_config, &sqlinclude_module); conf->Verbose = atoi( arg ); if( conf->Verbose < VERBOSE_LEVEL_QUIET ) conf->Verbose = VERBOSE_LEVEL_QUIET; else if( conf->Verbose > VERBOSE_LEVEL_HARDCORE ) conf->Verbose = VERBOSE_LEVEL_HARDCORE; return(NULL); } /* ** Let's allocate module config struct and fill with defaults */ static void *create_sqli_config( pool *p, server_rec *s ) { sqli_server_conf *c = (sqli_server_conf *) ap_pcalloc( p, sizeof( sqli_server_conf ) ); if( c ) { // setting up defaults... strcpy( c->sql_server , "localhost" ); c->sql_port = 0; strcpy( c->sql_user , "sqlinclude" ); strcpy( c->sql_password, "" ); strcpy( c->sql_db , "" ); c->SocketFile = NULL; c->UseNameColumn = TRUE; c->AbortOnError = FALSE; c->Verbose = VERBOSE_LEVEL_BASIC; } return c; } /* ** Hare are the config directives our module implements */ static const command_rec sqli_cmds[] = { { "SQL_ServerName", cmd_sqli_servername, NULL, RSRC_CONF, TAKE1, "the MySQL server, the module shall connect to. Default is 'localhost'" }, { "SQL_ServerPort", cmd_sqli_serverport, NULL, RSRC_CONF, TAKE1, "the MySQL server port, the module shall connect to. If none, default port is used" }, { "SQL_SQLUser", cmd_sqli_sqluser, NULL, RSRC_CONF, TAKE1, "the MySQL user modules shall authenticate as. Default 'sqlinclude'" }, { "SQL_SQLPassword", cmd_sqli_sqlpassword, NULL, RSRC_CONF, TAKE1, "the MySQL password for SQLI_User. No default" }, { "SQL_SQLDB", cmd_sqli_sqldb, NULL, RSRC_CONF, TAKE1, "the MySQL database the module shall use. Default 'sqlinclude'" }, { "SQL_Include", cmd_sqli_include, NULL, RSRC_CONF, TAKE1, "valid SQL query. Data returned in 1st column will be passed to Apache" }, { "SQL_AbortOnError", cmd_sqli_abortonerror, NULL, RSRC_CONF, FLAG, "if 'On' row process will be aborted on syntax error. Default 'Off'" }, { "SQL_UseNameColumn", cmd_sql_usenamecolumn, NULL, RSRC_CONF, FLAG, "On/Off - specifies if you want to use 2nd column as data description" }, { "SQL_Verbose", cmd_sql_verbose, NULL, RSRC_CONF, TAKE1, "0,1,2,3,4,5 - level of module talkative. 0-nothing but errors, 5-speak up baby. Default is 1" }, { NULL } }; module MODULE_VAR_EXPORT sqlinclude_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ NULL, /* dir config creater */ NULL, /* dir merger --- default is to override */ create_sqli_config, /* server config */ NULL, /* merge server config */ sqli_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, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* post read-request */ };