/* 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 <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.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 8
#endif

#ifndef DEBUG_OWA_LOGGING
#define DEBUG_OWA_LOGGING 0
#endif

#ifndef DEBUG_PLSQL_CONNCACHING
#define DEBUG_PLSQL_CONNCACHING 4
#endif

/* Helper routines for communications with a database and data definitions for them*/
plsql_DBDirConf* connect_cache(const char* conff,request_rec *r);

/*Globals*/
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;

static plsqlcfg *our_dconfig(request_rec *r)
{
    return (plsqlcfg *) ap_get_module_config(r->per_dir_config, &plsql_module);
}
static plsql_svr_cfg *our_sconfig(server_rec *s)
{
    return (plsql_svr_cfg *) ap_get_module_config(s->module_config, &plsql_module);
}
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,char isParam)
{
    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 and shouldn't be there anyways
						    */
			;
		else if(isParam=='y'&&s[i]=='.')/*replace "." with "_" in parameter names*/
			*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>=5
    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
            "httpd: parse_form_data: Starting:data=\"%s\". URI %s",
            qs,r->parsed_uri.path);
#endif
 	tmp=qs;
    /*get all the names and values*/
    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*/
		if(!tmp)
			break;
		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,'y');decodeURI(curvar->value[0],'n');/*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_log_rerror(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_log_rerror(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';
		}
#if DEBUG_PLSQL>9
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
    	            "httpd: get_posted_args: Parsing data: \n%s. File %s",
					buf,r->filename);
#endif
		t=parse_form_data(r,buf);
#if DEBUG_PLSQL>9
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
        	         "httpd: get_posted_args: Parsed. File %s",r->filename);
#endif
		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)
{
    text errtext[1024]={0};
    ub4  errlen;
    ub4  errnum;
    char *ign;
	request_rec* r;
    if(!con)return 0;
	r=con->r;
	while(r&&r->prev)
		r=r->prev;
    switch(callRes)
    {
	case OCI_SUCCESS_WITH_INFO:
		if(con->r){
			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(r->pool,con->err,errtext):ap_pstrdup(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 3113: /* Lost connection to database */
			case 3114:
			case 1041:
	    		if(con->r){
					char* confk=find_url_match_ex(con->r);
					ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, con->r,
           						  "httpd: plsql processing: Lost conncetion to Oracle, reestablishing. URL %s",
								  con->r->uri);
					
					if(confk){
		    			plsql_DBDirConf* cf=(plsql_DBDirConf*)ap_table_get(expanded_dir_dbs,confk);
		    			/*cleanup_dbdirconf(cf);
		    			ap_table_unset(expanded_dir_dbs,confk);*/
						cf->mtime=0;
						cf=connect_cache(confk,con->r);
						ap_table_setn(expanded_dir_dbs,confk,(char*)cf);
				}
	    	}
	    	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>=8
    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. URI %s",errtext,ex,
					  con->r->parsed_uri.path);
		con->err=con->err?ap_pstrcat(r->pool,con->err,errtext,NULL):ap_pstrdup(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++;
	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>=9
	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,con->r,
        	    "plsql:plsql_setup_env: env_line=%s. URI=%s",stmt,con->r->uri);
#endif
	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(conf){
		conf->con.r=NULL;
		plsql_cleanupConn(&(conf->con));
	}
}

/*
 * Finds open connection for given config
 * and prepares it for use with given request.
 * If none is found, opens a new one and registers it.
 * If modification time of config is greater then
 * on found connection, and it is first time we use
 * connection during given request processing,
 * closes connection and reopens it with new config data.
 */ 
plsql_DBDirConf* connect_cache(const char* conff,request_rec *r)
{
    FILE* f;
    plsql_DBDirConf* conf=0;
    char buf[MAX_CONF_LINE]={0,};
    char uname[MAX_DBUNAME]={0,};
    char passwd[MAX_DBPASSWD]={0,};
    char dbal[MAX_DBALIAS]={0,};
	int  dblog=-1;
#if DEBUG_PLSQL_CONNCACHING>=1
	const char* cached=ap_table_get(r->notes,"plsql-conn-cached");
#endif
#if DEBUG_PLSQL_CONNCACHING>=1
	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
        	      "plsql:connect_cache: pid=%x, conff=%s",getpid(),conff);
