/* Copyright (c) 1995-1998 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/>.
 *
 */

/* 
 * Apache pl/sql module.  Provide access to Oracle Database in a way compatible with OWS.
 *
 */

#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 "mod_plsql.h"

#define MAXENTLEN (6)

#ifdef CHARSET_EBCDIC
#define RAW_ASCII_CHAR(ch)  os_toebcdic[(unsigned char)ch]
#else /*CHARSET_EBCDIC*/
#define RAW_ASCII_CHAR(ch)  (ch)
#endif /*CHARSET_EBCDIC*/

#ifndef DEBUG_PLSQL
#define DEBUG_PLSQL 7
#endif
#ifdef DEBUG_PLSQL
#	define ap_logrerror ap_log_rerror
#else
#	define ap_logrerror doNothing
	void doNothing(const char *c,...){}
#endif /*DEBUG_PLSQL*/
/* Helper routines for communications with a database and data definitions for them*/

/*Globals*/
/*static plsql_ShortOCIConn theCon;*/
module MODULE_VAR_EXPORT plsql_module;

static ap_pool* chpool=0; /*Used to keep all the cached connections, etc. basically pchild from http_main.*/
static table* expanded_dir_dbs;

/*
 * Locate our directory configuration record for the current request.
 */
static plsqlcfg *our_dconfig(request_rec *r)
{
    return (plsqlcfg *) ap_get_module_config(r->per_dir_config, &plsql_module);
}
/*
 * Locate our server configuration record for the specified server.
 */
static plsql_svr_cfg *our_sconfig(server_rec *s)
{
    return (plsql_svr_cfg *) ap_get_module_config(s->module_config, &plsql_module);
}

/*
 * Likewise for our configuration record for the specified request.
 */
static plsqlcfg *our_rconfig(request_rec *r)
{
    return (plsqlcfg *) ap_get_module_config(r->request_config, &plsql_module);
}

/*Helper functions*/
/******************************************************************************
 * Parser functions
 *****************************************************************************/

/* Convert a word to upper case*/
static char *ToUpper(char *word)
{
    int i=0;
    while(word[i]){
		word[i]=toupper(word[i]);
		++i;
    }
    return word;
}

/* Convert a word to lower case*/
static char *ToLower(char *word)
{
    int i=0;
    while(word[i]){
		word[i]=tolower(word[i]);
		++i;
    }
    return word;
}

static void decodeURI(char *s)
{
    int i=0;
    char hex[3]={0,0,0};
    char *wp=s;/* current Write Position*/
    while(s[i]){
	if(s[i]=='%'){
	    char c;
	    if(!(hex[0]=s[++i]))continue;
	    if(!(hex[1]=s[++i]))continue;
	        c=(char)strtol(hex,NULL,16);
	    *wp++=c;
	    if(c=='\'')
		*wp++='\'';
	}
	else if(s[i]=='+')
	    *wp++=' ';
	else if(s[i]=='\'')/*unencoded single quotes will be scipped, since they are hazardous*/
	    wp++;
	else
	    *wp++=s[i];
	++i;
    }
    *wp=0;
}
char* find_url_match(pool *p,const char *uri,const char *path,plsql_dbalias dbals[])
{
    int i=0;
    regmatch_t regm[10];
    while(dbals[i].url_match&&dbals[i].config_match){
		char *found;
		if (!regexec(dbals[i].url_match,uri,dbals[i].url_match->re_nsub+1,regm,0)){
		    uri_components puri;
	    	char k=0;
			if(dbals[i].config_match[0]=='/'||!path)
				return dbals[i].config_match;
			found=strrchr(path,'/');
			found=ap_pstrndup(p,path,found-path);
			return ap_psprintf(p,"%s/%s",found,dbals[i].config_match);
		}
		i++;
    }
    return NULL;
}
char* find_url_match_ex(request_rec* r)
{
    plsql_svr_cfg *scfg = our_sconfig(r->server);
    char * ret=find_url_match(r->pool,r->uri,r->filename,(plsql_dbalias*)scfg->dir_dbs->elts);
    if(!ret){
		char* tmp=strrchr(r->filename,'/');
		ret=ap_pstrndup(r->pool,r->filename,(tmp!=0)?tmp-r->filename:strlen(r->filename));
    }
    return ret;
}

/********************************************************\
* Reads and parses data passed using post method         *
\********************************************************/
#ifndef POST_BUF_SIZE
#define POST_BUF_SIZE HUGE_STRING_LEN
#endif
static plsql_ProcParam* parse_form_data(request_rec *r,const char *qs)
{
    const char *tmp;
    /*array_header *vars=0;*/
    plsql_ProcParam* vars=0;
    int i=-1;
    if(!qs)return 0;
    tmp=qs;
    /*count name/value pairs*/
    do{
		tmp=strchr(tmp,'=');
		++i;
    }while(tmp++);
    if(!i)return 0;
#if DEBUG_PLSQL>=2
    ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
            "httpd: parse_form_data: Starting. File %s",
            r->filename);
#endif
 	tmp=qs;
    /*get all the names and values*/
