From 7c1bf3d258320cedf4c4147edc83432bada8418d Mon Sep 17 00:00:00 2001
From: Nigel Kukard <nkukard@lbsd.net>
Date: Sun, 16 Nov 2008 07:56:16 +0000
Subject: [PATCH] * Added caching engine * Added database engine

---
 smradius/cache.pm    | 218 ++++++++++++++++++++++++++
 smradius/dbilayer.pm | 359 +++++++++++++++++++++++++++++++++++++++++++
 smradius/dblayer.pm  | 292 +++++++++++++++++++++++++++++++++++
 3 files changed, 869 insertions(+)
 create mode 100644 smradius/cache.pm
 create mode 100644 smradius/dbilayer.pm
 create mode 100644 smradius/dblayer.pm

diff --git a/smradius/cache.pm b/smradius/cache.pm
new file mode 100644
index 00000000..2844a141
--- /dev/null
+++ b/smradius/cache.pm
@@ -0,0 +1,218 @@
+# Caching engine
+# Copyright (C) 2007 Nigel Kukard  <nkukard@lbsd.net>
+# Copyright (C) 2008, LinuxRulz
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+package cbp::cache;
+
+use strict;
+use warnings;
+
+
+require Exporter;
+our (@ISA,@EXPORT);
+@ISA = qw(Exporter);
+@EXPORT = qw(
+	cacheStoreKeyPair
+	cacheGetKeyPair
+);
+
+use Cache::FastMmap;
+
+# Cache stuff
+my $cache_type = "FastMmap";
+my $cache;
+
+
+# Our current error message
+my $error = "";
+
+# Set current error message
+# Args: error_message
+sub setError
+{
+	my $err = shift;
+	my ($package,$filename,$line) = caller;
+	my (undef,undef,undef,$subroutine) = caller(1);
+
+	# Set error
+	$error = "$subroutine($line): $err";
+}
+
+# Return current error message
+# Args: none
+sub Error
+{
+	my $err = $error;
+
+	# Reset error
+	$error = "";
+
+	# Return error
+	return $err;
+}
+
+
+
+
+
+# Initialize cache
+sub Init
+{
+	my $server = shift;
+	my $ch;
+
+
+	# Create Cache
+	$ch = Cache::FastMmap->new(
+		'page_size' => 2048,
+		'num_pages' => 1000,
+		'raw_values' => 1,
+		'unlink_on_exit'	=> 1,
+	);
+
+	# Stats
+	$ch->set('Cache/Stats/Hit',0);
+	$ch->set('Cache/Stats/Miss',0);
+
+	# Set server vars
+	$server->{'cache_engine'}{'handle'} = $ch;
+};
+
+# Destroy cache
+sub Destroy
+{
+	my $server = shift;
+
+};
+
+# Connect child to cache
+sub connect
+{
+	my $server = shift;
+
+	$cache = $server->{'cache_engine'}{'handle'};
+}
+
+
+# Disconnect child from cache
+sub disconnect
+{
+	my $server = shift;
+
+}
+
+
+# Store keypair in cache
+# Parameters:
+# 		CacheName	- Name of cache we storing things in
+# 		Key			- Item key
+# 		Value		- Item value
+sub cacheStoreKeyPair
+{
+	my ($cacheName,$key,$value) = @_;
+
+
+	if (!defined($cacheName)) {
+		setError("Cache name not defined in store");
+		return -1;
+	}
+
+	if (!defined($key)) {
+		setError("Key not defined for cache '$cacheName' store");
+		return -1;
+	}
+
+	if (!defined($value)) {
+		setError("Value not defined for cache '$cacheName' key '$key' store");
+		return -1;
+	}
+
+	# If we're not caching just return
+	return 0 if ($cache_type eq 'none');
+	
+	# Store
+	$cache->set("$cacheName/$key",$value);
+
+	return 0;
+}
+
+
+# Get data from key in cache
+# Parameters:
+# 		CacheName	- Name of cache we storing things in
+# 		Key			- Item key
+sub cacheGetKeyPair
+{
+	my ($cacheName,$key) = @_;
+
+	
+	if (!defined($cacheName)) {
+		setError("Cache name not defined in get");
+		return (-1);
+	}
+
+	if (!defined($key)) {
+		setError("Key not defined for cache '$cacheName' get");
+		return (-1);
+	}
+
+	# If we're not caching just return
+	if ($cache_type eq 'none') {
+		return (0,undef);
+	}
+
+	# Check and count
+	my $res = $cache->get("$cacheName/$key");
+	if ($res) {
+		$cache->get_and_set('Cache/Stats/Hit',sub { return ++$_[1]; });
+	} else {
+		$cache->get_and_set('Cache/Stats/Miss',sub { return ++$_[1]; });
+	}
+
+	return (0,$res);
+}
+
+
+# Return cache hit ratio
+sub getHitRatio
+{
+	my $res;
+
+
+	# Get counter
+	$res = $cache->get('Cache/Stats/Hit');
+
+	return $res;
+}
+
+
+# Return cache miss ratio
+sub getMissRatio
+{
+	my $res;
+
+
+	# Get counter
+	$res = $cache->get('Cache/Stats/Miss');
+
+	return $res;
+}
+
+
+1;
+# vim: ts=4
diff --git a/smradius/dbilayer.pm b/smradius/dbilayer.pm
new file mode 100644
index 00000000..7a06fb55
--- /dev/null
+++ b/smradius/dbilayer.pm
@@ -0,0 +1,359 @@
+# Database independent layer module
+# Copyright (C) 2005-2007 Nigel Kukard  <nkukard@lbsd.net>
+# Copyright (C) 2008, LinuxRulz
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+
+
+package cbp::dbilayer;
+
+use strict;
+use warnings;
+
+
+use cbp::config;
+use DBI;
+
+
+
+my $internalError = "";
+
+
+sub internalErr
+{
+	my $error = $internalError;
+
+	$internalError = "";
+
+	return $error;
+}
+
+
+# Initialize class and return a fully connected object
+sub Init
+{
+	my $server = shift;
+	my $dbconfig = $server->{'cbp'}->{'database'};
+
+
+	# Check if we created
+	my $dbh = cbp::dbilayer->new($dbconfig->{'DSN'},$dbconfig->{'Username'},$dbconfig->{'Password'},$dbconfig->{'TablePrefix'});
+	return undef if (!defined($dbh));
+
+	return $dbh;
+}
+
+
+# Constructor
+sub new
+{
+	my ($class,$dsn,$username,$password,$table_prefix) = @_;
+
+	# Iternals
+	my $self = {
+		_dbh => undef,
+		_error => undef,
+
+		_dsn => undef,
+		_username => undef,
+		_password => undef,
+
+		_table_prefix => "",
+
+		_in_transaction => undef,
+	};
+
+	# Set database parameters	
+	if (defined($dsn)) {
+		$self->{_dsn} = $dsn;
+		$self->{_username} = $username;
+		$self->{_password} = $password;
+		$self->{_table_prefix} = $table_prefix if (defined($table_prefix) && $table_prefix ne "");
+	} else {
+		$internalError = "Invalid DSN given";
+		return undef;
+	}
+
+	# Create...
+	bless $self, $class;
+	return $self;
+}
+
+
+
+# Return current error message
+# Args: none
+sub Error
+{
+	my ($self) = @_;
+
+	my $err = $self->{_error};
+
+	# Reset error
+	$self->{_error} = "";
+
+	# Return error
+	return $err;
+}
+
+
+# Return connection to database
+# Args: none
+sub connect
+{
+	my ($self) = @_;
+
+
+	$self->{_dbh} = DBI->connect($self->{_dsn}, $self->{_username}, $self->{_password}, { 
+			'AutoCommit' => 1, 
+			'PrintError' => 0,
+			'FetchHashKeyName' => 'NAME_lc'
+	});
+
+	# Connect to database if we have to, check if we ok
+	if (!$self->{_dbh}) {
+		$self->{_error} = "Error connecting to database: $DBI::errstr";
+		return -1;
+	}
+
+	# Apon connect we are not in a transaction
+	$self->{_in_transaction} = 0;
+
+	return 0;
+}
+
+
+# Check database connection  
+# Args: none
+sub _check
+{
+	my $self = shift;
+
+
+	# If we not in a transaction try connect
+	if ($self->{_in_transaction} == 0) {
+		# Try ping
+		if (!$self->{_dbh}->ping()) {
+			# Disconnect & reconnect
+			$self->{_dbh}->disconnect();
+			$self->connect(); 
+		}
+	}
+}
+
+
+# Return database selection results...
+# Args: <select statement>
+sub select
+{
+	my ($self,$query,@params) = @_;
+
+
+	$self->_check();
+
+#	# Build single query instead of using binding of params
+#	# not all databases support binding, and not all support all
+#	# the places we use ?
+#	$query =~ s/\?/%s/g;
+#	# Map each element in params to the quoted value
+#	$query = sprintf($query,
+#		map { $self->quote($_) } @params
+#	);
+#use Data::Dumper; print STDERR Dumper($query);
+	# Prepare query
+	my $sth;
+	if (!($sth = $self->{_dbh}->prepare($query))) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+
+	# Check for execution error
+#	if (!$sth->execute()) {
+	if (!$sth->execute(@params)) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+
+	return $sth;
+}
+
+
+# Perform a command
+# Args: <command statement>
+sub do
+{
+	my ($self,$command,@params) = @_;
+
+
+	$self->_check();
+
+#	# Build single command instead of using binding of params
+#	# not all databases support binding, and not all support all
+#	# the places we use ?
+#	$command =~ s/\?/%s/g;
+#	# Map each element in params to the quoted value
+#	$command = sprintf($command,
+#		map { $self->quote($_) } @params
+#	);
+#use Data::Dumper; print STDERR Dumper($command);
+
+	# Prepare query
+	my $sth;
+#	if (!($sth = $self->{_dbh}->do($command))) {
+	if (!($sth = $self->{_dbh}->do($command,undef,@params))) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+
+	return $sth;
+}
+
+
+# Function to get last insert id
+# Args: <table> <column>
+sub lastInsertID
+{
+	my ($self,$table,$column) = @_;
+
+
+	# Get last insert id
+	my $res;
+	if (!($res = $self->{_dbh}->last_insert_id(undef,undef,$table,$column))) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+
+	return $res;
+}
+
+
+# Function to begin a transaction
+# Args: none
+sub begin
+{
+	my ($self) = @_;
+
+	$self->_check();
+	
+	$self->{_in_transaction}++;
+
+	# Don't really start transaction if we more than 1 deep
+	if ($self->{_in_transaction} > 1) {
+		return 1;
+	}
+
+	# Begin
+	my $res;
+	if (!($res = $self->{_dbh}->begin_work())) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+	
+	return $res;
+}
+
+
+# Function to commit a transaction
+# Args: none
+sub commit
+{
+	my ($self) = @_;
+
+	
+	# Reduce level
+	$self->{_in_transaction}--;
+
+	# If we not at top level, return success
+	if ($self->{_in_transaction} > 0) {
+		return 1;
+	}
+
+	# Reset transaction depth to 0
+	$self->{_in_transaction} = 0;
+
+	# Commit
+	my $res;
+	if (!($res = $self->{_dbh}->commit())) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+	
+	return $res;
+}
+
+
+# Function to rollback a transaction
+# Args: none
+sub rollback
+{
+	my ($self) = @_;
+
+
+	# If we at top level, return success
+	if ($self->{_in_transaction} < 1) {
+		return 1;
+	}
+	
+	$self->{_in_transaction} = 0;
+
+	# Rollback
+	my $res;
+	if (!($res = $self->{_dbh}->rollback())) {
+		$self->{_error} = $self->{_dbh}->errstr;
+		return undef;	
+	}
+	
+	return $res;
+}
+
+
+# Function to quote a database variable
+# Args: <stuff to quote>
+sub quote
+{
+	my ($self,$stuff) = @_;
+
+	return $self->{_dbh}->quote($stuff);
+}
+
+
+# Function to cleanup DB query
+# Args: <sth>
+sub free
+{
+	my ($self,$sth) = @_;
+
+
+	if ($sth) {
+		$sth->finish();
+	}	
+}
+
+
+# Function to return the table prefix
+sub table_prefix
+{
+	my $self = shift;
+
+	return $self->{_table_prefix};
+}
+
+
+
+
+1;
+# vim: ts=4
diff --git a/smradius/dblayer.pm b/smradius/dblayer.pm
new file mode 100644
index 00000000..efd910f7
--- /dev/null
+++ b/smradius/dblayer.pm
@@ -0,0 +1,292 @@
+# Common database layer module
+# Copyright (C) 2005-2007 Nigel Kukard  <nkukard@lbsd.net>
+# Copyright (C) 2008, LinuxRulz
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+
+package cbp::dblayer;
+
+use strict;
+use warnings;
+
+# Exporter stuff
+require Exporter;
+our (@ISA,@EXPORT);
+@ISA = qw(Exporter);
+@EXPORT = qw(
+	DBConnect
+	DBSelect
+	DBDo
+	DBLastInsertID
+	DBBegin
+	DBCommit
+	DBRollback
+	DBQuote
+	DBFreeRes
+	
+	DBSelectNumResults
+
+	hashifyLCtoMC
+);
+
+
+
+use cbp::config;
+
+use cbp::dbilayer;
+
+
+# Database handle
+my $dbh = undef;
+
+# Our current error message
+my $error = "";
+
+# Set current error message
+# Args: error_message
+sub setError
+{
+	my $err = shift;
+	my ($package,$filename,$line) = caller;
+	my (undef,undef,undef,$subroutine) = caller(1);
+
+	# Set error
+	$error = "$subroutine($line): $err";
+}
+
+# Return current error message
+# Args: none
+sub Error
+{
+	my $err = $error;
+
+	# Reset error
+	$error = "";
+
+	# Return error
+	return $err;
+}
+
+
+
+# Initialize database handle
+# Args: <database handle>
+sub setHandle
+{
+		my $handle = shift;
+
+		$dbh = $handle;
+}
+
+
+# Return database selection results...
+# Args: <select statement>
+sub DBSelect
+{
+	my ($query,@params) = @_;
+
+
+	my $table_prefix = $dbh->table_prefix();
+
+	# Replace table prefix macro
+	$query =~ s/\@TP\@/$table_prefix/g;
+
+	# Prepare query
+	my $sth;
+	if (!($sth = $dbh->select($query,@params))) {
+		setError("Error executing select '$query': ".$dbh->Error());
+		return undef;	
+	}
+
+	return $sth;
+}
+
+
+# Perform a command
+# Args: <command statement>
+sub DBDo
+{
+	my ($command,@params) = @_;
+
+
+	my $table_prefix = $dbh->table_prefix();
+
+	# Replace table prefix macro
+	$command =~ s/\@TP\@/$table_prefix/g;
+
+	# Prepare query
+	my $sth;
+	if (!($sth = $dbh->do($command,@params))) {
+		setError("Error executing command '$command': ".$dbh->Error());
+		return undef;	
+	}
+
+	return $sth;
+}
+
+
+# Function to get last insert id
+# Args: <table> <column>
+sub DBLastInsertID
+{
+	my ($table,$column) = @_;
+
+
+	my $res;
+	if (!($res = $dbh->lastInsertID(undef,undef,$table,$column))) {
+		setError("Error getting last inserted id: ".$dbh->Error());
+		return undef;	
+	}
+
+	return $res;
+}
+
+
+# Function to begin a transaction
+# Args: none
+sub DBBegin
+{
+	my $res;
+	if (!($res = $dbh->begin())) {
+		setError("Error beginning transaction: ".$dbh->Error());
+		return undef;	
+	}
+
+	return $res;
+}
+
+
+# Function to commit a transaction
+# Args: none
+sub DBCommit
+{
+	my $res;
+	if (!($res = $dbh->commit())) {
+		setError("Error committing transaction: ".$dbh->Error());
+		return undef;	
+	}
+
+	return $res;
+}
+
+
+# Function to rollback a transaction
+# Args: none
+sub DBRollback
+{
+	my $res;
+	if (!($res = $dbh->rollback())) {
+		setError("Error rolling back transaction: ".$dbh->Error());
+		return undef;	
+	}
+
+	return $res;
+}
+
+
+# Function to quote a database variable
+# Args: <stuff to quote>
+sub DBQuote
+{
+	my $stuff = shift;
+
+
+	return $dbh->quote($stuff);
+}
+
+
+# Function to cleanup DB query
+# Args: <sth>
+sub DBFreeRes
+{
+	my $sth = shift;
+
+
+	if ($sth) {
+		$sth->finish();
+	}	
+}
+
+
+
+#
+# Value Added Functions
+#
+
+
+# Function to get table prefix
+sub DBTablePrefix
+{
+	return $dbh->table_prefix();
+}
+
+
+
+# Return how many results came up from the specific SELECT query
+# Args: <select statement>
+sub DBSelectNumResults
+{
+	my $query = shift;
+
+
+	# Prepare query
+	my $sth;
+	if (!($sth = $dbh->select("SELECT COUNT(*) AS num_results $query"))) {
+		setError("Error executing select: ".$dbh->Error());
+		return undef;	
+	}
+
+	# Grab row
+	my $row = $sth->fetchrow_hashref();
+	if (!defined($row)) {
+		setError("Failed to get results from a select: ".$dbh->Error());
+		return undef;
+	}	
+
+	# Pull number
+	my $num_results = $row->{'num_results'};
+	$sth->finish();
+
+	return $num_results;
+}
+
+
+# Convert a lower case array to mixed case
+sub hashifyLCtoMC
+{
+	my ($record,@entries) = @_;
+
+
+	# If we undefined, return
+	return undef if (!defined($record));
+
+	my $res;
+
+	# Loop with each item, assign from lowecase database record to our result
+	foreach my $entry (@entries) {
+		$res->{$entry} = $record->{lc($entry)};
+	}
+
+	return $res;
+}
+
+
+
+
+
+1;
+# vim: ts=4
-- 
GitLab