#endif /*DEBUG_PLSQL*/
    if(conf=(plsql_DBDirConf*)ap_table_get(expanded_dir_dbs,conff)){
		struct stat s;
		const char *cached;
#if DEBUG_PLSQL_CONNCACHING>=4
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
        		      "plsql:connect_cache: Found open connection for conff=%s",conff);
#endif /*DEBUG_PLSQL*/
		conf->con.r=r;
		conf->con.err=0;
		stat(conff,&s);
		cached=ap_table_get(r->notes,"plsql-conn-cached");
		if(conf->mtime>=s.st_mtime||cached){/*file wasn't modified since connection was open or we used it alredy during this req*/
#if DEBUG_PLSQL_CONNCACHING>=4
			ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
        			      "plsql:connect_cache: Connection is fine. conff=%s",conff);
#endif /*DEBUG_PLSQL*/
			ap_table_set(r->notes,"plsql-conn-cached","y");
			return conf;
		}
#if DEBUG_PLSQL_CONNCACHING>=2
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
        		      "plsql:connect_cache: Reopening connection. conff=%s",conff);
#endif /*DEBUG_PLSQL*/
		plsql_cleanupConn(&(conf->con));
		if(conf->db_log!=-1)
			ap_pclosef(chpool,conf->db_log);
    }
#if DEBUG_PLSQL_CONNCACHING>=1
	if(cached){
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
        		      "plsql:connect_cache: Reopening connection in the middle! conff=%s",conff);	
		return NULL;
	}
#endif
    ap_block_alarms();
    f=fopen(conff,"r");
    if(!f){
		ap_table_unset(expanded_dir_dbs,conff);
		ap_unblock_alarms();
		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);
		}
		else if(strcasecmp(buf,"DBLogFile")==0){
			char* logname=tmp;
			if(tmp[0]!='/'){
				char *tmp2=strrchr(conff,'/');
				logname=ap_palloc(r->pool,tmp2-conff+2);
				memcpy(logname,conff,tmp2-conff+1);
				logname[tmp2-conff+1]=0;
				logname=ap_pstrcat(r->pool,logname,tmp,NULL);
			}
			dblog=ap_popenf(chpool,logname,O_APPEND|O_CREAT|O_WRONLY,0644);
			if(dblog==-1){
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
							  "httpd: plsql processing: cnnect_cache: Opening log file \"%s\" failed: %s. URI %s",
							  logname,strerror(errno),r->parsed_uri.path);
				ap_table_unset(expanded_dir_dbs,conff);
				ap_unblock_alarms();
				return NULL;
			}
#if DEBUG_PLSQL_CONNCACHING>=4
			ap_log_rerror ( APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
							"httpd: plsql processing: connect_cache: Opening log file \"%s\" fd=%d. URI %s",
							logname,dblog,r->parsed_uri.path);
#endif /*DEBUG_PLSQL*/
		}
	}
	fclose(f);
	if(!conf)
		conf=ap_pcalloc(chpool,sizeof(plsql_DBDirConf));
	conf->con.r=r;
	if(!plsql_ConnectDB(&(conf->con),uname,passwd,dbal)){
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
					  "httpd: plsql processing: connect_cache: failed to connect to database, conff=%s. URI %s",
					  conff,r->parsed_uri.path);
		ap_table_unset(expanded_dir_dbs,conff);
		ap_unblock_alarms();
		return NULL;
	}
	if(!conf->confpath)
		conf->confpath=ap_pstrdup(chpool,conff);
	conf->db_log=dblog;
	conf->mtime=time(NULL);
	ap_table_setn(expanded_dir_dbs,conf->confpath,(char*)conf);
	ap_register_cleanup(chpool,conf,cleanup_dbdirconf,ap_null_cleanup);
	ap_table_set(r->notes,"plsql-conn-cached","y");
	ap_unblock_alarms();
	return conf;
}