/*
pid=23&pid=24&pid=26&cid=1&pid=-1&pid=-1&qq=-1&qq=-1&sub=ADD+TO+BASKET
*/
    while(i--){
		plsql_ProcParam* curvar=ap_pcalloc(r->pool,sizeof(plsql_ProcParam));
		curvar->numvals=1;
		curvar->value=ap_palloc(r->pool,sizeof(char*));
		tmp=strchr(tmp,'=');/* next "=" -- where name ends*/
		curvar->name=ap_pstrndup(r->pool,qs,tmp-qs);/* copy name*/
		qs=++tmp;/*set qs to point to character next to "=" -- begining of value (if any)*/
		tmp=strchr(tmp,'&');/* find "&" -- end of name */
		tmp=tmp?tmp:qs+strlen(qs)+1;/* if not found, then this is last one -- name is rest of string*/
		curvar->value[0]=ap_pstrndup(r->pool,qs,tmp-qs);/*copy it there*/
		qs=++tmp;/* make qs point to the begining of next n/v */
		decodeURI(curvar->name);decodeURI(curvar->value[0]);/*decode name and value (should happen earlier)*/
		ToUpper(curvar->name);/* uppercase name -- arguments in PL/SQL are reported in uppercase by OCIDesc...*/
		if(!vars)
		    vars=curvar;
		else{
	    	char f=0;
		    plsql_ProcParam* tmp=vars,*tmp2=0;//find the place where cur->name is>=tmp->name
			while(tmp){
				int i;
#if DEBUG_PLSQL>=4
			    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
			                "httpd: parse_form_data:  %s=%s, %s=%s. File %s",
				tmp->name,tmp->value[tmp->numvals-1],curvar->name,curvar->value[0],r->filename);
#endif
				i=strcmp(tmp->name,curvar->name);
				if(i==0){
			    	char** cp=tmp->value;
			    	int i;
#if DEBUG_PLSQL>=4
				    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
		            			    "httpd: parse_form_data:  %s=%s. File %s",
									tmp->name,curvar->name,r->filename);
#endif
				    tmp->value=ap_palloc(r->pool,(++(tmp->numvals))*sizeof(char*));
				    for(i=0;i<tmp->numvals-1;++i)
						tmp->value[i]=cp[i];
				    //memcpy(tmp->value,cp,sizeof(char*)*(tmp->numvals-1));
				    tmp->value[tmp->numvals-1]=curvar->value[0];
				    f=1;
			    	break;
				}else if(i>0){
				    curvar->next=tmp;//insert cur in here;
				    if(tmp2)
						tmp2->next=curvar;
					else /* it should be inserted before first var */
						vars=curvar;
				    f=1;
				    break;
				}
				tmp2=tmp;
				tmp=tmp->next;
		    }
	    	if(!f){	/*if curvar has to be appended to the very end */
				tmp2->next=curvar;
				curvar->next=0;
			}
		}
    }
    return vars;
}
static plsql_ProcParam* plsql_get_posted_data(request_rec *r,int *ret)
{
    char *buf=0,*tmp=malloc(POST_BUF_SIZE);
    int len_read,tot_read=0,rr;
    plsql_ProcParam *t=0;
    if(!ret)ret=&rr;/* ignore ret value if NULL passed as ret*/
    *ret=OK;
    if(r->method_number!=M_POST)/*Make sure we are "POSTED"*/
    {
#if DUBUG_PLSQL>=5
	ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
                "httpd: get_posted_args: not a post request. File %s",
                r->filename);
#endif
	return 0;
    }
    if((*ret=ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK))!=OK)
    {
	ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
                "httpd: get_posted_args: problems with setup_client_block: %i. File %s",
		*ret,
                r->filename);
	return 0;
    }
    if(ap_should_client_block(r))
    {
	ap_hard_timeout("receiving POSTED args",r);
        while((len_read=ap_get_client_block(r, tmp,POST_BUF_SIZE))>0)
	{
	    buf=realloc(buf,tot_read+len_read+1);
	    memcpy(buf+tot_read,tmp,len_read);
	    tot_read+=len_read;
	    buf[tot_read]='\0';
	}
#ifdef DEBUG_PLSQL
	ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
                "httpd: get_posted_args: Parsing data: \n%s. File %s",
		buf,
                r->filename);
#endif
	t=parse_form_data(r,buf);
	ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
                "httpd: get_posted_args: Parsed. File %s",
                r->filename);
	ap_reset_timeout(r);
    }
    free(buf);
    return t;
}
/*********************************************************\
* Parses query string and puts it into a table            *
* Note: this function doesn't check for request method    *
\*********************************************************/
static plsql_ProcParam* plsql_parse_query_string(request_rec *r)
{
    return parse_form_data(r,r->parsed_uri.query);
}


/*functions*/
API_EXPORT_NONSTD(void) cleanup_dbdirconf(void* c);
void plsql_cleanupConn(plsql_ShortOCIConn *con)
{
  if(con)
  {
    if(con->hCtx)OCILogoff(con->hCtx,con->hErr);con->hCtx=0;
    if(con->hErr)OCIHandleFree(con->hErr,OCI_HTYPE_ERROR);con->hErr=0;
    if(con->hEnv)OCIHandleFree(con->hEnv,OCI_HTYPE_ENV);con->hEnv=0;
    con->r=0;
    con->err=0;
  }
}
static int plsql_check_error_ex(sword callRes,plsql_ShortOCIConn *con,const char* ex)
{
    //static int call_num=0;
    text errtext[1024]={0};
    ub4  errlen;
    ub4  errnum;
    char *ign;
    //call_num++;
    if(!con)return 0;
    switch(callRes)
    {
	case OCI_SUCCESS_WITH_INFO:
#if DEBUG_PLSQL>=4
	    puts("!!!Success with info!!!\n");
#endif
	    if(con->r){
#if DEBUG_PLSQL>=4
		puts("Has r");
#endif
	        if(con->hErr)
		    OCIErrorGet(con->hErr,1,NULL,&errnum,errtext,sizeof(errtext),OCI_HTYPE_ERROR);
		ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
            	    "httpd: plsql processing (%s): File %s",errtext,
        	    con->r->filename);
		con->err=con->err?ap_pstrcat(con->r->pool,con->err,errtext):ap_pstrdup(con->r->pool,errtext);
	    }
        case OCI_SUCCESS:
        case OCI_NO_DATA:
	    return 1;
        case OCI_ERROR:
	{
	    if(con->hErr){
		OCIErrorGet(con->hErr,1,NULL,&errnum,errtext,sizeof(errtext),OCI_HTYPE_ERROR);
	    }else if(con->hEnv){
		OCIErrorGet(con->hEnv,1,NULL,&errnum,errtext,sizeof(errtext),OCI_HTYPE_ENV);
	    }
	    switch(errnum){
		case 03113: /* Lost connection to database */
		case 03114:
		    if(con->r){
			char* confk=find_url_match_ex(con->r);
			if(confk){
			    plsql_DBDirConf* cf=(plsql_DBDirConf*)ap_table_get(expanded_dir_dbs,confk);
			    cleanup_dbdirconf(cf);
			    ap_table_unset(expanded_dir_dbs,confk);
			}
		    }
		    break;
		default:
		    break;
	    }
	    break;
	}
        case OCI_INVALID_HANDLE:
	    sprintf(errtext,"Invalid handle.\t");
	    break;
	default:
	    sprintf(errtext,"Unexpected error: %i\t",callRes);
	    break;
    }
#if DEBUG_PLSQL>=4
    printf("Error %s\n",errtext);
#endif
    if(con->r){
        if(!ex)ex="";
	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
                "httpd: plsql processing error: %s. %s. File %s",errtext,ex,
                con->r->filename);
        con->err=con->err?ap_pstrcat(con->r->pool,con->err,errtext):ap_pstrdup(con->r->pool,errtext);
    }
    return 0;
}
static int plsql_check_error(sword callRes,plsql_ShortOCIConn *con)
{
    return plsql_check_error_ex(callRes,con,NULL);
}



