package RestfulDB::DBSettings;

require Exporter;
our @ISA = qw( Exporter );
@EXPORT_OK = qw( get_database_settings );

use strict;
use warnings;
use Carp qw(croak);
use File::Basename qw(basename dirname);
use RestfulDB::Charman qw(remove_diacritics decode_hex_string);

# The get_database_settings() subroutine should return a cloned hash
# of the following structure, with fields filled in:

our %DB_settings = (
    db_name   => undef,
    db_table  => undef,
    db_user   => undef, # The database wrapper will pick a default
                        # user name if this field is undef.
    db_engine => undef,
    db_path   => undef,
    remote_user_for_print => undef,
);

# Determine database settings from the HTTP request (encoded in
# environment variables, passed as $env) and the CGI variables. The
# CGI variables are supposed to be already cleaned and untainted.

# Settings are common to all CGI scripts are collected here and
# reused:

sub get_database_settings
{
    my ($cgi_params, $env, $parameters ) = @_;

    our %DB_settings;
    my %db_settings = %DB_settings;

    my ($db_name, $db_table, $db_user);
    
    my $level = exists $parameters->{level} ?
        $parameters->{level} : 1;

    my $db_dir = $parameters->{db_dir}; # must be defined

    croak "Undefined db_dir passed!" unless defined $db_dir;
    
    # Decide what will be the default database and table names from
    # the provided URI (ENV REQUEST_URI):
    if( $level == 0 ) {
        $db_name  = basename( $env->{REQUEST_URI} );
    } elsif( $level == 1 ) {
        $db_name  = basename( dirname( $env->{REQUEST_URI} ));
        $db_table = basename( $env->{REQUEST_URI} );
    } elsif( $level == 2 ) {
        $db_name  = basename( dirname( dirname( $env->{REQUEST_URI} )));
        $db_table = basename( dirname( $env->{REQUEST_URI} ));
        if( $env->{REQUEST_URI} ne "" ) {
            $db_settings{record_id} = basename( $env->{REQUEST_URI} );
            if( $db_settings{record_id} =~ /^(.*?)\?/ ) {
                $db_settings{record_id} = $1;
            }
        }
    } elsif( $level == 3 ) {
        $db_name  = basename( dirname( dirname( dirname ( $env->{REQUEST_URI} ))));
        $db_table = basename( dirname( dirname( $env->{REQUEST_URI} )));
        if( $env->{REQUEST_URI} ne "" ) {
            $db_settings{record_id} = basename ( dirname( $env->{REQUEST_URI} ));
            $db_settings{record_column} = basename( $env->{REQUEST_URI} );
            if( $db_settings{record_column} =~ /^(.*?)\?/ ) {
                $db_settings{record_column} = $1;
            }
        }
    } else {
        croak "only URI levels 0, 1, 2 and 3 are supported, " .
            "but level '$level' was requested";
    }

    # Remove query string from the table name, in case we got it from
    # the request URI:
    if( defined $db_table && $db_table =~ /^(.*?)\?/ ) {
        $db_table = $1;
    }

    # If a database was specified explicitely as a CGI parameter, it
    # overrides our default guess:
    if( $cgi_params->{db} ) {
        $db_name = $cgi_params->{db};
    }

    # Similarly, override the table name from the CGI parameters:
    $db_table = $cgi_params->{table} if $cgi_params->{table};

    # Check whether we had enough components in the database path to
    # get the database name and table name:
    if( $db_table && ($db_name eq "/" || $db_name eq "." ||
                      $db_table eq "/" || $db_table eq ".") ) {
        croak "not enough components in the request '$env->{REQUEST_URI}' " .
            "to determine database name and database table at level $level";
    }
    
    # Determine what is our default database engine:
    if( exists $cgi_params->{engine} ) {
        $db_settings{db_engine} = $cgi_params->{engine};
    } else {
        # Engine not specified explicitly:

        # Decide what engine to use looking at the name of the
        # database (file):

        if( $db_name !~ /\./ ) {
            # If there is no dot in the database name, engine is
            # MySQL:
            $db_settings{db_engine} = "mysql";
        } elsif( $db_name =~ /2$/ ) {
                # Database "extension" ends in digit "2", e.g. "base.db2":
                # assume SQLite2 database:
                $db_settings{db_engine} = "SQLite2";
        } else {
            # Otherwise assume SQLite (SQLite3) by default:
            $db_settings{db_engine} = "SQLite";
        }
    }
    
    my $remote_user ;
    if( exists $env->{REMOTE_USER} ) {
        $remote_user = $env->{REMOTE_USER};
    }

    # In case the $ENV{REMOTE_USER} value comes from an x509
    # certificate, take a CN (Common Name) component as a database
    # user name, convert possible \xAB codes for nice prinout and
    # remove diacritic signs a (MySQL) login user:
    if( defined $remote_user ) {
        if( $remote_user =~ m,/CN=([^/]*)(/|$), ) {
            $db_user = remove_diacritics( decode_hex_string( $1 ));
            $remote_user = decode_hex_string( $remote_user );
        } else {
            $db_user = $remote_user;
        }
    }

    my $db_path = $db_dir . "/" . $db_name;

    $db_settings{db_name}  = $db_name;
    $db_settings{db_path}  = $db_path;
    $db_settings{db_table} = $db_table;
    $db_settings{db_user}  = $db_user;
    $db_settings{remote_user_for_print} = $remote_user;
    
    wantarray ? %db_settings : \%db_settings;
}

1;