int plsql_execute(plsql_ShortOCIConn *con,text *stmt)
{
	OCIStmt *hStmt=0;
#if DEBUG_PLSQL>2
	ap_log_rerror ( APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,con->r,
					"httpd: plsql processing :plsql_execute: Executing \n%s\n\tFile %s",
					stmt,con->r->filename);
#endif /*DEBUG_PLSQL*/
	if( plsql_check_error_ex(OCIHandleAlloc(con->hEnv,(dvoid*)&hStmt,OCI_HTYPE_STMT,0,0),con,"OCIHandleAlloc")&&
		plsql_check_error_ex(OCIStmtPrepare(hStmt,con->hErr,stmt,strlen(stmt),OCI_NTV_SYNTAX,OCI_DEFAULT),con,"OCIStmtPrepare")&&
		plsql_check_error_ex(OCIStmtExecute(con->hCtx,hStmt,con->hErr,1,0,0,0,OCI_DEFAULT),con,"OCIStmtExecute")&&
		plsql_check_error_ex(OCIHandleFree(hStmt,OCI_HTYPE_STMT),con,"OCIHandleFree"))
		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;
	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>=9
	        ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,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>5
			ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 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_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 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);
	    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_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 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);
	    return 1;
	}
	return 0;
}
/*
static const char *cmd_dbal(cmd_parms *cmd, void *mconfig,char *dbal)
{

    plsqlcfg *cfg = (plsqlcfg *) mconfig;

    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->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->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_oraauth(cmd_parms *cmd, void *mconfig,char *conff)
{
    plsqlcfg *cfg = (plsqlcfg *) mconfig;
	if(!cmd->path)
		return "mod_plsql:OraAuth: Only allowed in dirrectroy contxt";
	cfg->confname=ap_psprintf(cmd->pool,"%s/%s",cmd->path,conff);
	if(!cfg->confname)
		return "mod_plsql:Out of memory during configuration";
    return NULL;
}
static const char *cmd_dbalias(cmd_parms *cmd, void *mconfig,char *location,char *config)
{
	/*
	 * On server level -- save url match to global table of reg expressions.
	 * On .htaccess (or rather dirrectory) level -- make location relative to
	 *	that dirrectory. No matching -- no bothering.
	 */
    if(!cmd->path){
		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 "mod_plsql:URL match is not valid regular expression";
		dbal->config_match=ap_pstrdup(cmd->pool,config?config:DEFAULT_PLSQL_CONF);
		if(!dbal->config_match)
			return "mod_plsql:Out of memory during configuration";
		return NULL;
    }else{
		plsqlcfg* cfg=(plsqlcfg*)mconfig;
		cfg->confname=ap_pstrcat(cmd->pool,cfg->location,config);
		if(!cfg->confname)
			return "mod_plsql:Out of memory during configuration";
		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_log_rerror (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_log_rerror (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 catch our connections, we must clear everything after we do all the processing.*/
void ConnClear(plsql_ShortOCIConn* con)
{
    OCIStmt *hStmt=0;
    con->err=0;
    con->r=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);
}
void ClearLog(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.ClearLog;\nEND;\n",
				     sizeof("BEGIN\n oci_www.ClearLog;\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(plsql_ShortOCIConn* con,const char* stmtFin)
{
	/*OK! We've got the connection to database. DO IT!*/
	request_rec* r=con->r;
	int i=0;
	int ret=OK;
	const char *success;
#if DEBUG_PLSQL>=8
    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
   	              "httpd: plsql processing :plsql_run: Do we need to set up environment?. File %s",
       			  r->uri);
#endif
	success=ap_table_get(r->notes,"plsql-env-ready");
	if(success&&strcmp(success,"true")){
#if DEBUG_PLSQL>=8
	    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
   		              "httpd: plsql processing :plsql_run: Yes, we do. File %s",
       				  r->uri);
#endif
		ap_add_cgi_vars(r);ap_add_common_vars(r);
		if((ret=plsql_SetUpEnv(con,r->subprocess_env))!=OK)
			return ret;
	}
#if DEBUG_PLSQL>=8
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
   	              "httpd: plsql processing :plsql_run: Environment is set. File %s",
       			  r->uri);
#endif
	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_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
						"httpd: plsql processing : Location= %s. File %s",
						loc,r->parsed_uri.path);
#endif
		if(loc){
/*			if(loc[0]=='/'){
#if DEBUG_PLSQL>=5
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
								"httpd: plsql processing : Internal redirect to %s. File %s",
								loc,r->parsed_uri.path);
#endif
				r->method=ap_pstrdup(r->pool,"GET");
		    	r->method_number=M_GET;
				ap_table_unset(r->headers_in,"Content-Length");
				ap_internal_redirect(loc,r);
			    return OK;
			}else{*/
#if DEBUG_PLSQL>=5
				ap_log_rerror(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_log_rerror(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;
    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>=9
		    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,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>=7
   	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
       	          "httpd: plsql processing :plsql_do: Statement ready:\n%s\n File %s",
       			  stmtFin,r->parsed_uri.path);
#endif
#if DEBUG_PLSQL>=3
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,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>=8
    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,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(!confk){
#if DEBUG_PLSQL>=8
	    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
   		              "httpd: plsql processing :plsql_do: db.conf not found in per-server list. File %s",
       				  r->parsed_uri.path);
#endif
		confk=dcfg->confname;
   	}
#if DEBUG_PLSQL>=8
	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,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>=8
   	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
       	          "httpd: plsql processing :plsql_do: Connecting to a database, 4 (confk=%s). File %s",
       			  confk,r->parsed_uri.path);
#endif
    if(!conf){
	    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
   		              "httpd: plsql processing :plsql_run: Cannot connect to a database (config=%s). URI=%s",
       				  confk,r->parsed_uri.path);
    	return SERVER_ERROR;
	}