/* Getting DB Connection info*/
text* plsql_GetDBAl(request_rec *r)
{
    plsqlcfg *cfg=our_dconfig(r);
    return cfg->dbAl;
}
text* plsql_GetUName(request_rec *r)
{
    plsqlcfg *cfg=our_dconfig(r);
    return cfg->uName;
}
text* plsql_GetUPasswd(request_rec *r)
{
    plsqlcfg *cfg=our_dconfig(r);
    return cfg->uPasswd;
}
/* setup environment for a PL/SQL call*/
static int plsql_addEnv(void *c,const char *key,const char *value)
{
    plsql_ShortOCIConn *con=(plsql_ShortOCIConn*)c;
    char *stmt="BEGIN oci_www.setenv(:key,:value); END;";
    OCIStmt *hStmt=0;
    OCIBind *hKey=0,*hVal=0;
    static int call_num=0;
    call_num++;
    /*ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
                "httpd: plsql processing: plsql_addEnv (#%i): key=%s;val=%s.",
		call_num,key,value);
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
                "httpd: plsql processing: plsql_addEnv (#%i): key=;val=.",
		call_num);*/
    /*strcpy(stmt,"BEGIN\n oci_www.setenv('");
    strcat(stmt,key);strcat(stmt,"','");strcat(stmt,value);strcat(stmt,"');END;");*/
    if( plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con)&&
        plsql_check_error( OCIStmtPrepare(hStmt,con->hErr,stmt,strlen(stmt),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	plsql_check_error( OCIBindByName(hStmt,&hKey,con->hErr,":key",strlen(":key"),&key,256,OCI_TYPECODE_VARCHAR2,0,0,0,0,0,OCI_DEFAULT),con)&&
	plsql_check_error( OCIBindByName(hStmt,&hVal,con->hErr,":value",strlen(":value"),&value,1024,OCI_TYPECODE_VARCHAR2,0,0,0,0,0,OCI_DEFAULT),con)&&
	plsql_check_error( OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con))
    {
	plsql_check_error( OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
	return 1;
    }
    plsql_check_error( OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
            "httpd: plsql: Unable to setup environment. Variable %s value %s. File %s",
            key,value,con->r->filename);
    return 0;
}
/* 
*  this implementation of PL/SQL call environment setup simply
*  iterates through table of environment variables, adding each of them
*  separately. !!!Very inefficient!!!
*/
int plsql_SetUpEnv(plsql_ShortOCIConn *con,table *call_env)
{
    char stmt[1024];/*="BEGIN\n oci_www.setenv(:var,:val);END;";*/
    OCIStmt *hStmt;
    OCIBind *hKey=0,*hVal=0;
    char *var,*val;
    int i=0;
    table_entry *c_env=(table_entry*)(ap_table_elts(call_env)->elts);
    if(!plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con))
	return SERVER_ERROR;
    while(i<ap_table_elts(call_env)->nelts)
    {
	var=c_env[i].key;
	val=c_env[i].val;
	strcpy(stmt,"BEGIN\n oci_www.setenv('");strcat(stmt,var);strcat(stmt,"','");strcat(stmt,val);strcat(stmt,"');END;");
#if DEBUG_PLSQL>=5
	ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,con->r,
        	    "plsql:plsql_setup_env: env_line=%s. URI=%s",stmt,con->r->uri);
#endif //DEBUG_PLSQL
	if(!(plsql_check_error( OCIStmtPrepare(hStmt,con->hErr,stmt,strlen(stmt),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	     plsql_check_error( OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con))
	  )
	    return SERVER_ERROR;
	++i;
    }
    plsql_check_error( OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
    return OK;
}
int plsql_ConnectDB(plsql_ShortOCIConn *con,text *uname,text *passwd,text *dbal)
{
    if(!uname||!strcmp(uname,"")){
	return 0;
    }
    if(!passwd||!strcmp(passwd,"")){
	return 0;
    }
    if(!dbal){
	dbal="";
    }
    if( plsql_check_error( OCIEnvInit(&(con->hEnv),OCI_DEFAULT,0,0),con)&&
	plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&(con->hErr),OCI_HTYPE_ERROR,0,0),con)&&
	plsql_check_error( OCILogon(con->hEnv,con->hErr,(dvoid*)&(con->hCtx),uname,strlen(uname),passwd,strlen(passwd),dbal,strlen(dbal)),con))
	return 1;
    return 0;
}

API_EXPORT_NONSTD(void) cleanup_dbdirconf(void* c)
{
    plsql_DBDirConf* conf=(plsql_DBDirConf*)c;
#if DEBUG_PLSQL>=4
    printf("cleanup_dbdirconf\n");
#endif
    if(conf){
        conf->con.r=NULL;
	plsql_cleanupConn(&(conf->con));
    }
}

plsql_DBDirConf* connect_cache2(plsqlcfg* dcfg,const char* conff,request_rec* r)
{
    plsql_DBDirConf* conf;
    return NULL;
#if DEBUG_PLSQL>=4
    printf("connect_cache2; conff=%s\n",conff);
#endif
#if DEBUG_PLSQL>=1
    if(!conff||!dcfg)
	return NULL;
#endif
    if(conf=(plsql_DBDirConf*)ap_table_get(expanded_dir_dbs,conff)){
	conf->con.r=r;
	return conf;
    }
    conf=ap_pcalloc(chpool,sizeof(*conf));
    ap_block_alarms();
    if(!plsql_ConnectDB(&(conf->con),dcfg->uName,dcfg->uPasswd,dcfg->dbAl)){
	ap_unblock_alarms();
	return NULL;
    }
    conf->confpath=ap_pstrdup(chpool,conff);
    ap_table_set(expanded_dir_dbs,conff,(char*)conf);
    ap_register_cleanup(chpool,conf,cleanup_dbdirconf,ap_null_cleanup);
    ap_unblock_alarms();
    conf->con.r=r;
    return conf;
}
plsql_DBDirConf* connect_cache(const char* conff,request_rec *r)
{
    FILE* f;
    plsql_DBDirConf* conf;
    char buf[MAX_CONF_LINE]={0,};
    char uname[MAX_DBUNAME]={0,};
    char passwd[MAX_DBPASSWD]={0,};
    char dbal[MAX_DBALIAS]={0,};
#if DEBUG_PLSQL>=4
    printf("connect_cache; conff=%s\n",conff);
#endif
    if(conf=(plsql_DBDirConf*)ap_table_get(expanded_dir_dbs,conff)){
	conf->con.r=r;
	return conf;
    }
    ap_block_alarms();
    f=fopen(conff,"r");
    if(!f)
	return NULL;
    while(fgets(buf,sizeof(buf),f)){
	char *tmp=strchr(buf,'=');
	if(!tmp)
	    continue;
	*tmp++=0;
	tmp[strlen(tmp)-1]=0;
	if(strcasecmp(buf,"DBAl")==0){
	    strcpy(dbal,tmp);
	}
	else if(strcasecmp(buf,"DBUser")==0){
	    strcpy(uname,tmp);
	}
	else if(strcasecmp(buf,"DBPwd")==0){
	    strcpy(passwd,tmp);
	}
    }
    fclose(f);
    conf=ap_pcalloc(chpool,sizeof(*conf));
    if(!plsql_ConnectDB(&(conf->con),uname,passwd,dbal)){
        ap_unblock_alarms();
	return NULL;
    }
    conf->confpath=ap_pstrdup(chpool,conff);
    ap_table_set(expanded_dir_dbs,conff,(char*)conf);
    ap_register_cleanup(chpool,conf,cleanup_dbdirconf,ap_null_cleanup);
    ap_unblock_alarms();
    conf->con.r=r;
    return conf;
}

int plsql_execute(plsql_ShortOCIConn *con,text *stmt)
{
	OCIStmt *hStmt;
        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,con->r,
                "httpd: plsql processing :plsql_execute: Executing \n%s\n\tFile %s",
        	stmt,con->r->filename);
	
        if( plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con)&&
    	    plsql_check_error( OCIStmtPrepare(hStmt,con->hErr,stmt,strlen(stmt),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
    	    plsql_check_error( OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con)&&
    	    plsql_check_error( OCIHandleFree(hStmt,OCI_HTYPE_STMT),con))
		return 1;
	return 0;
}
int plsql_output(plsql_ShortOCIConn *con)
{
	text    *getHTTPHeader="BEGIN :lin:=oci_www.GetNextData; END;";
        OCIBind *hLin=NULL;
	text lin[4001];
        sb2  linInd=-1;
	/*OCIBind *hN=NULL;*/
        /*sword n=0;*/
	OCIStmt *hGetter=NULL;
	int ret;
        if(!(
	    plsql_check_error(OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con)&&
	    plsql_check_error(OCIStmtPrepare(hGetter,con->hErr,"BEGIN oci_www.GoStart;END;",strlen("BEGIN oci_www.GoStart;END;"),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	    plsql_check_error(OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con)&&
	    plsql_check_error(OCIHandleFree(hGetter,OCI_HTYPE_STMT),con)
	  ))
	    return 0;
	memset(lin,0,sizeof(lin));
	hGetter=NULL;
	if(
	    plsql_check_error(OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con)&&
	    plsql_check_error(OCIStmtPrepare(hGetter,con->hErr,getHTTPHeader,strlen(getHTTPHeader),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
    	    plsql_check_error(OCIBindByName(hGetter,&hLin,con->hErr,":lin",sizeof(":lin")-1,&lin,sizeof(lin),SQLT_STR,&linInd,0,0,0,0,OCI_DEFAULT),con)
	  )
	{
            while(plsql_check_error( OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con))
	    {
		if(linInd==-1)break;
#if DEBUG_PLSQL>=5
	        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,con->r,
        	    "plsql:plsql_output: Line:\n\t%s\n",lin);
#endif //DEBUG_PLSQL
		ap_rputs(lin,con->r);
		bzero(lin,sizeof(lin));
    	    }
	    plsql_check_error( OCIHandleFree(hGetter,OCI_HTYPE_STMT),con);
	    return 1;
	}
	return 0;
}

int plsql_headers(plsql_ShortOCIConn *con)
{
	text    *getHTTPHeader="BEGIN :lin:=oci_www.GetNextDataHdr; END;";
        OCIBind *hLin=NULL;
	text lin[1024];
        sb2  linInd=-1;
	OCIStmt *hGetter=NULL;
	int ret;
	bzero(lin,sizeof(lin));
        if( plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con)&&
	    plsql_check_error( OCIStmtPrepare(hGetter,con->hErr,"BEGIN oci_www.GoStartHdr;END;",strlen("BEGIN oci_www.GoStartHdr;END;"),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	    plsql_check_error( OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con)&&
	    plsql_check_error( OCIHandleFree(hGetter,OCI_HTYPE_STMT),con)&&

	    plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con)&&
	    plsql_check_error( OCIStmtPrepare(hGetter,con->hErr,getHTTPHeader,strlen(getHTTPHeader),OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
    	    plsql_check_error( OCIBindByName(hGetter,&hLin,con->hErr,":lin",strlen(":lin"),&lin,1024,SQLT_STR,&linInd,0,0,0,0,OCI_DEFAULT),con))
	{
	    char success;
            while(plsql_check_error(OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con))
	    {
		char *nm;
		if(linInd==-1)break;
		nm=strchr(lin,':');
		if(nm)/*ignore invalid headers for now ...*/
		{
		    *nm++=0;
		    while(ap_isspace(*nm)&&*nm++);/*skip white spaces*/
		    ap_table_set(con->r->headers_out,ap_pstrdup(con->r->pool,lin),ap_pstrdup(con->r->pool,nm));
		}
#if DEBUG_PLSQL>2
	        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
        		      "httpd: plsql: Header \"%s\" = \"%s\". File %s",
        		      lin,nm,con->r->filename);
		{
		    const char *l=ap_table_get(con->r->headers_out,lin);
		    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
        			    "httpd: plsql: \"%s\" = \"%s\". File %s",
        		            lin,l,con->r->filename);
		}
#endif
		bzero(lin,sizeof(lin));
    	    }
	    if(!success)
	    {
		return 0;
	    }
	    plsql_check_error( OCIHandleFree(hGetter,OCI_HTYPE_STMT),con);
#if 1
	    hGetter=0;
	    if(plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con)&&
	       plsql_check_error( OCIStmtPrepare(hGetter,con->hErr,"BEGIN :lin:=oci_www.ContentType; END;",sizeof("BEGIN :lin:=oci_www.ContentType; END;")-1,OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
    	       plsql_check_error( OCIBindByName(hGetter,&hLin,con->hErr,":lin",strlen(":lin"),&lin,sizeof(lin),SQLT_STR,&linInd,0,0,0,0,OCI_DEFAULT),con)&&
	       plsql_check_error(OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con)
	      )
	    {
		con->r->content_type=ToLower(ap_pstrdup(con->r->pool,lin));
		ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, con->r,
        			"httpd: plsql: Content-type = \"%s\"\n\tFile %s",
        			lin,con->r->filename);
	    }
	    else
		return 0;
	    plsql_check_error( OCIHandleFree(hGetter,OCI_HTYPE_STMT),con);
#else
	    con->r->content_type="text/html";
#endif //0|1
	    return 1;
	}
	return 0;
}

static const char *cmd_dbal(cmd_parms *cmd, void *mconfig,char *dbal)
{

    plsqlcfg *cfg = (plsqlcfg *) mconfig;

    /*cfg->local = 1;*/
    cfg->dbAl=ap_pstrdup(cmd->pool,dbal);
    return NULL;
}
static const char *cmd_uname(cmd_parms *cmd, void *mconfig,char *uname)
{
    plsqlcfg *cfg = (plsqlcfg *) mconfig;

    /*cfg->local = 1;*/
    cfg->uName=ap_pstrdup(cmd->pool,uname);
    return NULL;
}
static const char *cmd_upasswd(cmd_parms *cmd, void *mconfig,char *upasswd)
{

    plsqlcfg *cfg = (plsqlcfg *) mconfig;

    /*cfg->local = 1;*/
    cfg->uPasswd=ap_pstrdup(cmd->pool,upasswd);
    return NULL;
}
static const char *cmd_errorlevel(cmd_parms *cmd, void *mconfig,char *errorlevel)
{
    plsqlcfg *cfg = (plsqlcfg *) mconfig;
    sscanf(errorlevel,"%i",&(cfg->errorlevel));
    return NULL;
}
static const char *cmd_dbalias(cmd_parms *cmd, void *mconfig,char *location,char *config)
{
    server_rec* r=cmd->server;
    plsql_svr_cfg *cfg=(plsql_svr_cfg*)ap_get_module_config(r->module_config,&plsql_module);
    plsql_dbalias* dbal=(plsql_dbalias*)ap_push_array(cfg->dir_dbs);
    if(!(dbal->url_match=ap_pregcomp(cmd->pool,location, REG_EXTENDED)))
	return "DBAlias is not valid regular expression";
    dbal->config_match=ap_pstrdup(cmd->pool,config?config:DEFAULT_PLSQL_CONF);
    return NULL;
}
static char* plsql_GetProcName(request_rec *r)
{
    char *s=strrchr(r->parsed_uri.path,'/');
    if(!s||!++s||*s=='?')return (char*)0;
    return ap_pstrdup(r->pool,s);
}
static int plsql_logHeader(void *v, char *key, char* value)
{
    request_rec *r=(request_rec*)v;
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_Header: \"%s\"=\"%s\". File %s",
        	  key,value,r->parsed_uri.path);
}
typedef struct app_arg
{
    char *str;
    request_rec *r;
} app_arg;
static int append_arg(void *r,const char *key,const char *val)
{
    app_arg *aa=(app_arg*)r;
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,aa->r,
                  "httpd: plsql processing :append_arg: appending. File %s",
        	  aa->r->parsed_uri.path);
#endif
    aa->str=ap_psprintf(aa->r->pool,"%s, %s=>'%s',",aa->str,key,val);
    return 1;
}
/* Since we cach our connections, we must clear everything after we do all the processing.*/
void ConnClear(plsql_ShortOCIConn* con)
{
    OCIStmt *hStmt;
    //con->err=0;
    plsql_check_error(OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con);
    plsql_check_error(OCIStmtPrepare(hStmt,con->hErr,"BEGIN\n oci_www.ClearAll;\nEND;\n",
				     sizeof("BEGIN\n oci_www.ClearAll;\nEND;\n")-1,
				     OCI_NTV_SYNTAX,OCI_DEFAULT),con);
    plsql_check_error(OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con);
    plsql_check_error(OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
    con->err=0;
    con->r=0;
}

static int plsql_run(request_rec* r,plsql_ShortOCIConn* con,const char* stmtFin)
{
    //OK! We've got the connection to database. DO IT!
    int i=0;
    int ret=OK;
    const char *success=ap_table_get(r->notes,"plsql-auth-success");
    ap_add_cgi_vars(r);ap_add_common_vars(r);
    if(success&&strcmp(success,"true")&&(ret=plsql_SetUpEnv(con,r->subprocess_env))!=OK)
	return ret;
    if(plsql_execute(con,stmtFin)){
	const char *loc;
	ap_soft_timeout("PL/SQL output", r);
	plsql_headers(con);
	loc=ap_table_get(r->headers_out,"Location");
#if DEBUG_PLSQL>=2
	ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
        	      "httpd: plsql processing : Location= %s. File %s",
    		      loc,r->parsed_uri.path);
#endif
	if(loc){
#if DEBUG_PLSQL>=5
	    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            		  "httpd: plsql processing : Redirect to %s. File %s",
    			  loc,r->parsed_uri.path);
#endif
	    return REDIRECT;
	}
	ap_send_http_header(r);
    /*
     * If we're only supposed to send header information (HEAD request), we're
     * already there.
     */
	if (r->header_only)
    	    return OK;
	if(!plsql_output(con))
	{
	    ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,"plsql: plsql_output error. File %s",r->parsed_uri.path);
	    return SERVER_ERROR;
	}
    }
    else/*error executing*/{
	r->content_type="text/plain";
	ap_send_http_header(r);
	ap_rputs("Error during execution.\n",r);
	ap_rputs("\n\nTried to execute:\n\n",r);
		ap_rputs(stmtFin,r);
		ap_rputs(con->err?con->err:"",r);
	return OK;
	/*return HTTP_INTERNAL_SERVER_ERROR;*/
    }
    return OK;
}

static int plsql_do(request_rec *r)
{
    char *err=0;
    plsql_ShortOCIConn *con;//={0,0,0,r};
    plsqlcfg *dcfg = our_dconfig(r);
    plsql_svr_cfg *scfg = our_sconfig(r->server);
    char* confk;
    plsql_DBDirConf* conf=0;
    char tag[MAX_STRING_LEN];
    char parsed_string[MAX_STRING_LEN];
    char *tag_val;
    char *singleParams="";
    char *stmtFin,*procName=plsql_GetProcName(r);
    int ret;
    plsql_ProcParam *params=0,*params2=0;
    /* Just pass all parameters to procedure, quoting them with single quotes. Let Oracle
       figure out datatypes.
    */
    params=plsql_get_posted_data(r,&ret);/*Note! take care of error conditions later*/
    params2=plsql_parse_query_string(r);
    params=params?params:params2; //if there is posted data, ignore QUERY_STRING. It'll be available via ENV anyway
    if(params)
    {
	int i;
	/*
	 Find duplicate parameter names, so that we can do owa_util.ident_arr
	 All single parameters go into "single", others go into "array_header" one chain per name 
	*/
	plsql_ProcParam* pp=params;
	char* multParamStrings="";
	int cnt=1;
	while(pp)
	{
	    plsql_ProcParam* t=pp;
#if DEBUG_PLSQL>=4
	    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            		 "plsql: processing:Param %s=%s.\n\tFile:%s",
			 pp->name,pp->value,
        		 r->parsed_uri.path);
#endif
	    pp=pp->next;
	    if(t->numvals>1)
	    {
		int i=t->numvals;
		multParamStrings=
		    ap_psprintf(r->pool,"%s\tp_%s owa_util.ident_arr:=owa_util.ident_arr(",
		    		multParamStrings,t->name);
		while(i--)
		    multParamStrings=ap_psprintf(r->pool,"%s'%s',",multParamStrings,t->value[i]);
		multParamStrings[strlen(multParamStrings)-1]=0;
		multParamStrings=ap_psprintf(r->pool,"%s);\n",multParamStrings);
		singleParams=ap_psprintf(r->pool,"%s,\n\t%s=>p_%s",singleParams,
	    				     t->name,t->name);
	    }
	    else
	    {
    		singleParams=ap_psprintf(r->pool,"%s,\n\t\t%s=>'%s'",singleParams,
	    				     t->name,t->value[0]);
	    }
	}
	stmtFin=ap_psprintf(r->pool,"DECLARE\n%s\nBEGIN\n\t%s(%s);\n\tCOMMIT;\nEND;\n",
			    multParamStrings,procName,++singleParams);
    }
    else
	stmtFin=ap_psprintf(r->pool,"BEGIN\n\t%s;\n\tCOMMIT;\nEND;\n",procName);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_do: Statement ready:\n%s\n File %s",
        	  stmtFin,r->parsed_uri.path);