#if DEBUG_PLSQL>=8
    ap_log_rerror (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);
#if DEBUG_PLSQL>=8
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
   	              "httpd: plsql processing :plsql_do: Connecting to a database, 6; con=%x,con->r=%x. File %s",
       			  con,con->r,r->uri);
#endif
   	ret=plsql_run(con,stmtFin);
#if DEBUG_PLSQL>=5
   	ap_log_rerror (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)
{
    /* Just initialize OCI Library and some vars, for connection caching*/
    plsql_check_error( OCIInitialize(OCI_OBJECT,0,0,0,0),0);
}

static void plsql_child_init(server_rec *s, pool *p)
{
    chpool=p;
    expanded_dir_dbs=ap_make_table(p,1);
}
static void plsql_child_exit(server_rec *s, pool *p)
{
}
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_merge_server_config(pool *p, void *server1_conf,
                                         void *server2_conf)
{
	plsql_svr_cfg *merged_config = (plsql_svr_cfg *)ap_pcalloc(p, sizeof(plsql_svr_cfg));
	plsql_svr_cfg *s1conf=(plsql_svr_cfg*)server1_conf;
	plsql_svr_cfg *s2conf=(plsql_svr_cfg*)server2_conf;
	merged_config->dir_dbs=ap_append_arrays(p,s1conf->dir_dbs,s2conf->dir_dbs);
	return(void*)merged_config;
}

static void *plsql_create_dir_config(pool *p, char *dirspec)
{

    plsqlcfg *cfg;
    cfg = (plsqlcfg *) ap_pcalloc(p, sizeof(plsqlcfg));
    cfg->errorlevel=1;
	cfg->location=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->confname = ap_pstrdup(p, nconf->confname);
	merged_config->location=nconf->location;
    return (void *) merged_config;
}