#endif
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_do: Connecting to a database,auth_type=%s 1. File %s",
        	  ap_auth_type(r),r->parsed_uri.path);
#endif

    if(!procName)return HTTP_NOT_FOUND;
    ap_soft_timeout("PL/SQL execution", r);
    r->content_type = "text/html";

#if DEBUG_PLSQL>=5
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_do: Connecting to a database, 2. File %s",
        	  r->parsed_uri.path);
#endif
    confk=find_url_match_ex(r);
#if DEBUG_PLSQL>=5
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_do: Connecting to a database, 3, %s. File %s",
        	  confk,r->parsed_uri.path);
#endif
    conf=connect_cache(confk,r);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_do: Connecting to a database, 4 (confk=%s). File %s",
        	  confk,r->parsed_uri.path);
#endif

    if(!conf)
    {
	conf=connect_cache2(dcfg,confk,r);
        if(!conf)
	{
	    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_run: Cannot connect to a database (config=%s). URI=%s",
        	  confk,r->uri);
	    return SERVER_ERROR;
	}    
    }
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
                  "httpd: plsql processing :plsql_do: Connecting to a database, 5. File %s",
        	  r->uri);
#endif
    con=&(conf->con);
    ret=plsql_run(r,con,stmtFin);
#if DEBUG_PLSQL>=5
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
                  "httpd: plsql processing :plsql_do: Finish, ret=%d. URL %s",
        	  ret,r->uri);
#endif

    ConnClear(con);
    ap_kill_timeout(r);
    return ret;
}
/*
 * Open the database connection? Or perhaps session management init...
 */
static void plsql_init(server_rec *s, pool *p)
{
    ;
}

static void plsql_child_init(server_rec *s, pool *p)
{
    /* Just initialize OCI Library and some vars, for connection caching*/
    /* TODO: some kind of persistent connections ...*/
    plsql_check_error( OCIInitialize(OCI_OBJECT,0,0,0,0),0);
    chpool=ap_make_sub_pool(p);
    expanded_dir_dbs=ap_make_table(p,1);
}
static void plsql_child_exit(server_rec *s, pool *p)
{
    ap_destroy_pool(chpool);
    //cpool=0;
    //expanded_dir_dbs=0;
}
static void *plsql_create_server_config(pool *p, server_rec *s)
{
    plsql_svr_cfg *cfg=ap_pcalloc(p,sizeof(plsql_svr_cfg));
    cfg->dir_dbs=ap_make_array(p,1,sizeof(plsql_dbalias));
    return (void *)cfg;
}
static void *plsql_create_dir_config(pool *p, char *dirspec)
{

    plsqlcfg *cfg;
    /*char *dname = dirspec;*/

    /*
     * Allocate the space for our record from the pool supplied.
     */
    cfg = (plsqlcfg *) ap_pcalloc(p, sizeof(plsqlcfg));
    //cfg->cmode = CONFIG_MODE_DIRECTORY;
    cfg->errorlevel=1;
    if(dirspec)
        cfg->loc=ap_pstrdup(p, dirspec);
    return (void *) cfg;
}
static void *plsql_merge_dir_config(pool *p, void *parent_conf,
                                      void *newloc_conf)
{

    plsqlcfg *merged_config = (plsqlcfg *) ap_pcalloc(p, sizeof(plsqlcfg));
    plsqlcfg *pconf = (plsqlcfg *) parent_conf;
    plsqlcfg *nconf = (plsqlcfg *) newloc_conf;
    char *note;

    /*
     * Some things get copied directly from the more-specific record, rather
     * than getting merged.
     */
//    merged_config->local = nconf->local;
    merged_config->loc = ap_pstrdup(p, nconf->loc);
    merged_config->uName=nconf->uName?ap_pstrdup(p,nconf->uName):pconf->uName;
    merged_config->uPasswd=nconf->uPasswd?ap_pstrdup(p,nconf->uPasswd):pconf->uPasswd;
    merged_config->dbAl=nconf->dbAl?ap_pstrdup(p,nconf->dbAl):pconf->dbAl;
    /*
     * Others, like the setting of the `congenital' flag, get ORed in.  The
     * setting of that particular flag, for instance, is TRUE if it was ever
     * true anywhere in the upstream configuration.
     */
//    merged_config->congenital = (pconf->congenital | pconf->local);
    /*
     * If we're merging records for two different types of environment (server
     * and directory), mark the new record appropriately.  Otherwise, inherit
     * the current value.
     */
    //merged_config->cmode =
    //    (pconf->cmode == nconf->cmode) ? pconf->cmode : CONFIG_MODE_COMBO;
    /*
     * Now just record our being called in the trace list.  Include the
     * locations we were asked to merge.
     */
//    note = ap_pstrcat(p, "example_merge_dir_config(\"", pconf->loc, "\",\"",
//                   nconf->loc, "\")", NULL);
//    trace_add(NULL, NULL, merged_config, note);
    return (void *) merged_config;
}