/*************************************************************************\
 * Finds a propper configuration file *
\*************************************************************************/
static int plsql_translate_handler(request_rec *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>=8
    if(!dbals)
    {
		ap_log_rerror (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_log_rerror (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>=8
	    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	     "plsql: translate: URI=%s, not found",
    		     r->parsed_uri.path);
#endif
        return DECLINED;
    }
/*	con=connect_cache(conff,r);
	if(!con)
		return HTTP_NOT_FOUND;
*/
    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];
    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); /*If plsql_do was never called and thus connection isn't cleared*/
    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>=8
    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
           	      "httpd: plsql processing :plsql_authorize: Set up environment. URL=%s",
				  r->parsed_uri.path);
#endif
    ap_table_set(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_log_rerror(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?r->connection->user:"",spw);
#if DEBUG_PLSQL>=5
    ap_log_rerror (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;oci_www.sys_log_error(SQLERRM);\nEND;"
#if DEBUG_PLSQL>=8
    ap_log_rerror(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_log_rerror(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_log_rerror (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_log_rerror (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_log_rerror(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_set(r->notes,"plsql-auth-success","false");
		return OK;/*AUTH_REQUIRED;*/
	}

#if DEBUG_PLSQL>=3
	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
            	  "plsql: authorize:success.\n:%s",
        		   r->parsed_uri.path);
#endif
    ap_table_set(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_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	  "plsql: authorize:conf=%s.: %s",
				  confk,r->parsed_uri.path);
#endif
    if(!confk){
		if(!(confk=dcfg->confname))
			return DECLINED;
	} 
    conf=connect_cache(confk,r);
    if(!conf)
		return SERVER_ERROR;
#if DEBUG_PLSQL>=3
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	     "plsql: authorize:Got connection. conf=%s.: %s",
        	     confk,r->parsed_uri.path);
#endif
	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_log_rerror (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 void plsql_log_errors(request_rec *r)
{
	plsql_ShortOCIConn* con;
	plsql_svr_cfg *scfg=our_sconfig(r->server);
	plsqlcfg *dcfg=our_dconfig(r);
	plsql_DBDirConf* conf;
#define plsql_get_log_line	"BEGIN :lin:=oci_www.GetNextErr(:num); END;"
#define plsql_get_sys_log_line	"BEGIN :lin:=oci_www.GetSysErr(:num); END;"
	OCIBind *hLin=NULL;
	OCIBind *hNum=NULL;
	text lin[4096];
    sb2  linInd=0;
	int  num=1;
	OCIStmt *hGetter=NULL;
	int ret;
	const char *confk=find_url_match(r->pool,r->uri,r->filename,(plsql_dbalias*)scfg->dir_dbs->elts);
#if DEBUG_PLSQL>=7
    ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	   "plsql: log: conf=%s. URI= %s",
        		   confk,r->parsed_uri.path);
#endif
    if(!confk){
		if(!(confk=dcfg->confname))
			return;
	} 
    conf=connect_cache(confk,r);
    if(!conf){
#if DEBUG_PLSQL>=7
    	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	   "plsql: log: didn't find connection. URI= %s",
        		   confk,r->parsed_uri.path);
#endif
		return;
	}
	con=&(conf->con);
	if(conf->db_log!=-1){
		bzero(lin,sizeof(lin));
		if( plsql_check_error_ex(OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con,"log_errors: Allocating error log statement handle")&&
			plsql_check_error_ex(OCIStmtPrepare(hGetter,con->hErr,plsql_get_log_line,sizeof(plsql_get_log_line)-1,OCI_NTV_SYNTAX,OCI_DEFAULT),con,"log_errors: Preparing log retival statement")&&
			plsql_check_error_ex(OCIBindByName(hGetter,&hLin,con->hErr,":lin",strlen(":lin"),&lin,sizeof(lin),SQLT_STR,&linInd,0,0,0,0,OCI_DEFAULT),con,"log_errors: Bind lin")&&
			plsql_check_error_ex(OCIBindByName(hGetter,&hNum,con->hErr,":num",strlen(":num"),&num,sizeof(num),SQLT_INT,NULL,0,0,0,0,OCI_DEFAULT),con,"log_errors: Bind num")
	  	)
		{
			while(plsql_check_error(OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con)&&linInd!=-1){
				time_t t=time(NULL);
				char* st=ctime(&t);
				char *log_line;
				st[strlen(st)-1]=0;
				log_line=ap_psprintf(r->pool,"%s:%d:%s: %s\n",st,getpid(),r->parsed_uri.path,lin);
				flock(conf->db_log,LOCK_EX);
				if(write(conf->db_log,log_line,strlen(log_line))==-1){
			    	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,r,
    				        	   "plsql: log: unable to write to log file: %s. URI= %s",
        						   strerror(errno),r->parsed_uri.path);
				}
				fsync(conf->db_log);
				flock(conf->db_log,LOCK_UN);
				bzero(lin,sizeof(lin));
#if DEBUG_OWA_LOGGING>=1
		    	ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
    			        	   "plsql: log: \"%s\". URI= %s",
        					   log_line,r->parsed_uri.path);
#endif /*DEBUG_OWA_LOGGING*/
				num++;
    		}
#if DEBUG_PLSQL>=7
			ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
	            	   "plsql: log: num=%d, conf->db_log=%d. URI= %s",
	        		   num,conf->db_log,r->parsed_uri.path);
#endif
			plsql_check_error(OCIHandleFree(hGetter,OCI_HTYPE_STMT),con);
		}
	}
	hGetter=NULL;
	num=1;
	bzero(lin,sizeof(lin));
	if( plsql_check_error_ex(OCIHandleAlloc(con->hEnv,(dvoid*)&hGetter,OCI_HTYPE_STMT,0,0),con,"log_errors: Allocating error log statement handle")&&
		plsql_check_error_ex(OCIStmtPrepare(hGetter,con->hErr,plsql_get_sys_log_line,sizeof(plsql_get_sys_log_line)-1,OCI_NTV_SYNTAX,OCI_DEFAULT),con,"log_errors: Preparing log retival statement")&&
		plsql_check_error_ex(OCIBindByName(hGetter,&hLin,con->hErr,":lin",strlen(":lin"),&lin,sizeof(lin),SQLT_STR,&linInd,0,0,0,0,OCI_DEFAULT),con,"log_errors: Bind lin")&&
		plsql_check_error_ex(OCIBindByName(hGetter,&hNum,con->hErr,":num",strlen(":num"),&num,sizeof(num),SQLT_INT,NULL,0,0,0,0,OCI_DEFAULT),con,"log_errors: Bind num")
	  )
	{
		while(plsql_check_error(OCIStmtExecute(con->hCtx,hGetter,con->hErr,1,0,0,0,OCI_DEFAULT),con)&&linInd!=-1){
			time_t t=time(NULL);
			char* st=ctime(&t);
			st[strlen(st)-1]=0;
			ap_log_rerror(APLOG_MARK,APLOG_NOERRNO|APLOG_ERR,r,"plsql:%s\n",lin);
			bzero(lin,sizeof(lin));
			num++;
   		}
#if DEBUG_PLSQL>=7
		ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,r,
            	   "plsql: log: num=%d. URI= %s",
        		   num,r->parsed_uri.path);
#endif
		plsql_check_error(OCIHandleFree(hGetter,OCI_HTYPE_STMT),con);
	}
	ClearLog(con);
}
static int plsql_logger(request_rec *r)
{
	plsqlcfg *cfg;
	cfg = our_dconfig(r);
	while(r){
		plsql_log_errors(r);
		r=r->next;
	}
	return OK;
}

static const command_rec plsql_cmds[] =
{
    {"DBAlias",cmd_dbalias,NULL,RSRC_CONF|OR_AUTHCFG,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."},
	{"OraAuth",cmd_oraauth,NULL,OR_AUTHCFG,TAKE1,"In case you only want to use authorization from datbase"},
    /*{"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 */
    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 */
};