/*************************************************************************\
 * Doesn't actually do translation. Instead it only sets plsql handler,  *
 * and leaves actual URI->filename to other modules.                     *
\*************************************************************************/
static int plsql_translate_handler(request_rec *r)
{

    //plsqlcfg *cfg;
    //cfg = our_dconfig(r);
    plsql_DBDirConf* dbdir;
    plsql_svr_cfg* cfg;
    plsql_dbalias *dbals;
    char* conff;
    plsql_DBDirConf* con;

    cfg=our_sconfig(r->server);
    dbals=(plsql_dbalias*)(cfg->dir_dbs->elts);
#if DEBUG_PLSQL>=3
    if(!dbals)
    {
        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: translate: URI=%s, No dbals!",
    		     r->parsed_uri.path);
	return DECLINED;
    }
#endif

    conff=find_url_match(r->pool,r->uri,NULL,dbals);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: translate: file=%s, URI=%s, %s",
    		     r->filename,r->parsed_uri.path,conff);
#endif
    if(!conff)
    {
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: translate: URI=%s, not found",
    		     r->parsed_uri.path);
#endif
        return DECLINED;
    }
    //con=connect_cache(conff);
    //if(!con)
	//return HTTP_NOT_FOUND;
    //ap_table_set(expanded_dir_dbs,conff,(char*)con);
    r->handler="plsql";
    return DECLINED;
}

static int plsql_get_auth_pw(request_rec *r,const char** pw)
{
    const char *auth_line = ap_table_get(r->headers_in,
                                      r->proxyreq ? "Proxy-Authorization"
                                                  : "Authorization");
    const char *t;

    if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
        return DECLINED;

    if (!auth_line) {
        *pw="";
        return OK;
    }

    if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
        /* Client tried to authenticate using wrong auth scheme */
        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
                    "plsql: client used wrong authentication scheme, data ignored: %s", r->uri);
        *pw="";
        return OK;
    }

    t = ap_uudecode(r->pool, auth_line);
    /* Note that this allocation has to be made from r->connection->pool
     * because it has the lifetime of the connection.  The other allocations
     * are temporary and can be tossed away any time.
     */
    r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':');
    r->connection->ap_auth_type = "Basic";

    *pw = t;

    return OK;
}
static int do_we_auth(request_rec* r)
{
    const array_header* reqs=ap_requires(r);
    int i=0;
    require_line *rl;
    if(!reqs)return 0;
    while(i<reqs->nelts)
    {
	if(strcasecmp(((require_line*)(reqs->elts))[i].requirement,"plsql")==0)
	    return 1;
	++i;
    }
    return 0;
}
static int plsql_authorize(request_rec* r,plsql_ShortOCIConn* con)
{
    char stmt[1024];//="BEGIN\n :ret:=; END;";
    OCIStmt *hStmt=0;
    OCIBind *hAuthRes=0,*hRealm=0;
    int i=0;
    const char *spw="";
    int ret=0;
    int resAuth=0,resAuthInd=0,realmInd=0;
    char realm[2001];
    bzero(realm,sizeof(realm));
    ConnClear(con);
    con->r=r;

    ap_add_cgi_vars(r);ap_add_common_vars(r);
    if((ret=plsql_SetUpEnv(con,r->subprocess_env))!=OK)
	return ret;
	
#if DEBUG_PLSQL>=3
        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	      "httpd: plsql processing :plsql_authorize: Set up environment. URL=%s",
        	      r->parsed_uri.path);
#endif
    ap_table_setn(r->notes,"plsql-env-ready","true");

    if(!plsql_check_error( OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con))
	return SERVER_ERROR;
    if((ret=plsql_get_auth_pw(r,&spw))!=OK)
    {
#if DEBUG_PLSQL>=3
        ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	      "httpd: plsql processing :plsql_authorize: Getting PWD returned %d. URL=%s",
        	      ret,r->parsed_uri.path);
#endif

	return ret;
    }

    /* Set UserID and password*/
    ret=sprintf(stmt,"BEGIN\n oci_www.UserID:='%s';\n oci_www.UserPWD:='%s';\nEND;\n",r->connection->user,spw);
#if DEBUG_PLSQL>=5
    ap_logrerror (APLOG_MARK,APLOG_NOERRNO|APLOG_NOTICE,r,
                  "httpd: plsql processing :plsql_authorize: Set User Info SQL =\"%s\". URL=%s",
        	  stmt,r->parsed_uri.path);
#endif
    if(!(plsql_check_error(OCIStmtPrepare(hStmt,con->hErr,stmt,ret,OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
         plsql_check_error(OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con)))
    {
	plsql_check_error(OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
	return SERVER_ERROR;
    }
    /*call authorize, to see if user is fine here*/
#define AUTH_SQL "BEGIN\n IF authorize THEN\n  :res:=1;\n ELSE\n  :res:=0;\n END IF;\nEXCEPTION\n\tWHEN OTHERS THEN\n\t\t:res:=0;\nEND;"
#if DEBUG_PLSQL>=5
    ap_logrerror(APLOG_MARK,APLOG_NOERRNO|APLOG_NOTICE,r,
                  "httpd: plsql processing :plsql_authorize: AUTH_SQL = %s",AUTH_SQL
    	       );
#endif
    if(!(plsql_check_error(OCIStmtPrepare(hStmt,con->hErr,AUTH_SQL,sizeof(AUTH_SQL)-1,OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	 plsql_check_error(OCIBindByName(hStmt,&hAuthRes,con->hErr,":res",sizeof(":res")-1,&resAuth,sizeof(resAuth),SQLT_INT,&resAuthInd,0,0,0,0,OCI_DEFAULT),con)&&
	 plsql_check_error(OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con))
      )
    {
	plsql_check_error(OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
	return SERVER_ERROR;
    }
#define REALM_SQL "BEGIN\n :rea:=oci_www.AuthRealm;\nEND;"
    if(!(plsql_check_error(OCIStmtPrepare(hStmt,con->hErr,REALM_SQL,sizeof(REALM_SQL)-1,OCI_NTV_SYNTAX,OCI_DEFAULT),con)&&
	 plsql_check_error(OCIBindByName(hStmt,&hRealm,con->hErr,":rea",sizeof(":rea")-1,&realm,sizeof(realm),SQLT_STR,&realmInd,0,0,0,0,OCI_DEFAULT),con)&&
	 plsql_check_error(OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con))
      )
    {
	plsql_check_error( OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
	return SERVER_ERROR;
    }
    plsql_check_error(OCIHandleFree(hStmt,OCI_HTYPE_STMT),con);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
                  "httpd: plsql processing :plsql_authorize: Got AuthRealm (%s,%d). URL=%s",
        	  realm,realmInd,r->parsed_uri.path);
#endif
    if(realmInd>=0)
        ap_set_auth_name(r,realm);
    else
	ap_set_auth_name(r,r->uri);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
                  "httpd: plsql processing :plsql_authorize: Set AuthRealm (%s). URL=%s",
        	  realm,r->parsed_uri.path);
#endif
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	     "plsql: authorize:authRealm=%s(%d). URI=%s",
        	     realm,realmInd,r->parsed_uri.path);
#endif
    if(resAuthInd==-1||!resAuth)
    {
#if DEBUG_PLSQL>=3
	ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	      "plsql: authorize: resAuth=%d; resAuthInd=%d. URI=%s",
        	      resAuth,resAuthInd,r->parsed_uri.path);
#endif
	ap_note_basic_auth_failure(r);
	ap_table_setn(r->notes,"plsql-auth-success","false");
	return OK;//AUTH_REQUIRED;
    }

#if DEBUG_PLSQL>=3
	        ap_logrerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            			 "plsql: authorize:success.\n:%s",
        			 r->parsed_uri.path);
#endif
    ap_table_setn(r->notes,"plsql-auth-success","true");
    return OK;
}

static int plsql_check_user_id(request_rec *r)
{
    plsql_svr_cfg *scfg=our_sconfig(r->server);
    plsqlcfg *dcfg=our_dconfig(r);
    char* confk;
    plsql_DBDirConf* conf;

    if(!do_we_auth(r))
	return DECLINED;
    
    confk=find_url_match(r->pool,r->uri,r->filename,(plsql_dbalias*)scfg->dir_dbs->elts);
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: authorize:conf=%s.:%s",
        	     confk,r->parsed_uri.path);
#endif
    if(!confk)
	return DECLINED; //TODO: this way old-stype configurations cannot be authenticated.
			 //This MAY need to be fixed.
    conf=connect_cache(confk,r);
    if(!conf)
	return DECLINED;//????Or should it be HTTP_ERROR?
    {
	return plsql_authorize(r,&(conf->con));
    }
}
static int plsql_auth_checker(request_rec *r)
{
    const char *success=ap_table_get(r->notes,"plsql-auth-success");
    
#if DEBUG_PLSQL>=3
    ap_logrerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: auth_checker:success_note=%s.URI=%s",
        	     success,r->parsed_uri.path);
#endif
    if(!success)/*Never called for PL/SQL auth, or other problems (SERVER_ERROR)...*/
        return DECLINED;
    if(strcmp(success,"true")==0)
	return OK;
    if(strcmp(success,"false")==0){
	ap_note_basic_auth_failure(r);
	return AUTH_REQUIRED;
    }
    return SERVER_ERROR;
}
static int plsql_type_checker(request_rec *r)
{

    //plsqlcfg *cfg;

    //cfg = our_dconfig(r);
    /*
     * TODO: set responce stuff based on responces from PL/SQL processing
     */
    return DECLINED;
}
static int plsql_fixer_upper(request_rec *r)
{
    //plsqlcfg *cfg;

    //cfg = our_dconfig(r);
    return DECLINED;
}
static int plsql_logger(request_rec *r)
{

    plsqlcfg *cfg;

    cfg = our_dconfig(r);
    /*TODO: Logging*/
    return DECLINED;
}

static const command_rec plsql_cmds[] =
{
    {"DBAlias",cmd_dbalias,NULL,RSRC_CONF,TAKE12,"Dirrectory (or rather Location), in which everything is passed on to plsql module.\n"
						 "\tFirst URI to match, thenconfig file for that URI."
						 "\n\tIf config file starts with \"/\" it is take as absolute path,"
						 " otherwise it is appended to the end of URI path."},
    {"DBAl",cmd_dbal,NULL,OR_AUTHCFG,TAKE1,"DB alias (Net8) to use when connecting to database"},
    {"DBUser",cmd_uname,NULL,OR_AUTHCFG,TAKE1,"DB user to use when connecting to database"},
    {"DBPwd",cmd_upasswd,NULL,OR_AUTHCFG,TAKE1,"DB password to use when connecting to database"},
    {"DBErrorLevel",cmd_errorlevel,NULL,OR_AUTHCFG,TAKE1,"How much information you want on error page. 1-2, default 1."},
    /*{"DBErrorLog",cmd_dberrorlog,NULL,OR_AUTHCFG,TAKE1,""},*/
    {NULL}
};
static const handler_rec plsql_handlers[] =
{
    {"plsql", plsql_do},
    {NULL}
};
module plsql_module =
{
    STANDARD_MODULE_STUFF,
    plsql_init,               /* module initializer */
    plsql_create_dir_config,  /* per-directory config creator */
    plsql_merge_dir_config,   /* dir config merger */
    plsql_create_server_config,       /* server config creator */
    NULL,/*plsql_merge_server_config,        /* server config merger */
    plsql_cmds,               /* command table */
    plsql_handlers,           /* [7] list of handlers */
    plsql_translate_handler,  /* [2] filename-to-URI translation */
    plsql_check_user_id,      /* [5] check/validate user_id */
    plsql_auth_checker,       /* [6] check user_id is valid *here* */
    NULL,/*plsql_access_checker,     /* [4] check access by host address */
    plsql_type_checker,       /* [7] MIME type checker/setter */
    plsql_fixer_upper,        /* [8] fixups */
    plsql_logger,             /* [10] logger */
    NULL,/*plsql_header_parser,      /* [3] header parser */
    plsql_child_init,         /* process initializer */
    plsql_child_exit,         /* process exit/cleanup */
    NULL,/*plsql_post_read_request   /* [1] post read_request handling */
};
