#!/usr/bin/perl
use lib '/home/erealms/ethereal/mgmt/perl';

#################################################################################
# Created       : Martin Foster
# Modified      : 18-Mar-2007
#################################################################################
#
# System Panel - Harmonized script designed to behave as a control panel
# Copyright (C) 2000-2007  Martin Foster
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# Author of this script can be contacted at the following:
#       E-Mail  : martin@ethereal-realms.org
#       Address : 4-3526 Wolfe Cres
#                 Halifax, Nova Scotia
#                 B3L 3S2
#
#################################################################################

use CGI qw(-no_debug -nosticky);				# Common gateway interface
use CGI::Carp qw(fatalsToBrowser);				# CGI Error logs
use DB_File;							# BerkeleyDB
use Digest::MD5 qw(md5_hex);					# Digest handling
use XML::RSS;							# RSS feed generation
use strict;							# Strict variable enforcement

use Ethereal::Audit;						# Audit trail
use Ethereal::Database;						# Database handler
use Ethereal::Filter;						# Filter handler
use Ethereal::Geo;						# Geographical handler
use Ethereal::Home;						# Homepage handler
use Ethereal::Login;						# Login functionality
use Ethereal::Mail;						# Mail handler
use Ethereal::Menu;						# Consistent menu
use Ethereal::Param;						# Parameter control
use Ethereal::Spacing;						# Spacing handler
use Ethereal::Template;						# Template handler

#################################################################################
# Gobal override
#################################################################################
$CGI::POST_MAX=1024 * 350;					# Maximum posts
$CGI::DISABLE_UPLOADS = 1;					# Disable uploads

#################################################################################
# Data Members
#################################################################################
my $audit;							# Auditing information
my $cgi;							# Common gateway interface handle
my $database;							# Database handle
my $login;							# Login handle
my $menu;							# Menu handle
my $param;							# Parameter handle
my $space;							# Spacing handler
my $tmpl;							# Template name

my $address;							# Script address
my $inline;							# Inline address
my $links;							# Navigation links

my $purl;							# Pass url
my $surl;							# Link address
my $sparam;							# Script parameters

my @args;							# Arguments to pass

my %admin;							# Administrative hash hash
my %scripts;							# Script addresses and names


#################################################################################
# Program Area
#################################################################################

	# Initial handle
	$cgi      = new CGI;
	$database = new Ethereal::Database();

	# Retrieve parameters
	my @allow = $cgi->param('ALLOW');
	my @block = $cgi->param('BLOCK');
	my @clear = $cgi->param('CLEAR');
	my @level = $cgi->param('ALEVEL');
	my @ops   = $cgi->param('AOPS');

	# Disabled shared memory
	$database->{'ADMN'} = 'true';
	
	# Connect and fetch
	$database->Connect($cgi);

	# Set title
	$database->{'TITLE'} = $database->{'SYS'}{'TitAdmin'};



	# Link with has
	$database->GetHashAdmin(\%admin);


	#
	# Parameter handling
	$param    = new Ethereal::Param($database, $cgi);

	# Pull parameters
	$param->GetParam();


	# Create Instances
	$audit    = new Ethereal::Audit($database, $cgi, $param);
	$menu     = new Ethereal::Menu($database, $param);
	$login    = new Ethereal::Login($database, $cgi, $param, $menu);
	$tmpl     = new Ethereal::Template(\%admin);
	$space    = new Ethereal::Spacing($admin{'SetLimit'});


	#
	# Enable custom header and footer
	$login->{'HEADER'} = $database->DocumentGetAdminHeader();
	$login->{'FOOTER'} = $database->DocumentGetAdminFooter();
	
	# Authenthication
	if ($login->GetVerificationAdmin())
	{
		# Display header
		print $menu->Rewrite($login->{'HEADER'}), "\n";

		# Retrieve script
		$surl = $cgi->url(-absolute=>1);

		# Fetch and format
		$sparam =  $cgi->path_info();
		$sparam =~ s/^(\/)(.*)(\/)?$/$2/;


		# Passage url
		$purl = $surl . '/' . $sparam;
		
		
		# Generate widget
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'}
		 );


		# Menu control
		# Establish sections
		%scripts = (
		  $admin{'TagMnuAccess'}    => 'access',
		  $admin{'TagMnuAttribute'} => 'attrib',
		  $admin{'TagMnuAudit'}     => 'audit',
		  $admin{'TagMnuContact'}   => 'contact',
		  $admin{'TagMnuFAQ'}       => 'faq',
		  $admin{'TagMnuGenre'}     => 'genre',
		  $admin{'TagMnuTemplate'}  => 'document',
		  $admin{'TagMnuRealm'}     => 'realm',
		  $admin{'TagMnuRotate'}    => 'rotate',
		  $admin{'TagMnuUser'}      => 'system',
		  $admin{'TagMnuWeblog'}    => 'weblog'
		 );	

		# Initialize
		$links = '';

		# Loop
		foreach $address (sort(keys(%scripts)))
		{
			# Spacer requirements
			$links .= $space->Get($address);

			# Display
			$links .= ($sparam =~ /^$scripts{$address}/)
			  ? $tmpl->Pass('TagMenu',
			      MLINK => $surl . '/' . $scripts{$address} . $inline,
			      MNAME => $address)
			  : $tmpl->Pass('TagMenuView',
			      MLINK => $surl . '/' . $scripts{$address} . $inline,
			      MNAME => $address);
		}


		# 
		# Generate info to pass
		push(@args, $database, $cgi, $param, $tmpl, $audit, \%admin, $links, $purl);


		# Sections
		# Access administration
		if ($sparam =~ /^access/i)
		{
			AccessControl(@args, \@allow, \@block);
		}

		# Attribute administrator
		elsif ($sparam =~ /^attrib/i)
		{
			# List data
			DataCycler(@args);
		}

		# Auditing access
		elsif ($sparam =~ /^audit/i)
		{
			# Display something
			if (defined($param->{'AUDITING'}))
			{
				# Display information
				AuditHandle(@args, \@ops, \@level);
			}

			else
			{
				# List data
				AuditSelect(@args);
			}
		}

		# Contact elements
		elsif ($sparam =~ /^contact/i)
		{
			# Handler
			ContactHandler(@args);
		}

		# Document administration
		elsif ($sparam =~ /^document/i)
		{
			# Handles the ins and outs of full templates
			DocumentHandler(@args);
		}

		# Frequently Asked Questions (FAQ)
		elsif ($sparam =~ /^faq/i)
		{
			# Detailed view
			if (defined($param->{'FAQID'}))
			{
				# Handling of questions themselves
				FaqQuestion(@args);
			}

			# General overview
			else
			{
				# General Listing
				FaqListing(@args);
			}
		}

		# Genre elements
		elsif ($sparam =~ /^genre/i)
		{
			# Genre handler
			GenreHandler(@args);
		}

		# Realm administration
		elsif ($sparam =~ /^realm/i)
		{
			# Realm additions
			if (defined($param->{'ADDITION'}))
			{
				RealmAddition(@args);
			}

			# Realms displays
			else
			{
				# Defaults to public if nothing specified
				RealmDisplay(@args);
			}
		}


		# Banner and HTML rotation
		elsif ($sparam =~ /^rotate/i)
		{
			# Rotation summary
			RotateHandler(@args);
		}


		# Weblog administration
		elsif ($sparam =~ /^weblog/i)
		{
			# Weblog handling system
			WeblogHandler(@args, \@clear);
		}


		# User administration
		else
		{
			# Handles the primary modifications of a user
			if (defined($param->{'LOGIN'}))
			{
				UserModify(@args);
			}

			# Basic search with user selection and removal
			elsif (defined($param->{'SEARCH'}))
			{
				UserSelect(@args);
			}

			# Basic Prompt
			else
			{ 
				UserSearch(@args);
			}
		}
		
		# Display footer
		print $login->{'FOOTER'}, "\n";
	}


#################################################################################
# Sub-Routines
#################################################################################


#####################
# Access Control
#
# Access Control Lists is essentially the role of this script.  This specific 
# subroutine is aimed speficially at handling who can access which Realm
# and perform supervisory functions.

sub AccessControl
{
	#####################
	# Data Members

	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $template = shift;					# Template handling
	my $audit    = shift;					# Audit handle
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# List of links
	my $url      = shift;					# Self referencing link

	my $allow    = shift;					# Access granted list
	my $block    = shift;					# Access denied list

	my $handle;						# Handle name
	my $inline;						# Inline parameter list
	my $instance;						# Instances
	my $room;						# Singular room
	my $super    = '16';					# Supervisory access

	my $main     = '';					# Main global template
	my $realms   = '';					# Realms listing
	my $access   = '';					# Access control list

	my @rooms;						# List of rooms
	my @allow;						# Allowed members
	my @block;						# Blocked members


	#####################
	# Program Files


	# Realm selection template
	# Pull list
	$database->GetListNonPrivate(\@rooms);

	# Loop and append to template
	foreach $room (@rooms)
	{
		# Generate inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			ROOM  => $room
		 );

		# Append to template
		$realms .= $tmpl->Pass('TagListRealms',
			MNAME => $room,
			MLINK => $url . $inline
		 );
	}


	# Safety Check
	# Room has been selected
	if (defined($param->{'ROOM'}))
	{
		# Data handling 
		# Evaluation block
		eval
 		{
			# Additions
			if (defined($param->{$admin{'TxtMoveIn'}}))
			{
				# Special additions
				if ((defined($param->{'NALLOW'})) 
				 && (length($param->{'NALLOW'}) > 2))
				{
					# Determine validity
					($param->{'NALLOW'}) = $database->DataGetPuppeteerExistence($param->{'NALLOW'});
	
					# If user exists
					if (defined($param->{'NALLOW'}))
					{
						# Adjust privs
						$database->Write("UPDATE Puppeteer
							SET   PuppeteerSuperPrivs='yes'
							WHERE PuppeteerLogin=?",
	
						 $param->{'NALLOW'}
						);
	
						# Add to be added normally
						unshift(@{$block}, $param->{'NALLOW'});
					} 
				}
	
				# Handle addition
				foreach $handle (@{$block})
				{
					# Insert Entry
					$database->Write("INSERT INTO RealmSupervisor 
						(RealmName,
						 PuppeteerLogin,
						 RealmACL)
						VALUES (?,?,?)",
	
					 $param->{'ROOM'},
					 $handle,
					 $super
					);
	
					# Audit table
					$audit->AccessAdd($handle);	
	
	
					# Commit
					$database->Commit();
				}
			}
	
			# Removal
			elsif (defined($param->{$admin{'TxtMoveOut'}}))
			{
				# Cycle through
				foreach $handle (@{$allow})
				{
					# Determin amount of instances
					($instance) = $database->DataGet("SELECT COUNT(*)
						FROM RealmSupervisor
						WHERE PuppeteerLogin=?
					  AND RealmACL=?", 
						  
					 $handle,
				 	 $super);
	
					# Remove priviliges to all those that exist once
					if ((defined($instance))
					 && ($instance == 1))
					{
						$database->Write("UPDATE Puppeteer
							SET   PuppeteerSuperPrivs='no'
							WHERE PuppeteerLogin=?",
	
						 $handle
						);
					}

	
					# Remove data
					$database->Write("DELETE FROM RealmSupervisor
						WHERE PuppeteerLogin=?
						  AND RealmName=?",
	
					 $handle,
					 $param->{'ROOM'}
					);
	
					# Audit table
					$audit->AccessRem($handle);	
	
	
					# Commit
					$database->Commit();
				}
			}
	
		# Commit
		$database->Commit();
		};
	
		# If block failed
		if ($@)
		{
			# Issue warning
			warn ("Transaction aborted: $@");
	
			# Rollback
			$database->Rollback();
		}
		
	
		# Data retreival
		# Retrieve variables
		$database->GetList(\@block, "SELECT Puppeteer.PuppeteerLogin 
			FROM  Puppeteer 
			WHERE Puppeteer.PuppeteerSuperPrivs='yes'
			  AND NOT EXISTS (SELECT RealmSupervisor.PuppeteerLogin
				FROM RealmSupervisor
				WHERE RealmSupervisor.RealmName=?
				  AND RealmSupervisor.RealmACL=?
				  AND RealmSupervisor.PuppeteerLogin=Puppeteer.PuppeteerLogin) 
				ORDER BY Puppeteer.PuppeteerLogin", 
			
			 $param->{'ROOM'},
		 	 $super
		 	); 
	
		# Realm specific
		$database->GetList(\@allow, "SELECT PuppeteerLogin 
			FROM  RealmSupervisor
			WHERE RealmName=?
			  AND RealmACL=?
			ORDER BY PuppeteerLogin", 
			
		 $param->{'ROOM'},
		 $super
 		);
	
		# Generate inline
		($inline) = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'}
		 ); 
	
	
		# Generate widget
		my $wblock  = $cgi->scrolling_list('BLOCK', \@block, $block[0], '5', 'true');
		my $wallow  = $cgi->scrolling_list('ALLOW', \@allow, $allow[0], '5', 'true');
		my $wcntrl  = $cgi->submit($admin->{'TxtMoveIn'}) . "<br><br>" . $cgi->submit($admin->{'TxtMoveOut'});
	
		# Generate template
		$access = $tmpl->Pass('TmplAccessRealm',
			MROOM   => $param->{'ROOM'},
			WBLOCK  => $wblock,
			WALLOW  => $wallow,
			WCNTRL  => $wcntrl,
			WMANUAL => $cgi->textfield('NALLOW', '', 15, 10)
		 );	
	}

	#
	# No realm selected at the moment
	else
	{
		# Just a quick warning
		$access = $tmpl->Pass('TmplAccessNone');
	}


	# Form display
	# Form start
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat()), "\n";

	# Show template
	$tmpl->Show('TmplAccess',
		MACCESS  => $access,
		MLINKS   => $links,
		LSTREALM => $realms
	 );

	# Form end
	print $cgi->end_form(), "\n";
}
	
	
#####################
# Audit viewer
#
# This exists to allow adminisrators to look at the auditing table and see
# activities that have been done by users which require to be logged.

sub AuditHandle
{
	#####################
	# Data Members

	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handling
	my $audit    = shift;					# Auditing table
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links to other sections
	my $url      = shift;					# Self referencing link
	my $ops      = shift;					# Operations list
	my $level    = shift;					# Realm of influence

	my $action;						# AND or OR
	my $realm;						# Realm of influence query
	my $req;						# Request for information
	my $ver;						# Verification of rows

	my $offset;						# Offset for database
	my $pos;						# Position
	my $limit    = 200;					# Limiting factor

	my $res;						# Results set
	my $statement;						# Query handle

	my $back;						# Back button
	my $nav;						# Nav bar
	my $entries;						# Entries

	my $err;						# Proper error message
	my $bgcolor;						# Background colour
	my $single;						# Single value

	my $count;						# Counter
	my $rows;						# Rows found

	my $inline;						# Inline parameter list
	my $append  = '';					# Additional information for continuity

	my @pass;						# Passed valued
	my %err;						# Error hash
 
	#####################
	# Program area

	# Link with has
	$database->GetHashErr(\%err);


	# Ugly as hell hack to get page support
	# Loop through Operations
	foreach $single (@{$ops})
	{
		# Throw it in
		$append .= '&AOPS=' . $single;
	}

	# Levels
	foreach $single (@{$level})
	{
		# Throw it all in
		$append .= '&ALEVEL=' . $single;
	}



	# Build query
	# Initial step
	$req = "SELECT"
	  . " PuppeteerLogin    AS \"PuppeteerLogin\","
	  . " PuppeteerHostname AS \"PuppeteerHostname\","
	  . " AuditAction       AS \"AuditAction\","
	  . " AuditCode         AS \"AuditCode\","
	  . " AuditAdditional   AS \"AuditAdditional\","
	  . " AuditRealm        AS \"AuditRealm\","
	  . " getDateTime(AuditTimestamp)  AS \"AuditTimestamp\""
	  . "FROM Audit";

	# Verification of rows
	$ver = "SELECT COUNT(*) FROM Audit";


	# Operations
	# Figure out positions needed
	$count = @{$ops};
	$single = "?," x $count;
	chop($single);


	# Add in
	$req .= " WHERE AuditAction IN ($single) ";
	$ver .= " WHERE AuditAction IN ($single) ";

	# Cycle and add
	foreach $single (@{$ops}) { push(@pass, lc($single)); }


	# Realms of influence
	# Needed
	unless ((defined($param->{'ACODE'}))
	  && ($param->{'ACODE'} =~ /^\d{4}$/))
	{
		# Initialize
		$realm = '';

		# Only do following if no code specified
		foreach $single (@{$level})
		{
			# Query handler
			$action = (defined($action)) ? 'OR' : '';


			# Administrative
			if (lc($single) eq 'admin') 
			{
				# Append
				$realm .= $action . " (AuditCode >= ? AND AuditCode <= ?) ";

				# Push
				push(@pass, 9000, 9999);
			}

			# Supervisory
			elsif (lc($single) eq 'super') 
			{
				# Append
				$realm .= $action . " (AuditCode >= ? AND AuditCode <= ?) ";

				# Push
				push(@pass, 2000, 2999);
			}

			# User level
			elsif (lc($single) eq 'user') 
			{
				# Append
				$realm .= $action . "  (AuditCode >= ? AND AuditCode <= ?) ";

				# Push
				push(@pass, 1000, 1999);
			}
		}

		# Final append
		$req .= " AND (" . $realm . ") " if (length($realm) > 5);
		$ver .= " AND (" . $realm . ") " if (length($realm) > 5);
	}


	# Restrictions
	# Code
	if ((defined($param->{'ACODE'}))
	  && ($param->{'ACODE'} =~ /^\d{4}$/))
	{
		# Append
		$req .= " AND AuditCode=? ";	
		$ver .= " AND AuditCode=? ";	

		# Push onto array
		push(@pass, $param->{'ACODE'});
	} 

	# User
	if ((defined($param->{'AUSER'}))
	  && (length($param->{'AUSER'}) > 2))
	{
		# Append
		$req .= " AND (LOWER(PuppeteerLogin)=? OR LOWER(AuditAdditional)=?) ";	
		$ver .= " AND (LOWER(PuppeteerLogin)=? OR LOWER(AuditAdditional)=?) ";	

		# Push onto array
		push(@pass, lc($param->{'AUSER'}), lc($param->{'AUSER'}));
	} 

	# Realm restriction
	if ((defined($param->{'AROOM'}))
	  && (length($param->{'AROOM'}) > 2))
	{
		# Append
		$req .= " AND (LOWER(AuditRealm)=? OR LOWER(AuditAdditional)=?) ";	
		$ver .= " AND (LOWER(AuditRealm)=? OR LOWER(AuditAdditional)=?) ";	

		# Push onto array
		push(@pass, lc($param->{'AROOM'}), lc($param->{'AROOM'}));
	} 

	# Time handling
	# Add to search
	$req .= " AND AuditTimestamp > ? AND AuditTimestamp < ? ORDER BY AuditTimestamp DESC ";
	$ver .= " AND AuditTimestamp > ? AND AuditTimestamp < ? ";

	# Push onto array
	push(@pass, "$param->{ASTART} 00:00:00", "$param->{ASTOP} 23:59:59");

	
	# Check for necessary data
	# Narrow down the amount of rows required
	($rows) = $database->DataGet($ver, @pass);

	# Necessary amount there
	if ($rows > 0)
	{
		# Record attempt
		$audit->AccessAudit($param->{'USER'});


		# Position based handling
		# Adjusting
		$pos = $param->{'PAGE'};

		# Determine position
		$pos = ((defined($pos)) && ($pos =~ /^\d+$/))
		  ? $pos
		  : 1;

		# Determine offset
		$offset = ($pos - 1) * $limit;



		# Back button
		# Generate inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'}
		 );

		# Generate
		$back = $tmpl->Pass('TagNav',
			MNAME => $admin->{'TxtBack'},
			MLINK => $url . $inline
		 );


		# Navigation menu
		# Generate inline
		$inline = $param->EmbedInline($param->Flat());

		# Generate template
		$nav = Navigation($tmpl, $admin, $limit, $rows, $offset, $url, $inline . $append);


		#
		# Pass through query
		$database->Pull(\$statement, $req . "LIMIT $limit OFFSET $offset", @pass);

		# Initialize 
		$count   = 0;
		$entries = '';

		# Cycle and list
		while ($res = $statement->fetchrow_hashref())
		{
			# Colour handling
			# Determin colour of row
			$bgcolor = (($count % 2) != 0) ? "ROWPRI" : "ROWSEC";
			
			# Increment	
			$count++;
	
	
			# Format error
			$err =  $err{$res->{'AuditCode'}};
			$err =~ s/MCODE/$res->{'AuditAdditional'}/gs;
	
			# Verify
			$err = $res->{'AuditCode'} unless (defined($err));
	
	
			# Append to template
			$entries .= $tmpl->Pass('TmplAuditList',
				MCOLOR  => $bgcolor,
				MLOGIN  => $res->{'PuppeteerLogin'},
				MADDY   => $res->{'PuppeteerHostname'},
				MROOM   => $res->{'AuditRealm'},
				MACTION => $res->{'AuditAction'},
				MEVENT  => $err,
				MTIME   => $res->{'AuditTimestamp'}
			 );
		}
	
		# Finish
		$statement->finish();


		# Display template
		$tmpl->Show('TmplAudit',
			LSTAUDIT => $entries,
			LSTNAV   => $back . $nav
		 );
	}
}


#####################
# Audit request
#
# This allows administrators to make request to view the auditing information
# basically a one way form with simple questions to narrow things down.

sub AuditSelect
{
	#####################
	# Data Members

	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handling
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links section

	my $end;						# End earch time
	my $start    = '01-Jan-2000';				# Start time

	my $level;						# Formatted widget
	my $op;							# Operations widget

	my @levels   = qw(Admin Super User);
	my @ops      = qw(Action Auth Delete Insert Update);


	#####################
	# Program area

	# Format widgets
	# Levels of control
	$level = $cgi->checkbox_group(
		-name    => 'ALEVEL',
		-values  => \@levels,
		-default => \@levels,
		-columns => 1
	 );

	# Operations
	$op = $cgi->checkbox_group(
		-name    => 'AOPS',
		-values  => \@ops,
		-default => \@ops,
		-columns => 1
	 );

	# Capture date
	($end) = $database->DataGetDate();
	


	# HTML
	# Form
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat(), AUDITING => 'True'), "\n";

	# Display template
	$tmpl->Show('TmplAuditSearch',
		MLINKS  => $links,
		WOPS    => $op,
		WLVL    => $level,
		WCODE   => $cgi->textfield('ACODE',  '', 4, 4),
		WUSER   => $cgi->textfield('AUSER',  '', 20, 10),
		WROOM   => $cgi->textfield('AROOM',  '', 20, 30),
		WSTART  => $cgi->textfield('ASTART', $start, 15, 11),
		WSTOP   => $cgi->textfield('ASTOP',  $end, 15, 11),
		WSEARCH => $cgi->submit($admin->{'TxtSearch'})
	 );

	# End form
	print $cgi->end_form(), "\n";
}


#####################
# Contact Handler
#
# Designed to show the administrator all contact information that can be gathered
# and allow for it to be removed.

sub ContactHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Auditing code
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Menu links

	my $statement;						# Database statement

	my $contact;						# Contact handle
	my $filter;						# Filter widget
	my $name;						# Contact name
	my $type;						# Contact type

	my $listing;						# Listing template

	my @filter   = qw(None Email HTTP Numeric);		# Filter types


	#####################
	# Program area

	# Initial assigment
	$type = 'NONE';

	# Filter types
	$filter = $cgi->radio_group('FILTER', \@filter, $filter[0], 'true');


	# Add entry
	if (defined($param->{$admin->{'TxtCreate'}}))
	{
		# Insertion
		if ((defined($param->{'CONTACT'}))
		 && (length($param->{'CONTACT'}) > 2))
		{
			# Assignment
			$param->{'TYPE'} = (length($param->{'TYPE'}) > 2)
			  ? $param->{'TYPE'} 
			  : undef;

			# Make sure this works
			eval
			{
				# Carry forth changes
				$database->Write("INSERT INTO Contact
					(ContactName,
					 ContactType,
					 ContactFilter)
					VALUES(?,?,?)",

				 $param->{'CONTACT'},
				 lc($param->{'TYPE'}),
				 lc($param->{'FILTER'})
				);

				# Audit entry
				$audit->ContactAdd($param->{'CONTACT'});


				# Commit changes
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->Rollback();

			}
		}
	}

	# Initialize
	$listing = '';


	# Display handles
	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		 ContactName   AS \"ContactName\",
		 ContactType   AS \"ContactType\",
		 ContactFilter AS \"ContactFilter\"
		FROM Contact
		ORDER BY ContactType, ContactName");


	# Loop and display
	while ($contact = $statement->fetchrow_hashref())
	{
		# Name assignment
		$name =  $contact->{'ContactName'};
		$name =~ s/\W//s;


		# Removal
		if (defined($param->{$name}))
		{
			# Handle operation
			eval
			{
				# Remove
				$database->Write("DELETE FROM Contact
					WHERE ContactName=?",
				 $contact->{'ContactName'});

				# Audit entry
				$audit->ContactRem($contact->{'ContactName'});


				# Commit
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->rollback;
			}


			# Cleanup
			$param->Cleanup($name);
		}

		# Display
		else
		{
			# Type display
			# Ensure assignment
			$contact->{'ContactType'} = (defined($contact->{'ContactType'}))
			  ? $contact->{'ContactType'} 
			  : 'none';

			# Determine need
			if ((!defined($type))
			 || (uc($contact->{'ContactType'}) ne uc($type)))
			{
				# Additional section
				$listing .= $tmpl->Pass('TmplContactSect',
					MNAME => ucfirst($contact->{'ContactType'})
				 );

				# Assign type
				$type = $contact->{'ContactType'};
			}	

			# Add additional entry
			$listing .= $tmpl->Pass('TmplContactListing',
				MNAME   => $contact->{'ContactName'},
				MFILTER => $contact->{'ContactFilter'},
				WSELECT => $cgi->checkbox(-name=>$name, -label=>'')
			 );
		}
	}

	# Display of information
	# Start form
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat());

	# Display template
	$tmpl->Show('TmplContact',
		MLINKS     => $links,
		LSTCONTACT => $listing,
		WCREATE    => $cgi->submit($admin->{'TxtCreate'}),
		WDELETE    => $cgi->submit($admin->{'TxtDelete'}),
		WFILTER    => $filter,
		WNAME      => $cgi->textfield('CONTACT', '', 15, 25),
		WTYPE      => $cgi->textfield('TYPE', '', 10, 10)
	 );

	# Finish up
	print $cgi->end_form();
}


#####################
# Data Cycler
#
# A simple system designed to make the system more adaptable to new sections 
# being added to the system.   

sub DataCycler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Audit entry
	my $admin    = shift;					# Administrative script
	my $links    = shift;					# List of sections
	my $url      = shift;					# Self referencing link

	my $inline;						# Inline parameters
	my $section;						# Singular section

	my $listing;						# Listing of attributes
	my $menu     = '';					# Nav to other sections

	my $def      = 'system';				# Default setting
	my $type     = 'template';				# Type to avoid

	my @sections;						# List of sections


	#####################
	# Program area

	# Retreive sections
	$database->GetList(\@sections, "SELECT TagType
		FROM Tag
		WHERE TagType <> ?
		GROUP BY TagType
		ORDER BY TagType", 

	 $type
	);

	# Loop and show
	foreach $section (sort @sections)
	{
		# Create inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			SECT  => $section);	

		# Display entry
		$menu .= $tmpl->Pass('TagListAttributes',
			MLINK => $url . $inline, 
			MNAME => ucfirst($section)
		  );
	}

	# Always have a section
	$section = (defined($param->{'SECT'}))
	  ? $param->{'SECT'}
	  : $sections[0];


	# Handle section
	$listing = DataHandler($database, $cgi, $param, $tmpl, $admin, $section);


	# Information display
	# Form start
	print $cgi->start_form();
	print $param->EmbedNormal(
		USER  => $param->{'USER'},
		CRYPT => $param->{'CRYPT'},
		SECT  => $section
	 );

	# Display template
	$tmpl->Show('TmplAttrib',
		MLINKS    => $links,
		MSECT     => ucfirst($section),
		WUPDATE   => $cgi->submit($admin->{'TxtAlter'}),
		WRESET    => $cgi->reset($admin->{'TxtReset'}),
		LSTMENU   => $menu,
		LSTATTRIB => $listing				# Must remain last
	 );

	# Form end
	print $cgi->end_form(), "\n";
}


#####################
# Data Handler
#
# Takes in a CGI handle and the name of the hash to activate and displays 
# information within those hashes.   If changes, will make the change neccesary.

sub DataHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative script
	my $dbm	     = shift;					# Hash being used

	my $file     = $database->{'DBHASH'};			# Path location for BerkeleyDB
	my $search   = $database->{'SEARCH'};			# Search path

	my $listing  = '';					# Completed template list

	my $audit;						# Audit handle
	my $choice;						# Current selection
	my $data;						# Data of hash
	my $key;						# Arbitrary key
	my $value;						# Arbitrary value

	my %copy;						# Used hash
	my %hash;						# Arbitrary hash


	#####################
	# Program area

	# Audit handle
	$audit = new Ethereal::Audit($database, $cgi, $param);

	# Link with hash
	$database->GetHash(\%hash, $dbm);


	# Loop through and display information
	foreach $choice (sort keys %hash)
	{
		# If diffrent change
		if ((defined($param->{"$dbm$choice"}))
		 && ($param->{"$dbm$choice"} ne $hash{$choice})) 
		{
			eval
			{
				# Pull data from form
				$data = $param->{"$dbm$choice"};


				# Update values
				$hash{$choice} = $data;
	
				# Update value
				$database->Write("UPDATE Tag
					SET TagData=?
					WHERE TagName=?
					  AND TagType=?",

				 $data,
				 $choice,
				 $dbm
				);


				# Display fact of change
				# Templates
				if ($choice =~ /^Tmpl/)
				{
					# Template management
					$listing .= $tmpl->Pass('TmplAttribTextarea',
						MCHNG => $admin->{'TagChangeYes'},
						MNAME => $choice,
						WDATA => $cgi->textarea(
							-name    => $dbm . $choice,
							-rows    => 10,
							-columns => 65,
							-wrap    => 'OFF',
							-default => $data)
					 );
				}

				# Standard
				else
				{
					# Append to template
					$listing .= $tmpl->Pass('TmplAttribTextbox',
						MCHNG => $admin->{'TagChangeYes'},
						MNAME => $choice,
						WDATA => $cgi->textfield($dbm . $choice, $data, 50, 1000)
					 );
				}


				# Audit Trail
				$audit->TagChange($choice);


				# Commit changes
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->Rollback();
			}
		}
			
		# Else display as no one really cares
		else
		{
			# Non-changed information
			# Templates
			if ($choice =~ /^Tmpl/)
			{
				# Template management
				$listing .= $tmpl->Pass('TmplAttribTextarea',
					MCHNG => $admin->{'TagChangeNil'},
					MNAME => $choice,
					WDATA => $cgi->textarea(
						-name    => $dbm . $choice,
						-rows    => 10,
						-columns => 65,
						-wrap    => 'OFF',
						-default => $hash{$choice})
				 );
			}

			# Standard
			else
			{
				# Append to template
				$listing .= $tmpl->Pass('TmplAttribTextbox',
					MCHNG => $admin->{'TagChangeNil'},
					MNAME => $choice,
					WDATA => $cgi->textfield($dbm . $choice, $hash{$choice}, 50, 1000)
				 );
			}
		}
	}

	# Update of BerkelyDB databases
	# Check for need
	if ((defined($file))
	  && (-d $file))
  	{
		# Prepare tie
		# pull defaults
		my $def = new DB_File::BTREEINFO;

		# Adjust file
		$file .= '/' . $search . '.' . $dbm;


		# Tie variable
		tie(%copy, 'DB_File', $file, O_RDWR|O_CREAT, 0666, $def)
      		 or die('Error linking with $dbm hash: ', $!);


		# Copy over completely
		%copy = %hash;

		# Proof of concept
		$copy{'GENERATE'} = $dbm;

		# Untie and go
		untie %copy;


		# Set permissions to something sane
		chmod(0666, $file);
	}

	# Return listing
	return $listing;
}


#####################
# Document Modify
#
# Simply modifies documents available and allows one to select them it also
# allows you to select other templates while you wait.

sub DocumentHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative script
	my $links    = shift;					# Links to other sections
	my $url      = shift;					# Self-referencing link

	my $res;						# Results list
	my $statement;						# Statement handle

	my $work     = '';					# Working directory
	my $nav      = '';					# Navigation list

	my $inline;						# Inline parameters
	my $document;						# Document contents
	my $path;						# Path to physical file

	my %copy;						# Copy hash
	my %docs;						# Hash of documents
	

	#####################
	# Program area	

	# If defined save
	if (defined($param->{'DOCDATA'}))
	{
		# Evaluation block
		eval
		{
			# Update information
			$database->Write("UPDATE Tag 
				SET TagData=? 
				WHERE TagName=?
				  AND TagType=?",  

			 $param->{'DOCDATA'}, 
			 $param->{'DOCTYPE'},
			 $database->{'TMPL'}
						);

			# Audit Trail
			$audit->Document($param->{'DOCTYPE'});


			# Commit
			$database->Commit();
		};
			
		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}


	#
	# Pull all records individually
	# and pouplate the hash and array
	$database->Pull(\$statement, "SELECT
		  TagName AS \"TagName\",
       		  TagData AS \"TagData\"
		 FROM Tag
 		WHERE TagType=?
		ORDER BY TagName",

	 $database->{'TMPL'}
	);

	# Cycle and create
	while ($res = $statement->fetchrow_hashref())
	{
		# Generate inline
		$inline = $param->EmbedInline(
			USER    => $param->{'USER'},
			CRYPT   => $param->{'CRYPT'},
			DOCTYPE => $res->{'TagName'}
		 );

		# Append to template
		$nav .= $tmpl->Pass('TagListTemplates',
			MNAME => $res->{'TagName'},
			MLINK => $url . $inline
		 );

		# Populate hash
		$docs{$res->{'TagName'}} = $res->{'TagData'};
	}

	# Finish statement
	$statement->finish();


	#
	# Berkely DB handling
	# Check for need
	if ((defined($database->{'DBHASH'}))
	 && (-d $database->{'DBHASH'})
 	 && (defined($param->{'DOCDATA'})))
 	{
		# Adjust path
		$path = $database->{'DBHASH'} . '/' . $database->{'SEARCH'} . '.' . $database->{'TMPL'};
		
		
		# Defaults
		my $def = new DB_File::BTREEINFO;
		
		# Load up hash
		tie(%copy, 'DB_File', $path, O_RDWR|O_CREAT, 0666, $def)
		 or die("Unable to link to $database->{TMPL} hash", $!);

	 	# Copy over
		%copy = %docs;


		# Permissions change
		chmod(0666, $path);
	}

	# Cleanup
	$param->Cleanup('DOCDATA');


	# 
	# Retrieve contents
	if (defined($param->{'DOCTYPE'}))
	{
		# Select a document
		($document) = $database->DocumentGet($param->{'DOCTYPE'});

		# Create template
		$work = $tmpl->Pass('TmplDocumentWork',
			WUPDATE => $cgi->submit($admin->{'TxtSave'}),
			WDATA   => $cgi->textarea(
				-name    => 'DOCDATA',
				-rows    => 15,
				-columns => 75,
				-wrap    => 'off',
				-default => $document)
	 	 );
	}


	# Start from
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat()), "\n";	

	# Display template
	$tmpl->Show('TmplDocument',
		MLINKS  => $links,
		MWORK   => $work,
		LSTSECT => $nav
	 );

	# End of form
	print $cgi->end_form(), "\n";
}


#####################
# FAQ Listing
#
# Allows an administrator to manage a Frequently Asked Questions
# list for use on the system.    Since this is integrated with
# Ethereal Realms proper, it saves the implementation of other
# FAQ agents.

sub FaqListing
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Autiding file
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# List of sections
	my $url      = shift;					# Self referencing links

	my $res;						# Results list
	my $statement;						# Database statement

	my $inline;						# Inline parameters
	my $ogroup;						# Old group
	my $listing = '';					# List of entries


	#####################
	# Program area

	# Additions
	if ((defined($param->{$admin->{'TxtCreate'}}))
	 && (defined($param->{'NFAQQ'}))
	 && (defined($param->{'NFAQG'}))
	 && (length($param->{'NFAQQ'}) > 3)
	 && (length($param->{'NFAQG'}) > 3))
	{
		# Evaluate block
		eval
		{
			# Add entry
			$database->Write("INSERT INTO Faq
				(FaqQuestion,
				 FaqGroup)
				VALUES (?,?)",

			 $param->{'NFAQQ'},
			 lc($param->{'NFAQG'})
			);


			# Audit
			$audit->FaqAdd($param->{'NFAQQ'});
	
	
			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}


	#
	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		 FaqNum      AS \"FaqNum\",
		 FaqGroup    AS \"FaqGroup\",
	 	 FaqQuestion AS \"FaqQuestion\"
		FROM Faq
		ORDER BY FaqGroup, FaqQuestion"
	 );

	# Display and remove
	# Only if necessary
	if ($res = $statement->fetchrow_hashref())
	{
		# Evaluate structure
		eval
		{
			# Loop until completed
			do
			{
				# Display entry
				# If necessary
				unless (defined($param->{"RMFAQ$res->{FaqNum}"}))
				{
					# Subtitle display
					if ((!defined($ogroup))
					 || ($ogroup ne $res->{'FaqGroup'}))
				 	{
						# Listing
						$listing .= $tmpl->Pass('TmplFAQSections',
							MSECT => ucfirst($res->{'FaqGroup'})
						 );

						# Set for next pass
						$ogroup = $res->{'FaqGroup'};
					}

					# Inline parameters
					$inline = $param->EmbedInline(
						USER  => $param->{'USER'},
						CRYPT => $param->{'CRYPT'},
						FAQID => $res->{'FaqNum'}
					 );

					# Append entry
					$listing .= $tmpl->Pass('TmplFAQuestions',
						MLINK     => $url . $inline,
						MQUESTION => $res->{FaqQuestion},
						WSELECT   => $cgi->checkbox(-name=>"RMFAQ$res->{FaqNum}", -label=>'')
					 );
				}

				else
				{
					# Remove entry
					$database->Write("DELETE FROM Faq
						WHERE FaqNum=?",

					 $res->{'FaqNum'}
					);
		
					# Audit entry	
					$audit->FaqRem($res->{'FaqNum'} . ': ' . $res->{'FaqQuestion'});
				}

			} while ($res = $statement->fetchrow_hashref());


			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();

		}
	}

	# Start form
	print $cgi->start_form();
	print $param->EmbedNormal(
		USER  => $param->{'USER'}, 
		CRYPT => $param->{'CRYPT'}
	 );

	# List template
	$tmpl->Show('TmplFAQ',
		MLINKS       => $links,
		LSTQUESTIONS => $listing,
		WCREATE      => $cgi->submit($admin->{'TxtCreate'}),
		WDELETE      => $cgi->submit($admin->{'TxtDelete'}),
		WGROUP       => $cgi->textfield('NFAQG', '', 25, 25),
		WQUESTION    => $cgi->textfield('NFAQQ', '', 45, 250)
	 );

	# End form
	print $cgi->end_form(), "\n";
}


#####################
# FAQ Questions
#
# Detailed section for questions, allowing people to change
# the questions and answers as need be.

sub FaqQuestion
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative hash
        my $links    = shift;					# Links to other sections
	my $url      = shift;					# Self-referencing link

	my $inline;						# Inline parameters

	my $answer;						# Answer
	my $group;						# Grouping
	my $question;						# Question


	#####################
	# Program area

	# Update information
	if ((defined($param->{'FAQQ'}))
	 && (defined($param->{'FAQG'}))
	 && (defined($param->{'FAQA'}))
 	 && (length($param->{'FAQQ'}) > 3)
 	 && (length($param->{'FAQG'}) > 3))
 	{
		# Re-definition
		$param->{'FAQA'} = (length($param->{'FAQA'}) > 10)
		  ? $param->{'FAQA'} 
		  : undef;
		
		# Evaluate block
		eval
		{
			# Update information
			$database->Write("UPDATE Faq
				SET FaqQuestion=?,
				    FaqGroup=?,
				    FaqAnswer=?
				WHERE FaqNum=?",
			
			 $param->{'FAQQ'},
		 	 $param->{'FAQG'},
		 	 $param->{'FAQA'},
		 	 $param->{'FAQID'}
		 	);

			# Update audit entries
			$audit->FaqUpdate($param->{'FAQID'} . ': ' . $param->{'FAQQ'});

			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}
	

	# Pull information
	($question, $group, $answer) = $database->DataGet("SELECT
		 FaqQuestion,
		 FaqGroup,
		 FaqAnswer
		FROM Faq
		WHERE FaqNum=?",

	 $param->{'FAQID'}
 	);

	# Form beginnings
	print $cgi->start_form();
	print $param->EmbedNormal(
		USER  => $param->{'USER'},
		CRYPT => $param->{'CRYPT'},
		FAQID => $param->{'FAQID'}
	 );

	# Display template
	$tmpl->Show('TmplFAQDetails',
		MLINKS    => $links,
		MID       => $param->{'FAQID'},
		WALTER    => $cgi->submit($admin->{'TxtAlter'}),
		WANSWER   => $cgi->textarea('FAQA', $answer, 15, 75),
		WGROUPING => $cgi->textfield('FAQG', $group, 25, 25),
		WQUESTION => $cgi->textfield('FAQQ', $question, 45, 255)
	 );

	# End Form
	print $cgi->end_form, "\n";
}



#####################
# Genre Viewer
#
# Genres are used to seperate specific types of items into seperate sections.  Such as
# realms, image and text based galleries.   This allows someone to add and remove
# genres and add in a description for SSI style includes.

sub GenreHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links to sections
	my $url      = shift;					# Self-referencing link

	my $res;						# Results section
	my $statement;						# Statement handle

	my $inline;						# Inline parameters

	my $lst_main = '';					# Main list of elements
	my $lst_img  = '';					# List of images
	my $lst_txt  = '';					# List of text

	my $descr;						# Description
	my $type;						# Radio button widget
	my $work     = '';					# Working template

	my @genres;						# List of all genres

	my @type = qw(Main Realm Image Text);			# List of all orders


	#####################
	# Program area

	# Data handling
	# Creation
	if ((defined($param->{'CREATE'}))
	 && (defined($param->{'NGENRE'}))
	 && (length($param->{'NGENRE'}) > 2)
	 && (defined($param->{'NTYPE'})))
	{
		# Evaluation
		eval
		{
			# Safety
			# Confirmation
			$descr = $param->{'NDESCR'};
			$descr = ((defined($descr)) && (length($descr) > 3)) ? $descr : undef;
			$descr = substr(DBI::neat($descr, 252), 1, -1);


			# Behavioural changes
			# Sub section for Image or Prose
			if ((defined($param->{'CTYPE'}))
			 && (($param->{'CTYPE'} eq 'image')
			  || ($param->{'CTYPE'} eq 'text')))
			{
				# Adjust the current values
				$param->{'CNAME'} = $param->{'NGENRE'};
				$param->{'CSUB'}  = $param->{'NTYPE'};
			}

			# Defaulted norm
			else
			{
				# Set current values
				$param->{'CNAME'} = $param->{'NGENRE'};
				$param->{'CTYPE'} = lc($param->{'NTYPE'});
				$param->{'CSUB'}  = '<root>';
			}

			# Insertion
			$database->Write("INSERT INTO Genre
				(GenreName,
				 GenreType,
				 GenreSub,
				 GenreDescription)
				VALUES(?,?,?,?)",

			  $param->{'CNAME'}, 
			  $param->{'CTYPE'},
			  $param->{'CSUB'},
			  $descr
		 	);

			# Auditing entry
			$audit->GenreRem($param->{'CTYPE'} . "\\" . $param->{'CSUB'} . "\\" . $param->{'CNAME'});

			# Commit changes
			$database->Commit();


			# Cleanup
			$param->Cleanup('CREATE', 'NGENRE', 'NTYPE', 'NDESCR');
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}

	# Removal
	elsif ((defined($param->{$admin->{'TxtDelete'}}))
	 && (defined($param->{'CONFIRM'})))
	{
		# Evaluation
		eval
		{
			# Information handling
			# Remove entry
			$database->Write("DELETE FROM Genre
				WHERE GenreName=?
				  AND GenreType=?
				  AND GenreSub=?",

			  $param->{'CNAME'}, 
			  $param->{'CTYPE'},
			  $param->{'CSUB'}
		 	);

			# Auditing entry
			$audit->GenreRem($param->{'CTYPE'} . "\\" . $param->{'CSUB'} . "\\" . $param->{'CNAME'});


			# Commit changes
			$database->Commit();

			# Cleanup after yourself
			$param->Cleanup('CTYPE', 'CNAME', 'CSUB', 'CLEAR');
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}

	# Modification
	elsif ((defined($param->{'CDESCR'}))
	 && (defined($param->{'CTYPE'}))
	 && (defined($param->{'CNAME'}))
	 && (defined($param->{'CSUB'})))
 	{
		# Evaluation block
		eval
		{
			# Confirmation
			$descr = $param->{'CDESCR'};
			$descr = ((defined($descr)) && (length($descr) > 3)) ? $descr : undef;
			$descr = substr(DBI::neat($descr, 252), 1, -1);

			# Update row
			$database->Write("UPDATE Genre
				SET GenreDescription=?
				WHERE GenreName=?
				  AND GenreType=?
				  AND GenreSub=?",

			  $descr,
			  $param->{'CNAME'}, 
			  $param->{'CTYPE'},
			  $param->{'CSUB'}
			);

			# Audit entry 
			$audit->GenreUpdate($param->{'CTYPE'} . "\\" . $param->{'CSUB'} . "\\" . $param->{'CNAME'});


			# Database commit
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
	}

	# Always clear
	$param->Cleanup('CLEANUP');


	# Generate templates
	# Work out lists for root level
	$database->Pull(\$statement, "SELECT
		  GenreType  AS \"GenreType\",
		  GenreSub   AS \"GenreSub\",
		  GenreName  AS \"GenreName\"
		FROM Genre
		WHERE GenreSub='<root>' 
		ORDER BY GenreType, GenreSub, GenreName");

	# Loop and run through
	while ($res = $statement->fetchrow_hashref())
	{
		# Generate inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			CTYPE => $res->{'GenreType'},
			CNAME => $res->{'GenreName'},
			CSUB  => $res->{'GenreSub'}
		 );

		# Append to template
		$lst_main .= $tmpl->Pass('TagListGenres',
			MTYPE => ucfirst($res->{'GenreType'}),
			MNAME => $res->{'GenreName'},
			MLINK => $url . $inline
		 );
	}

	# Creation link
	# Generate inline
	$inline = $param->EmbedInline(
		USER   => $param->{'USER'},
		CRYPT  => $param->{'CRYPT'},
		CTYPE  => 'main',
		CREATE => 'True'
	 );

	# Append to template
	$lst_main .= $tmpl->Pass('TagListCreate',
		MNAME => $admin->{'TxtInsert'},
		MLINK => $url . $inline
	 );

	# Cleanup
	$statement->finish();


	#
	# Now in a sub-section handling

	# For images
	# Standard links
	$database->Pull(\$statement, "SELECT
		  GenreType  AS \"GenreType\",
		  GenreSub   AS \"GenreSub\",
		  GenreName  AS \"GenreName\"
		FROM Genre
		WHERE GenreSub != '<root>'
		  AND GenreType='image'
		ORDER BY GenreType, GenreSub, GenreName");

	# Loop and run through
	while ($res = $statement->fetchrow_hashref())
	{
		# Generate inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			CTYPE => $res->{'GenreType'},
			CNAME => $res->{'GenreName'},
			CSUB  => $res->{'GenreSub'}
		 );

		# Append to template
		$lst_img .= $tmpl->Pass('TagListGenres',
			MTYPE => ucfirst($res->{'GenreType'}),
			MNAME => $res->{'GenreName'},
			MLINK => $url . $inline
		 );
	}

	# Creation link
	# Generate inline
	$inline = $param->EmbedInline(
		USER   => $param->{'USER'},
		CRYPT  => $param->{'CRYPT'},
		CTYPE  => 'image',
		CREATE => 'True'
	 );

	# Append to template
	$lst_img .= $tmpl->Pass('TagListCreate',
		MNAME => $admin->{'TxtInsert'},
		MLINK => $url . $inline
	 );

	# Cleanup
	$statement->finish();


	#
	# For prose
	# Generate standard lists
	$database->Pull(\$statement, "SELECT
		  GenreType  AS \"GenreType\",
		  GenreSub   AS \"GenreSub\",
		  GenreName  AS \"GenreName\"
		FROM Genre
		WHERE GenreSub != '<root>'
		  AND GenreType='text'
		ORDER BY GenreType, GenreSub, GenreName");

	# Loop and run through
	while ($res = $statement->fetchrow_hashref())
	{
		# Generate inline
		$inline = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			CTYPE => $res->{'GenreType'},
			CNAME => $res->{'GenreName'},
			CSUB  => $res->{'GenreSub'}
		 );

		# Append to template
		$lst_txt .= $tmpl->Pass('TagListGenres',
			MTYPE => ucfirst($res->{'GenreType'}),
			MNAME => $res->{'GenreName'},
			MLINK => $url . $inline
		 );
	}

	# Creation link
	# Generate inline
	$inline = $param->EmbedInline(
		USER   => $param->{'USER'},
		CRYPT  => $param->{'CRYPT'},
		CTYPE  => 'text',
		CREATE => 'True'
	 );

	# Append to template
	$lst_txt .= $tmpl->Pass('TagListCreate',
		MNAME => $admin->{'TxtInsert'},
		MLINK => $url . $inline
	 );

	# Cleanup
	$statement->finish();




	# Determine screen to display
	# Creations page
	if (defined($param->{'CREATE'}))
	{
		# Additionally fetch sub-types
		# Images
		if ($param->{'CTYPE'} eq 'image')
	 	{
			# Fetch appropriate list
			$database->GetList(\@type, "SELECT GenreName
				FROM Genre
				WHERE GenreSub='<root>'
				  AND GenreType='image'
				ORDER BY GenreName");
		}

		# Prose
		elsif ($param->{'CTYPE'} eq 'text')
 		 {
			# Fetch appropriate list
			$database->GetList(\@type, "SELECT GenreName
				FROM Genre
				WHERE GenreSub='<root>'
				  AND GenreType='text'
				ORDER BY GenreName");
		}

		# Generate widget
		$type = $cgi->radio_group('NTYPE', \@type, $type[0], 'true');


		# Generate template
		$work = $tmpl->Pass('TmplGenreCreate',
			MTYPE   => $param->{'CTYPE'},
			WGNAME  => $cgi->textfield('NGENRE', '', 20, 25),
			WGTYPE  => $type,
			WDESCR  => $cgi->textarea('NDESCR', '', 10, 70),
			WCREATE => $cgi->submit($admin->{'TxtCreate'})
		 );
	}

	# Display of template
	elsif ((defined($param->{'CTYPE'}))
	 && (defined($param->{'CNAME'}))
	 && (defined($param->{'CSUB'})))
	{
		# Pull description
		($descr) = $database->DataGet("SELECT GenreDescription
			FROM Genre
			WHERE GenreName=?
			  AND GenreType=?
			  AND GenreSub=?",

		 $param->{'CNAME'},
		 $param->{'CTYPE'},
		 $param->{'CSUB'}
		);


		# Generate template
		$work = $tmpl->Pass('TmplGenreModify',
			MNAME   => $param->{'CNAME'},
			MTYPE   => $param->{'CTYPE'},
			WDESCR  => $cgi->textarea('CDESCR', $descr, 10, 70),
			WALTER  => $cgi->submit($admin->{'TxtAlter'}),
			WDELETE => $cgi->submit($admin->{'TxtDelete'})
			         . $cgi->checkbox(-name=>'CONFIRM', -label=>'')
		 );
	}

	# Nothing selected
	else
	{
		# Set template
		$work = $tmpl->Pass('TmplGenreNil');
	}


	# Main form
	# Form display
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat());

	# Display template
	$tmpl->Show('TmplGenre',
		MLINKS  => $links,
		LSTMAIN => $lst_main,
		LSTIMG  => $lst_img,
		LSTTXT  => $lst_txt,
		MWORK   => $work
	 );

	# Form end
	print $cgi->end_form, "\n";
}

#####################
# Realm Addition
#
# Sub-routine that displays additions of new realms.

sub RealmAddition
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Auditing code
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links display
	
	my @radiotype	= qw(yes no hybrid alias);		# Realm type
	my @radiover	= qw(yes no);				# Verified type

	my $radiotype	= $cgi->radio_group('NEWTYPE',  \@radiotype,  $radiotype[0],  'true');
	my $radiover	= $cgi->radio_group('NEWVER',   \@radiover,   $radiotype[1],  'true');

	
	#####################
	# Program area

	# Cleanup
	$param->Cleanup('ADDITION');


	# Start form
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat()), "\n";

	# Display template
	$tmpl->Show('TmplRealmCreate',
		MLINKS  => $links,
		WRNAME  => $cgi->textfield('NEWNAME'  , '', 20, 30),
		WALIAS  => $cgi->textfield('NEWALIAS'  , '', 20, 30),
		WRTYPE  => $radiotype,
		WVERFD  => $radiover,
		WIMGHT  => $cgi->textfield('NEWHEIGHT', '400', 4, 4),
		WIMGWT  => $cgi->textfield('NEWWIDTH' , '400', 4, 4),
		WIMGSZ  => $cgi->textfield('NEWSIZE' , '50', 4, 3),
		WCREATE => $cgi->submit($admin->{'TxtCreate'})
	 );

	# End form
	print $cgi->end_form(), "\n";
}


#####################
# RealmDisplay
#
# Sub-routine that will display and handle basic operations reguarding direction
# of calls.   As in remove and additions.

sub RealmDisplay
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list reference
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Auditing table
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links to sections
	my $url      = shift;					# Self referencing links
	
	my $res;						# Received values
	my $statement; 						# Query handle

	my $count    = 0;					# Colour changer
	my $bgcolor;						# Background colour CSS
	my $handle;						# List of supervisory handles

	my $alias;						# Alias definition
	my $type;						# Realm type
	my $realm;						# Realm name modified
	my $status;						# Realm status

	my $inline;						# Generic button inline
	my $icreate;						# Create button inline

	my $nav      = '';					# Navigational menu
	my $listing  = '';					# Listing of realms
	my $control  = '';					# Usable commands

	my @super;						# List of supervisors


	#####################
	# Program area

	# Audit handle
	$audit = new Ethereal::Audit($database, $cgi, $param);

	# Data addition
	if (defined($param->{'NEWNAME'}))
	{
		# New aliases
		$param->{'NEWALIAS'} = (length($param->{'NEWALIAS'}) > 2)
		  ? $param->{'NEWALIAS'}
		  : undef;

		# Evaluate
		eval
		{
			# Create entry
			$database->Write("INSERT INTO Realm 
				(RealmName,
				 RealmAlias,
				 RealmVerified,
				 RealmPublic,
				 RealmImageHeight,
				 RealmImageWidth,
				 RealmImageSize)
				VALUES (?,?,?,?,?,?,?)", 

			 $param->{'NEWNAME'}, 
			 $param->{'NEWALIAS'}, 
			 $param->{'NEWVER'},
			 $param->{'NEWTYPE'}, 
			 $param->{'NEWHEIGHT'},
			 $param->{'NEWWIDTH'}, 
			 $param->{'NEWSIZE'}
			);

			# Board link
			$database->Write("INSERT INTO BoardTopic 
				(RealmName,
				 BoardTopic)
				VALUES (?,?)",

			 $param->{'NEWNAME'},
			 $admin->{'TxtDefault'}
			);

			# Audit Trail
			$audit->RealmAdd($param->{'NEWNAME'}, $param->{'NEWNAME'});


			# Commit
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}


		# Cleanup
		$param->Cleanup('NEWNAME');
	}

	# Links
	# Generate create inline
	($icreate)  = $param->EmbedInline($param->Flat(), ADDITION=>'True');


	# Pull types
	$database->Pull(\$statement, "SELECT RealmPublic
                FROM Realm
		WHERE RealmName NOT IN ('banned', 'system')
                GROUP BY RealmPublic
		ORDER BY RealmPublic DESC"
	 );

	# List all available
	while (($type) = $statement->fetchrow())
	{
		# Generate inline
		($inline) = $param->EmbedInline(
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'},
			TYPE  => $type
		 );

		# Display link
		$nav .= $tmpl->Pass('TagTabbed',
			MLINK => $url . $inline,
			MNAME => ucfirst($type)
		 );
	}

	# Cleanup
	$statement->finish();

	# Mandatory creation link
	$nav .= $tmpl->Pass('TagTabbed',
		MLINK => $url . $icreate,
		MNAME => $admin->{'TxtCreate'}
	 );


	# Ensure assignment
	$param->{'TYPE'} = (defined($param->{'TYPE'}))
	  ? $param->{'TYPE'}
	  : 'yes';


	# Data viewing
	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		 RealmName     AS \"RealmName\", 
		 RealmAlias    AS \"RealmAlias\", 
		 RealmLanguage AS \"RealmLanguage\", 
		 RealmAccess   AS \"RealmAccess\", 
		 RealmVerified AS \"RealmVerified\", 
		 RealmTags     AS \"RealmTags\", 
		 getDateTime(RealmTimestamp) AS \"RealmTimestamp\" 
		FROM  Realm 
		WHERE RealmPublic=? 
		  AND RealmName NOT IN ('banned', 'system')
		ORDER BY RealmName", 

	 $param->{'TYPE'}
	);

	# Fetch rows of data
	# Handle specific realms
	while ($res = $statement->fetchrow_hashref())
	{
		# Realm name
		$realm = $res->{'RealmName'};
                $realm = uc($realm);
		$realm =~ s/\W//gs;


		# Handling of changes
		if (defined($param->{$realm}))
		{
			# Upgrade or downgrade a realm
			if ((defined($param->{$admin->{'TxtPromote'}}))
			 || (defined($param->{$admin->{'TxtDemote'}})))
			{
				# Upgrades
				if (defined($param->{$admin->{'TxtPromote'}}))
				{
					# Only hybrids and private realms can be upgraded
					if ($param->{'TYPE'} eq 'hybrid') { $status = 'yes'; }
					elsif ($param->{'TYPE'} eq 'no')  { $status = 'hybrid'; };
				}

				# Downgrades
				else
				{
					# Only public can be brought down
					if ($param->{'TYPE'} eq 'yes')    { $status = 'hybrid'; }
				}


				# Commit changes
				if (defined($status))
				{
					# Evaluate
					eval
					{
						# Database update
						$database->Write("UPDATE Realm
							SET RealmPublic=?
							WHERE RealmName=?",

						 $status,
						 $res->{'RealmName'}
						);

						# Audit entry
						$audit->RealmStatus($status, $res->{'RealmName'});


						# Commit
						$database->Commit();
					};
	
					# If block failed
					if ($@)
					{
						# Issue warning
						warn("Transaction aborted: $@");

						# Undo incomplete changes
						$database->Rollback();
					}

					# Undefine values
					$status = undef;
					$realm  = undef;
				}

				# Cleanup
				$param->Cleanup($realm);
			}


			# Handling of removal
			if (defined($param->{$admin->{'TxtDelete'}}))
			{
				# Evaluate
				eval
				{
					# Remove all dangling supervisors/moderators
					$database->GetList(\@super, "SELECT p.PuppeteerLogin
						FROM  Puppeteer p, RealmSupervisor r
						WHERE p.PuppeteerLogin = r.PuppeteerLogin
						AND r.RealmName=?
						ORDER BY p.PuppeteerLogin",

					 $res->{'RealmName'}
					);

					# Cycle though
					foreach $handle (@super)
					{
						# Determin amount of instances
						my ($val) = $database->DataGet("SELECT COUNT(*) 
							FROM RealmSupervisor
							WHERE PuppeteerLogin=?", $handle);

						# Remove priviliges to all those that exist once
						if ((defined($val)) && ($val == 1))
						{
							# Strip access
							$database->Write("UPDATE Puppeteer 
								SET   PuppeteerSuperPrivs='no',
								      PuppeteerModPrivs='no'
								WHERE PuppeteerLogin=?",
							 $handle
							);

							# Record change
							$audit->SuperStrip($handle, $res->{'RealmName'});
						} 		
					}

					# Remove entry
					$database->Write("DELETE FROM Realm
						WHERE RealmName=?", 

					 $res->{'RealmName'}
					);

					# Record removal
					$audit->RealmRem($res->{'RealmName'}, $res->{'RealmName'});


					# Commit change
					$database->Commit();
				};

				# If block failed
				if ($@)
				{
					# Issue warning
					warn("Transaction aborted: $@");

					# Undo incomplete changes
					$database->Rollback();
				}

				# Removal cleanup
				$param->Cleanup($realm);

				# Undefine realm
				$realm = undef;
			}
		}


		# Only process if there is a need
		if (defined($realm))
		{
			# Colour handling
			# Determin colour of row
			$bgcolor = (($count % 2) != 0)
			  ? "ROWPRI"
			  : "ROWSEC";
		
			# Increment	
			$count++;


			# Alias display
			$alias = (defined($res->{'RealmAlias'}))
			  ? $res->{'RealmAlias'}
			  : $admin->{'TxtAliasNil'};
	

			# Append to template
			$listing .= $tmpl->Pass('TmplRealmListing',
				MBGCOLOR  => $bgcolor,
				MNAME     => $res->{'RealmName'},
				MACCESS   => $res->{'RealmAccess'},
				MLANG     => $res->{'RealmLanguage'},
				MTAGS     => $res->{'RealmTags'},
				MVERIFIED => $res->{'RealmVerified'},
				MDATE     => $res->{'RealmTimestamp'},
				MALIAS    => $alias,
				WSELECT   => $cgi->checkbox(-name=>$realm, -label=>'')
			 );
		}
	}

	# End query
	$statement->finish();


	# Final form elements
	# Not related to aliases
	if ($param->{'TYPE'} ne 'alias')
	{
		# Add to template
		$control = $tmpl->Pass('TmplRealmControl',
			WDELETE  => $cgi->submit($admin->{'TxtDelete'}),
			WPROMOTE => $cgi->submit($admin->{'TxtPromote'}),
			WDEMOTE  => $cgi->submit($admin->{'TxtDemote'})
		 );
	}

	# This culprit is an alias
	else
	{
		# Only allow for removal
		$control = $tmpl->Pass('TmplRealmDelete',
			WDELETE => $cgi->submit($admin->{'TxtDelete'})
		 );
	}

	# Start form
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat()), "\n";

	# Display template
	$tmpl->Show('TmplRealm',
		MLINKS     => $links,
		LSTREALMS  => $listing,
		LSTNAV     => $nav,
		LSTCONTROL => $control
	 );

	# End form
	print $cgi->end_form(), "\n";
}


#####################
# Rotate Handler
#
# The additions, removal and overall selection of specific rotating entires can
# all be done through this panel. 

sub RotateHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative entry
	my $links    = shift;					# Links to sections
	my $url      = shift;					# Self referencing links

	my $res;						# Results page
	my $statement;						# Statement list

	my $data;						# Captured data
	my $name;						# Singled out name
	my $zone;						# Singular zone

	my $conduct;						# Conduct widget
	my $edit;						# Modification widget
	my $work;						# Working template
	my $inline;						# Inline parameters

	my @conducts = qw(Image Static Text);			# List of conducts
	my @items;						# List of displayed items
	my @zones    = qw(Banner Gallery JavaScript Portal SSI);# Available zones	

	my %nav;						# Navigational lists


	#####################
	# Program area

	# Information management
	# Addition
	if ((defined($param->{$admin->{'TxtCreate'}}))
	 && (defined($param->{'NCONDUCT'}))
	 && (defined($param->{'NNAME'}))
	 && (length($param->{'NNAME'}) > 3))
 	{
		# Evaluate before comitting
		eval
		{
			# Definition
			$param->{'NTEXT'} = (length($param->{'NTEXT'}) > 15)
			  ? $param->{'NTEXT'}
			  : undef;

			# Insert entry
			$database->Write("INSERT INTO Rotate
				(RotateName,
				 RotateConduct,
				 RotateZone,
				 RotateData)
				VALUES(?,?,?,?)",

			 $param->{'NNAME'},
			 lc($param->{'NCONDUCT'}),
			 lc($param->{'RZONE'}),
			 $param->{'NTEXT'}
			);

			# Audit entry
			$audit->RotateAdd(uc($param->{'RZONE'}) . '-' . $param->{'NNAME'});

			# Commit changes
			$database->Commit();


			# Set accordingly
			$param->{'RNAME'} = $param->{'NNAME'};
			$param->{'RZONE'} = $param->{'RZONE'};
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
		
		# Cleanup
		$param->Cleanup('NCONDUCT', 'NNAME', 'NTEXT', 'CREATE');
	}

	# Dates reset
	# To reorg based on date information
	if ((defined($param->{'RESET'}))
	 && ($param->{'RZONE'} eq 'Banner'))
 	{
		# Reset banner
		$database->Quick("UPDATE Rotate
			SET RotateTimestamp=LOCALTIMESTAMP
			WHERE RotateZone=?",

		 lc($param->{'RZONE'})
		);
	}

	# Removal
	if ((defined($param->{$admin->{'TxtDelete'}}))
	 && (defined($param->{'CONFIRM'})))
 	{
		# Evaluate before comitting
		eval
		{
			# Remove entry
			$database->Write("DELETE FROM Rotate
				WHERE RotateName=?
				  AND RotateZone=?",

			 $param->{'RNAME'},
			 lc($param->{'RZONE'})
			);
				
			# Audit entry
			$audit->RotateRem(uc($param->{'RZONE'}) . '-' . $param->{'NNAME'});

			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
		
		# Cleanup
		$param->Cleanup('RNAME');
	}

	# Edit an existing entry
	if ((defined($param->{$admin->{'TxtAlter'}}))
	 && (defined($param->{'RTEXT'})))
 	{
		# Evaluate before comitting
		eval
		{
			# Definition
			$param->{'RTEXT'} = (length($param->{'RTEXT'}) > 15)
			  ? $param->{'RTEXT'}
			  : undef;

			
			# Update entry
			$database->Write("UPDATE Rotate
				SET RotateData=?
				WHERE RotateName=?
				  AND RotateZone=?",

			 $param->{'RTEXT'},
			 $param->{'RNAME'},
			 lc($param->{'RZONE'})
			);

			# Audit entry
			$audit->RotateUpdate(uc($param->{'RZONE'}) . '-' . $param->{'RENTRY'});

			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}
		
		# Cleanup
		$param->Cleanup('RTEXT');
	}

	# Always cleanup
	$param->Cleanup('CONFIRM');


	# Navigational Lists
	# Loop and generate per zone
	foreach $zone (sort @zones)
	{
		# Find all occurences
		$database->Pull(\$statement, "SELECT 
			  RotateConduct AS \"RotateConduct\",
			  RotateName    AS \"RotateName\"
			FROM Rotate
			WHERE RotateZone=?
			ORDER BY RotateName", lc($zone));

		# Loop through as necessary
		while ($res = $statement->fetchrow_hashref())
		{
			# Create inline
			$inline = $param->EmbedInline(
				USER   => $param->{'USER'},
				CRYPT  => $param->{'CRYPT'},
				RZONE  => $zone,
				RNAME  => $res->{'RotateName'}
			 );	

			# Display entry
			$nav{$zone} .= $tmpl->Pass('TagListRotate',
				MLINK => $url . $inline, 
				MNAME => $res->{'RotateName'},
				MTYPE => ucfirst($res->{'RotateConduct'})
			 );
		}

		# Finish up
		$statement->finish();


		# Creation link
		# Create inline
		$inline = $param->EmbedInline(
			USER   => $param->{'USER'},
			CRYPT  => $param->{'CRYPT'},
			RZONE  => $zone,
			CREATE => 'True'
		 );

		# Display entry
		$nav{$zone} .= $tmpl->Pass('TagListCreate',
			MLINK => $url . $inline, 
			MNAME => $admin->{'TxtInsert'}
		 );
	}

	# Banner reset capability
	# Create inline
	$inline = $param->EmbedInline(
		USER   => $param->{'USER'},
		CRYPT  => $param->{'CRYPT'},
		RZONE  => 'Banner',
		RESET  => 'True');	

	# Display entry
	$nav{'Banner'} .= $tmpl->Pass('TagListCreate',
		MLINK => $url . $inline,
		MNAME => $admin->{'TxtClear'});



	# Change generation based on requirement
	# Modification template
	if (defined($param->{'RNAME'}))
	{
		# Pull text
		($data) = $database->DataGetRotate($param->{'RNAME'}, lc($param->{'RZONE'}));

		# Create wiget
		$edit = $cgi->textarea(
			-name    => 'RTEXT',
			-rows    => 15,
			-columns => 75,
			-wrap    => 'OFF',
			-default => $data);

		# Update template
		$work = $tmpl->Pass('TmplRotateEdit',
			MNAME   => $param->{'RNAME'},
			MTYPE   => $param->{'RTYPE'},
			WTEXT   => $edit,
			WALTER  => $cgi->submit($admin->{'TxtAlter'}),
			WDELETE => $cgi->submit($admin->{'TxtDelete'})
        			 . $cgi->checkbox(-name=>'CONFIRM', -label=>'')
		 );
	}

	# Creation template
	elsif (defined($param->{'CREATE'}))
	{
		# Create wigets
		$conduct = $cgi->popup_menu('NCONDUCT', \@conducts, $conducts[0]);
		$edit    = $cgi->textarea(
			-name    => 'NTEXT',
			-rows    => 15,
			-columns => 75,
			-wrap    => 'OFF',
			-default => '');

		# Update template
		$work = $tmpl->Pass('TmplRotateCreate',
			MTYPE    => $param->{'RTYPE'},
			WTEXT    => $edit,
			WNAME    => $cgi->textfield('NNAME', '', 15, 25),
			WCONDUCT => $conduct,
			WCREATE  => $cgi->submit($admin->{'TxtCreate'})
		 );
	}
	 
	else
	{
		# Default template
		$work = $tmpl->Pass('TmplRotateNil');
	}


	# Display form
	# Header and embeded information
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat());

	# Display template
	$tmpl->Show('TmplRotate',
		MLINKS     => $links,
		MWORK      => $work,
		LSTBANNER  => $nav{'Banner'},
		LSTGALLERY => $nav{'Gallery'},
		LSTJAVA    => $nav{'JavaScript'},
		LSTPORTAL  => $nav{'Portal'},
		LSTSSI     => $nav{'SSI'}
	 );

	# Print form
	print $cgi->end_form();
}


#####################
# SQL Handler
# 
# Looks at parameters and returns a formatted SQL statement to be used for searches

sub SqlHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $param    = shift;					# Parameter hash
	my $sql;						# SQL query	
	my $keyword;						# Keyword

	my $export;						# Export header
	my $limit    = 50;					# Limit per page
	my $offset   = 1;					# Base offset
	my $page;						# Page setting header
	my $system   = 'system';				# System handle

	# List of fields to select for reverse lookup
	my @range    = qw(Administrators Supervisors Banned Verified Non-Priviledged All-Encompasing);
	my @field    = qw(E-Mail Login Name Puppet Homepage OpenID);


	#####################
	# Program area

	# Page offsets
	$offset  = $param->{'PID'} if (($param->{'PID'}) =~ /^\d+$/);
	$offset *= $limit;
	$offset -= $limit;
	
	# Initial construction of mun
	$export = "SELECT DISTINCT(mun.PuppeteerLogin) AS \"PuppeteerLogin\", "
	     . "mun.PuppeteerName         AS \"PuppeteerName\", "
             . "mun.PuppeteerEMail        AS \"PuppeteerEMail\", "
	     . "mun.PuppeteerAdminPrivs   AS \"PuppeteerAdminPrivs\", "
	     . "mun.PuppeteerSuperPrivs   AS \"PuppeteerSuperPrivs\", "
	     . "mun.PuppeteerModPrivs     AS \"PuppeteerModPrivs\", "
	     . "mun.PuppeteerBan          AS \"PuppeteerBan\", "
	     . "mun.PuppeteerVerified     AS \"PuppeteerVerified\"";

	# Page support
	$page = "SELECT COUNT(*) FROM Puppeteer top WHERE EXISTS";

	# SQL statement
	$sql  = " FROM Puppeteer mun LEFT JOIN Puppet pup ON mun.PuppeteerLogin=pup.PuppeteerLogin";


	# Build SQL query
	# Admin privs
	if ($param->{'RANGE'} eq $range[0])
	{ $sql .= " WHERE mun.PuppeteerAdminPrivs='yes'"; }


	# Supervisor privs
	elsif ($param->{'RANGE'} eq $range[1])
	{ $sql .= " WHERE mun.PuppeteerSuperPrivs='yes'"; }
	
	# Banned users
	elsif ($param->{'RANGE'} eq $range[2])
	{ $sql .= " WHERE mun.PuppeteerBan='yes' AND mun.PuppeteerAlias IS NULL"; }

	# Verified users
	elsif ($param->{'RANGE'} eq $range[3])
	{ $sql .= " WHERE mun.PuppeteerVerified='yes'"; }
	
	# Non-Priviledged users
	elsif ($param->{'RANGE'} eq $range[4])
	{ $sql .= " WHERE mun.PuppeteerSuperPrivs='no' AND mun.PuppeteerAdminPrivs='no'"; }

	# Only complete following if nothing (or near to) in keywords
	unless (length($param->{'KEYWORD'}) < 3)
	{
		# Verification on data shaping
		if ($sql =~ /WHERE/)
		{ $sql .= " AND"; }
		else
		{ $sql .= " WHERE"; }
		

		# Field selection
		# E-Mail field search
		if ($param->{'FIELD'} eq $field[0])
		{ $sql .= " mun.PuppeteerEMail"; }

		# Login name search
		elsif ($param->{'FIELD'} eq $field[1])
		{ $sql .= " mun.PuppeteerLogin"; }

		# Full name search
		elsif ($param->{'FIELD'} eq $field[2])
		{ $sql .= " mun.PuppeteerName"; }

		# Puppet name search
		elsif ($param->{'FIELD'} eq $field[3])
		{ $sql .= " pup.PuppetName"; }

		# Homepage name search
		elsif ($param->{'FIELD'} eq $field[4])
		{ $sql .= " mun.PuppeteerHome"; }

		# OpenID name search
		elsif ($param->{'FIELD'} eq $field[5])
		{ $sql .= " mun.PuppeteerOpenID"; }

		# Rewrite
		$keyword = $param->{'KEYWORD'};
		$keyword =~ s/(\'|\"|\`)/\\$1/gs;
		$keyword =~ s/\n/\\n/gs;
		$keyword =~ s/\r/\\r/gs;
		$keyword =~ s/\\/\\/gs;

		
		# Completion of SQL statement
		$sql .= " ILIKE '\%$keyword%'";
	}

	# Return statement to parent
	if ($sql =~ /WHERE/)
	{
		# Complete with exclusion
		$sql .= " AND mun.PuppeteerLogin <> \'$system\'";

		# Block the system handle using AND
		$export .= " $sql ORDER BY mun.PuppeteerLogin LIMIT $limit OFFSET $offset";
		$page   .= " (SELECT mun.PuppeteerLogin $sql AND mun.PuppeteerLogin=top.PuppeteerLogin) LIMIT 1000";
	}
	else
	{
		# Complete with exclusion
		$sql .= " WHERE mun.PuppeteerLogin <> \'$system\'";

		# Block the system handle using WHERE
		$export .= " $sql ORDER BY mun.PuppeteerLogin LIMIT $limit OFFSET $offset";
		$page   .= " (SELECT mun.PuppeteerLogin $sql AND mun.PuppeteerLogin=top.PuppeteerLogin) LIMIT 1000";
	}


	# Found amount of entries
	($page) = $database->DataGet($page);


	# Return both for use
	return $export, $page, $limit;
}


#####################
# User Lookup
#
# Will perform a check against the IP addresses that the user has.   Based
# on that information will come up with other matches.

sub UserLookup
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $param    = shift;					# Parameter reference
	my $audit    = shift;					# Auditing handle				

	my $statement;						# Database statement
	my $send;						# File send

	my $count;						# Misc. counter
	my $email;						# Email address
	my $host;						# Singular hostname
	my $match;						# Fount match
	my $stamp;						# Timestamp

	my @hosts;						# List of hosts

	my %ranges;						# Address ranges


	#####################
	# Program Area

# Format for the output
my $LISTING = '
     @<<<<<<<<<<        @>>>>>>>>>>        @>>>>>>>>>>';

my $HEADER = '

   =======================================================
    Range :  @<<<<<<<<<<<<<<
    ----------------------------------------------------
    (Matches )         (Occurrences)      (Last used )';



	# Retreive Email and Timestamp
	($email) = $database->DataGetPuppeteerEmail($param->{'USER'}); 
	($stamp) = $database->DataGetDate();


	# Hostnames
	$database->GetList(\@hosts, "SELECT PuppeteerHostname
		FROM  Audit
		WHERE PuppeteerLogin IN (SELECT PuppeteerLogin
			FROM Puppeteer
			WHERE PuppeteerAlias=?
			ORDER BY PuppeteerLogin)
		   OR PuppeteerLogin=?
		GROUP BY PuppeteerHostname", 

	 $param->{'LOGIN'},
	 $param->{'LOGIN'}
	);


	# Discover ranges
	while ($host = shift(@hosts))
	{
		# Process if this is an IP address
		if ($host =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/)
		{
			# Determin range
			$host = "$1.$2.$3";

			# Populate hash
			$ranges{$host} = defined($ranges{$host}) ? 1 : $ranges{$host}++;	
		}
	}	

	# Evaluate block
	eval
	{
		# Send mail
		# Create instance
		$send = tie(*MAIL, 'Ethereal::Mail', $database);

		# Initial setup
		$send->SetSubject("$param->{LOGIN} cross-reference ($stamp)");

		# Recipients
		$send->AddTo($email);


		# Cycle through range
		foreach $host (sort(keys(%ranges)))
		{
			# Search through ranges
 			$database->Pull(\$statement, "SELECT PuppeteerLogin, COUNT(*), MAX(AuditTimestamp)
				FROM  Audit
				WHERE PuppeteerHostname::TEXT LIKE '${host}%' 
				GROUP BY PuppeteerLogin
				ORDER BY PuppeteerLogin");

			# Header
			$send->Write($HEADER, $host);


			# Display full list
			while (($match, $count, $stamp) = $statement->fetchrow())
			{
				$send->Write($LISTING, $match, $count, $stamp);
			}

			# Finish query
			$statement->finish();
		}

		# Close and send
		close(MAIL);

		# Record extract
		$audit->CrossRef($param->{'LOGIN'});


		# Commit database
		$database->Commit();
	};

	# If block failed
	if ($@)
	{
		# Issue warning
		warn("Transaction aborted: $@");

		# Undo incomplete changes
		$database->Rollback();
	}
}


#####################
# User Modify
#
# Will either prompt for information or make changes and list the changed 
# values to the user.   Only one value can be changed at a time.  

sub UserModify
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Audit handle
	my $admin    = shift;					# Administrative hash
	my $links    = shift;					# Links to sections
	my $url      = shift;					# Self referencing link

	my $geo;						# Geographical page
	my $page;						# Page handle

	my $res;						# Results list
	my $statement;						# Statement handle
	my $p_res;						# Puppet results
	my $p_statement;					# Puppet statement

	my $radioadmin;						# Radio adminstration
	my $radioban;						# Radio ban
	my $radiomod;						# Radio moderator
	my $radiosuper;						# Radio supervisory 
	my $radiover;						# Radio verivified user

	my $complete = '';					# Completed registration
	my $nav      = '';					# Navigational options
	my $hostnames= '';					# List of hostnames
	my $puppets  = '';					# List of puppets

	my $alias;						# Singular alias
	my $hostname;						# Individual hostname

	my $country;						# Country
	my $region;						# Region
	my $city;						# City
	my $location;						# Combined

	my $home;						# Homepage directory
	my $login;						# Login name
	my $openid;						# OpenID login
	my $salt;						# Password salt

	my $iback;						# Back link
	my $inline;						# Inline parameters

	my $pool;						# Puppet pool
	my $puppet;						# Singular puppet
	my $reg;						# Registration flag

	my $hspan;						# Homepage span
	my $hmethod;						# Homepage calc direction

	my @aliases;						# List of aliases
	my @choices = qw(yes no);				# Yes or no
	my @hostnames;						# Hostnames

	my %contrib;						# Homepage handler


	#####################
	# Program area

	# Change username to normal
	$login = $param->{'LOGIN'};

	# Home Page Handler
	if (defined($param->{'HOME'}))
	{
		# Final calculation
		$hmethod = (defined($param->{'POS'})) ? '+' : '-';
		$hspan   = "getTimestamp(PuppeteerHomeSpan) $hmethod INTERVAL '1 year'";

		# Evaluation
		eval
		{
			# Database update
			$database->Write("UPDATE Puppeteer
				SET   PuppeteerHomeSpan=$hspan
				WHERE PuppeteerLogin=?",

			 $login
			);

			# Auditing
			$audit->HomepageSpan($login);


			# Commit
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}


		# Cleanup
		$param->Cleanup(
			'HOME',
			'POS',
			'NEG'
		 );
	}


	# Call cross reference
	UserLookup($database, $param, $audit)
       	  if (defined($param->{'REFERENCE'}));


	# If save then save
	if (defined($param->{'SAVE'}))
	{
		# Evaluate block
		eval
		{
			# Password reset
			if ($param->{'PASSWORD'} =~ /^\*\w/)
			{
				# Determine password
				$salt = md5_hex(rand());
				$salt = substr($salt, 0, 10);

				# Strip star
				$param->{'PASSWORD'} =~ s/^\*//;

				# Generate crypt
				$param->{'PASSWORD'} = crypt($param->{'PASSWORD'}, $salt);


				# Audit entry
				$audit->PasswordChange($login);
			}

			# Handling of ban
			if ((defined($param->{'BANCONF'})) && ($param->{'BAN'} eq 'yes'))
			{	
				# Home page handling
				# Retreive home
				($home) = $database->DataGetPuppeteerHome($login);

				# Remove folder
				if (defined($home)) 
				{ 
					# Link to hash
					$database->GetHashContrib(\%contrib);

					# Create handle
					$page   = new Ethereal::Home($database);

					# Clear out directory
					$page->DirClear($contrib{'SetHomeRoot'} . '/' . $home); 
				}

				# Alias name
				($alias) = $database->DataGetPuppeteerExistence($param->{'ALIAS'});

				# Set attribute
				$database->Write("UPDATE Puppeteer
					SET PuppeteerBan='yes',
					    PuppeteerAlias=?
					WHERE PuppeteerLogin=?",

				 $alias,
				 $login);

				# Star out password
				$param->{'PASSWORD'} = '*************';

				# Audit Trail
				$audit->BanSystem($login);
			}	


			# Make changes
			$database->Write("UPDATE Puppeteer 
				 SET PuppeteerPassword=?,  
				     PuppeteerName=?,
				     PuppeteerEMail=?, 
				     PuppeteerAdminPrivs=?, 
				     PuppeteerSuperPrivs=?,
				     PuppeteerModPrivs=?,
				     PuppeteerVerified=?
			 	WHERE PuppeteerLogin=?", 

		 	 $param->{'PASSWORD'}, 
			 $param->{'FULLNAME'}, 
 			 $param->{'EMAIL'}, 
			 $param->{'ADMIN'}, 
			 $param->{'SUPER'},
			 $param->{'MOD'}, 
			 $param->{'VER'},
			 $login
			);

			# Audit trail
			$audit->PuppeteerChange($login);


			# Commit changes
			$database->Commit();
		};

		# If block failed
		if ($@)
		{
			# Issue warning
			warn("Transaction aborted: $@");

			# Undo incomplete changes
			$database->Rollback();
		}

		# Cleanup
		$param->Cleanup('SAVE', 'BANCONF');
	}


	# Back button
	# Generate inline
	($iback) = $param->EmbedInline(
		USER    => $param->{'USER'},
		CRYPT   => $param->{'CRYPT'},
		RANGE   => $param->{'RANGE'},
		KEYWORD => $param->{'KEYWORD'},
		FIELD   => $param->{'FIELD'},
		SEARCH  => $param->{'SEARCH'}
	 );

	# Generate inline
	($inline) = $param->EmbedInline($param->Flat(), HOME=>'True');

	# Append to templates
	# Back button
	$nav .= $tmpl->Pass('TagListButtons',
		MLINK => $url . $iback,
		MNAME => $admin->{'TxtBack'});

	# Cross-Referencing
	$nav .= $tmpl->Pass('TagListButtons',
		MLINK => $url . $inline . '&REFERENCE=True',
		MNAME => $admin->{'TagUserCross'});

	# Positive increment to homepage
	$nav .= $tmpl->Pass('TagListButtons',
		MLINK => $url . $inline . '&POS=True',
		MNAME => $admin->{'TagUserPos'});

	# Negative increment to homepage
	$nav .= $tmpl->Pass('TagListButtons',
		MLINK => $url . $inline . '&NED=True',
		MNAME => $admin->{'TagUserNeg'});


	#
	# Registration flag
	($reg) = $database->DataGetPuppeteerReg($login);

	# User failed to register
	if (defined($reg))
	{
		# Invalid registraion notice
		$complete = $tmpl->Pass('TmplUserComplete');
	}


	# Data display
	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		 PuppeteerLogin      AS \"PuppeteerLogin\",
		 PuppeteerOpenID     AS \"PuppeteerOpenID\",
		 PuppeteerPassword   AS \"PuppeteerPassword\",
		 PuppeteerName       AS \"PuppeteerName\",
		 PuppeteerAlias      AS \"PuppeteerAlias\",
		 PuppeteerEMail      AS \"PuppeteerEMail\",
		 PuppeteerFormatting AS \"PuppeteerFormatting\",
		 PuppeteerChatPref   AS \"PuppeteerChatPref\",
		 PuppeteerJavascript AS \"PuppeteerJavascript\",
		 PuppeteerAdminPrivs AS \"PuppeteerAdminPrivs\",
		 PuppeteerSuperPrivs AS \"PuppeteerSuperPrivs\",
		 PuppeteerModPrivs   AS \"PuppeteerModPrivs\",
		 PuppeteerVerified   AS \"PuppeteerVerified\",
		 PuppeteerBan        AS \"PuppeteerBan\",
		 PuppeteerHostname   AS \"PuppeteerHostname\",
		 PuppeteerHome       AS \"PuppeteerHome\",
		 getDateTime(PuppeteerHomeSpan)  AS \"PuppeteerHomeSpan\",
		 getDateTime(PuppeteerTimestamp) AS \"PuppeteerTimestamp\"
		FROM  Puppeteer 
		WHERE PuppeteerLogin=?", $login);

	# Retreive 
	$res = $statement->fetchrow_hashref();


	# Handle listing
	# Create instance
	$geo = new Ethereal::Geo($database);


	# Change behaviour based on class
	if ($res->{'PuppeteerBan'} eq 'yes')
	{
		# User is banned get aliases
		# Get list
		$database->GetList(\@aliases, "SELECT PuppeteerLogin
			FROM Puppeteer
			WHERE PuppeteerAlias=?
			ORDER BY PuppeteerLogin", $login);

		# Append login name
		unshift(@aliases, $login);


		# Recover Puppets
		foreach $alias (@aliases)
		{
			# Titles
			# Puppet title
			$puppets .= $tmpl->Pass('TmplUserAlias',
				MNAME => $alias
			 );

			# Hostnames title
			$hostnames .= $tmpl->Pass('TmplUserAlias',
				MNAME => $alias
			 );


			#
			# Recover puppets
			$database->Pull(\$p_statement, "SELECT
				  PuppetName AS \"PuppetName\",
				  PuppetPool AS \"PuppetPool\"
				FROM Puppet 
				WHERE PuppeteerLogin=?
				ORDER BY PuppetName",
			 $alias
			);

			# Loop and append
			while ($p_res = $p_statement->fetchrow_hashref())
			{
				# Add all hostnames
				$puppets .= $tmpl->Pass('TmplUserPuppets',
					MNAME => $cgi->escapeHTML($p_res->{'PuppetName'}),
					MROOM => $cgi->escapeHTML($p_res->{'PuppetPool'})
				 );
			}


			#
			# Recover IPs
			$database->GetList(\@hostnames, "SELECT PuppeteerHostname 
				FROM  Audit 
				WHERE PuppeteerLogin=? 
				GROUP BY PuppeteerHostname 
				ORDER BY PuppeteerHostname",

			 $alias
			);

			# Construct alias 
			while ($hostname = shift(@hostnames))
			{
				# Fetch details
				($country, $region, $city) = $geo->Locate($hostname);

				# Location
				$location = (defined($country))
				  ? $city . ', ' . $region . ', ' . $country
				  : $admin->{'TxtCityNil'};

				# Add all hostnames
				$hostnames .= $tmpl->Pass('TmplUserHostname',
					MHOST     => $hostname,
					MLOCATION => $location
				 );
			}
		}	
	}

	# Standard handling
	else
	{
		#
		# Recover IPs
		$database->GetList(\@hostnames, "SELECT PuppeteerHostname 
			FROM  Audit 
			WHERE PuppeteerLogin=? 
			GROUP BY PuppeteerHostname 
			ORDER BY PuppeteerHostname", 

		 $login
		);

		# Contruct list
		while ($hostname = shift(@hostnames))
		{
			# Fetch details
			($country, $region, $city) = $geo->Locate($hostname);

			# Location
			$location = (defined($country))
			  ? $city . ', ' . $region . ', ' . $country
			  : $admin->{'TxtCityNil'};

			# Add all hostnames
			$hostnames .= $tmpl->Pass('TmplUserHostname',
				MHOST     => $hostname,
				MLOCATION => $location
			 );
		}

		#
		# Recover puppets
		$database->Pull(\$p_statement, "SELECT
			  PuppetName AS \"PuppetName\",
			  PuppetPool AS \"PuppetPool\"
			FROM Puppet 
			WHERE PuppeteerLogin=?
			ORDER BY PuppetName",
		 $login
		);

		# Loop and append
		while ($p_res = $p_statement->fetchrow_hashref())
		{
			# Add all hostnames
			$puppets .= $tmpl->Pass('TmplUserPuppets',
				MNAME => $cgi->escapeHTML($p_res->{'PuppetName'}),
				MROOM => $cgi->escapeHTML($p_res->{'PuppetPool'})
			 );
		}
	}

	# Preperation of radio button labels
	$radioadmin = $cgi->radio_group('ADMIN', \@choices, $res->{'PuppeteerAdminPrivs'});
	$radioban   = $cgi->radio_group('BAN',   \@choices, $res->{'PuppeteerBan'});
	$radiosuper = $cgi->radio_group('SUPER', \@choices, $res->{'PuppeteerSuperPrivs'});
	$radiomod   = $cgi->radio_group('MOD'  , \@choices, $res->{'PuppeteerModPrivs'});
	$radiover   = $cgi->radio_group('VER'  , \@choices, $res->{'PuppeteerVerified'});

	# OpenID display
	$openid = (defined($res->{'PuppeteerOpenID'}))
	  ? $res->{'PuppeteerOpenID'}
	  : 'N/A';


	# Display form
	# Start form
	print $cgi->start_form(), "\n";
	print $param->EmbedNormal($param->Flat(), SAVE=>'True'), "\n";

	# Display template
	$tmpl->Show('TmplUser',
		MLINKS     => $links,
		MUSER      => $res->{'PuppeteerLogin'},
		MOPENID    => $openid,
		MLAST      => $res->{'PuppeteerTimestamp'},
		MHOST      => $res->{'PuppeteerHostname'},
		MHOME      => $res->{'PuppeteerHome'},
		MSHOME     => $res->{'PuppeteerHomeSpan'},
		MCHAT      => $res->{'PuppeteerChatPref'},
		MFORMAT    => $res->{'PuppeteerFormatting'},
		MJAVA      => $res->{'PuppeteerJavascript'},
		MCOMPLETE  => $complete,
		LSTNAV     => $nav,
		LSTPUPPETS => $puppets,
		LSTHOSTS   => $hostnames,
		WEMAIL     => $cgi->textfield('EMAIL', $res->{'PuppeteerEMail'} , 25, 45),
		WPASS      => $cgi->textfield('PASSWORD', $res->{'PuppeteerPassword'}, 25, 13),
		WNAME      => $cgi->textfield('FULLNAME', $res->{'PuppeteerName'}, 25, 30),
		WALIAS     => $cgi->textfield('ALIAS', $res->{'PuppeteerAlias'}, 25, 10),
		WRADMIN    => $radioadmin,
		WRSUPER    => $radiosuper,
		WRMOD      => $radiomod,
		WRVER      => $radiover,
		WRBAN      => $radioban,
		WRCONF     => $cgi->checkbox( -name=>'BANCONF', -label=>''),
		WALTER     => $cgi->submit($admin->{'TxtAlter'}),
		WRESET     => $cgi->reset($admin->{'Reset'})
	 );

	# End form 
	print $cgi->end_form(), "\n";

	# Finish statement
	$statement->finish();
}


#####################
# User Search
#
# Allows one to select range of users to select, Admin, Supervisors, et cetera

sub UserSearch
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative handle
	my $links    = shift;					# Links handle

	my $radiorange;						# Range radio button
	my $radiofield;						# Field ratio button


	#####################
	# Program area
	
	# Widget sets
	my @range = qw(Administrators Supervisors Banned Verified Non-Priviledged All-Encompasing);
	my @field = qw(E-Mail Login Name OpenID Puppet Homepage);

	$radiorange = $cgi->radio_group('RANGE', \@range, $range[5], 'true');
	$radiofield = $cgi->radio_group('FIELD', \@field, $field[0], 'true');


	# Form start	
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat(), SEARCH=>'True'), "\n";

	# Display template
	$tmpl->Show('TmplUserSearch',
		MLINKS  => $links,
		WRANGE  => $radiorange,
		WFIELD  => $radiofield,
		WKEYWD  => $cgi->textfield('KEYWORD', '', 25, 25),
		WSEARCH => $cgi->submit($admin->{'TxtSearch'})
	 );

	# Ending form
	print $cgi->end_form(), "\n";
}


#####################
# User Select
#
# Displays initial query and allows to select individual user

sub UserSelect
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list reference
	my $tmpl     = shift;					# Template handle
	my $audit    = shift;					# Auditing table
	my $admin    = shift;					# Administrative handle
	my $links    = shift;					# Link to other sections
	my $url	     = shift;					# Self referencing link

	my $query;						# Query to execute
	my $limit;						# Limit to display

	my $page;						# Homepage handler
	my $res;						# Results handle
	my $statement;						# Statement handle

	my $inline;						# General inline
	my $iback;						# Back one parameter 
	my $ipage;						# Page support parameter 

	my $bgcolor;						# Background CSS colour
	my $count    = 0;					# Counter
	my $gallery;						# Current gallery
	my $home;						# Home page
	my $login;						# Modified login name
	my $privs;						# Formatted privilege list

	my $nav      = '';					# Navigational aid
	my $listing  = '';					# Listing of elements

	my @privs;						# Privilege list
	my @galleries;						# Galleries

	my %contrib;						# Home page hash


	#####################
	# Program area	

	# Determine all information for search
    	($query, $page, $limit) = SqlHandler($database, $param);

	# Generate inlines
	($iback) = $param->EmbedInline(USER=>$param->{'USER'}, CRYPT=>$param->{'CRYPT'});
	($ipage) = $param->EmbedInline($param->Flat());


	# Navigational links
	# Mandatory back link
	$nav .= $tmpl->Pass('TagTabbed',
		 MLINK => $url . $iback,
		 MNAME => $admin->{'TxtBack'}
	  );

	# Loop and execute
	do
	{
		# Remove mount to verify spacing
		$page -= $limit;

		# Increment
		$count++;

		# Display pages
	  	$nav .= $tmpl->Pass('TagTabbed',
		      MLINK => $url . $ipage . "&PID\=$count",
		      MNAME => $count
	         );

	} while ($page > 0);


	# Reset values
	$count = 0;
	$page  = undef;


	# Generate listing
	# Data gathering and display
	$database->Pull(\$statement, $query);

	# Fetch rows of data
	while ($res = $statement->fetchrow_hashref())
	{
		# Name mangling
		$login  =  $res->{'PuppeteerLogin'};
		$login  =~ s/\W//gs;


		# Time to remove
		if (defined($param->{$login}))
		{
			# Evaluation
			eval
			{
				# Link to hash
				$database->GetHashContrib(\%contrib);

				# Create instances
				$page  = new Ethereal::Home($database, $contrib{'SetHomeRoot'})
				  unless (defined($page));


				# Home page handling
				# Retreive home
				($home) = $database->DataGetPuppeteerHome($res->{'PuppeteerLogin'});

				# Remove folder
				if (defined($home))
				{
					# Rip out directory
					$page->DirClear($home); 
				}


				# Gallery Handling
				# Retreive list of galleries
				$database->GetList(\@galleries, "SELECT GalleryID
					FROM Gallery
					WHERE PuppeteerLogin=?", $res->{'PuppeteerLogin'});

				# Run though list of puppets
				while ($gallery = shift(@galleries))
				{
					# Attempt to remove
					$page->DirClear("$database->{SYS}{SetBaseDir}/$gallery");
				}


				# Purge puppeteer
				$database->Write("DELETE FROM Puppeteer
					WHERE PuppeteerLogin=?", $res->{'PuppeteerLogin'});

				# Audit change
				$audit->UserRem($res->{'PuppeteerLogin'});


				# Commit changes
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->Rollback();
			}

			# Cleanup
			$param->Cleanup($login);
		}

		# Safe to display and process
		else
		{
			# Clear array
			splice(@privs, 0);

			# Privilege handling and display
			push(@privs, 'A') if ($res->{'PuppeteerAdminPrivs'} eq 'yes');
			push(@privs, 'B') if ($res->{'PuppeteerBan'} eq 'yes');
			push(@privs, 'M') if ($res->{'PuppeteerModPrivs'} eq 'yes');
			push(@privs, 'S') if ($res->{'PuppeteerSuperPrivs'} eq 'yes');
			push(@privs, 'V') if ($res->{'PuppeteerVerified'} eq 'yes');

			# Join up for display
			$privs = join(',', @privs);
		

			# Colour handling
			$bgcolor = (($count % 2) != 0) ? "ROWPRI" : "ROWSEC";
		
			# Increment	
			$count++;


			# Inline creation
			$inline = $param->EmbedInline($param->Flat(), LOGIN=>$res->{'PuppeteerLogin'});

			# Generate listing
			$listing .= $tmpl->Pass('TmplUserListing',
				LDETAIL  => $url . $inline,
				MBGCOLOR => $bgcolor,
				MEMAIL   => $res->{PuppeteerEMail},
				MNAME    => $res->{PuppeteerName},
				MPRIVS   => $privs,
				MUSER    => $res->{PuppeteerLogin},
				WSELECT  => $cgi->checkbox(-name=>$login, -label=>'')
			 );
		}
	}

	# Finish statement
	$statement->finish();


	# Display Form
	# Form handling
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat()), "\n";

	# Display template
	$tmpl->Show('TmplUserSummary',
		MLINKS   => $links,
		LSTNAV   => $nav,
		LSTUSERS => $listing,
		WDELETE  => $cgi->submit($admin->{'TxtDelete'})
	 );

	# End form
	print $cgi->end_form(), "\n";
}


#####################
# Weblog Handler
#
# The administrative script that administrators will use in order to manage weblog
# entries directly.   This also serves as a way to add entries into the weblog
# itself.

sub WeblogHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $tmpl     = shift;					# Template handler
	my $audit    = shift;					# Auditing handle
	my $admin    = shift;					# Administrative entry
	my $links    = shift;					# Links to other sections
	my $url      = shift;					# Self-referencing link
	my $clear    = shift;					# Clear handle

	my $filter;						# Filter handle
	my $rss;						# RSS code

	my $res;						# Results hash
	my $statement;						# Database statement
	
	my $button;						# Control widgets
	my $date;						# Captured date
	my $ins;						# Insertion identifier
	my $single;						# Single entry
	my $topic;						# Topic widget
	my $weblog;						# Weblog entry

	my @topics;						# Topic array
	my @weblogs;						# Weblog entries

	my %board;						# Board hash


	#####################
	# Program area

	# Define instances
	$filter = new Ethereal::Filter();

	# Link with hash
	$database->GetHashBoard(\%board);


	# Information management
	# Addition
	if (defined($param->{$admin->{'TxtSave'}}))
	{
		# Post to actually handle
		if ((defined($param->{'POST'}))
		 && (length($param->{'POST'}) > 3)
		 && (length($param->{'SUBJ'}) > 3))
		{
			# Ensure definition
			unless ((defined($param->{'TOPC'}))
			 && (length($param->{'TOPC'}) > 3))
			{
				# Set default topic
				$param->{'TOPC'} = $board{'TxtGeneral'};
			}	

			# Skip if desired
			unless (defined($param->{'DISABLE'}))
			{
				# Post mangling
				# Standard filter
				$param->{'POST'} = $filter->SafeHTML($param->{'POST'});
				$param->{'POST'} = $filter->MakeSane($param->{'POST'}, $board{'SetTruncate'});
			
				# Replace carriage returns
				$param->{'POST'} =~ s/\n/<BR>/gs;

				# Create hyperlinks
				$param->{'POST'} =~ s/(http:\/\/.*?)(\s|$)/<A HREF="$1" TARGET="_blank">$board{TagHyperlink}<\/A> /gm;
				$param->{'POST'} =~ s/([\w\.\-]*?\@[\w\.\-]*?\..*?)(\s|$)/<A HREF="mailto:$1">$board{TagEmail}<\/A> /gm;

				# Strip HTML
				$param->{'SUBJ'} =~ s/<[^>]*>//gs;
				$param->{'TOPC'} =~ s/<[^>]*>//gs;
			}

			# Evaluation block
			eval
			{
				# Database updates
				$database->Write("INSERT INTO Weblog
					(PuppetName,
					 LogTitle,
					 LogTopic,
					 LogPost)
					VALUES (getPuppet(?),?,?,?)",

				 $param->{'USER'},
				 $param->{'SUBJ'},
				 lc($param->{'TOPC'}),
				 $param->{'POST'}
				);


				# Cloning vat
				# Only if needed
				if ((defined($param->{'CLONE'}))
				 && ($param->{'CLONE'} ne $admin->{'TxtNone'}))
				{
					# Pull insertion number
					($ins) = $database->DataGetInsert('seqBoard');

					# Database insertion
					$database->Write("INSERT INTO Board
						(BoardIDNumber,
						 PuppetName,
 						 RealmName,
 						 BoardTopic,
 						 BoardSubject,
 						 BoardMessage,
						 BoardLevel,
 						 LevelZero,
 						 LevelOne,
 						 LevelTwo,
 						 LevelThree,
 						 LevelFour,
		 				 LevelFive)
       	 				VALUES ($ins,getPuppet(?),?,?,?,?,0,$ins,$ins,$ins,$ins,$ins,$ins)",

 					 $param->{'USER'},
 					 'system',
 					 $param->{'CLONE'},
 					 $param->{'SUBJ'},
 					 $param->{'POST'}
					);
				}


				# Audit entry
				$audit->WeblogAdd(uc($param->{'TOPC'}) . ':' . $param->{'SUBJ'});


				# Commit changes
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->Rollback();
			}

			# Commit successful
			# Create a RSS feed
			else
			{
				# Initialize instance
				$rss  = new XML::RSS (version => '1.0');
				
				# Fetch date
				($date) = $database->DataGetDateRSS();

				# Initialize header
				$rss->channel(
					title       => $database->{'SYS'}{'SetInfoServer'},
					link        => $database->{'SYS'}{'SetUrl'},
					description => $database->{'SYS'}{'SetDescription'},
					dc => {
						creator    => $database->{'SYS'}{'SetInfoContactAddress'},
						publisher  => $database->{'SYS'}{'SetInfoContactAddress'},
						date       => $date,
						language   => 'en-us',
   					 }
				 );
			
				# Search for entries
				# Prepare
				$database->Pull(\$statement, "SELECT
					 PuppetName   AS \"PuppetName\",
					 LogIDNumber  AS \"LogIDNumber\",
					 LogTitle     AS \"LogTitle\",
					 LogTopic     AS \"LogTopic\",
					 LogPost      AS \"LogPost\"
					FROM Weblog
					ORDER BY LogTimestamp DESC LIMIT 10");
				
				# Loop and write
				while ($res = $statement->fetchrow_hashref())
				{
					# Insert basic item
					$rss->add_item(
						title       => $res->{'LogTitle'},
						link        => $database->{'SYS'}{'LocScriptNews'} . $param->Crypt(WID=>$res->{'LogIDNumber'}),
						description => $filter->StripHTML($res->{'LogPost'}),
						dc => {
							subject  => uc($res->{'LogTopic'}),
							creator  => $res->{'PuppetName'}
						 }
					 );
				}	
				# Finish up
				$statement->finish();		

				# Save up
				$rss->save($database->{'SYS'}{'LocRSSNews'});
			}
		}
	}

	# Removal
	if (defined($param->{'CONFIRM'}))
	{
		# Cycle through
		foreach $single (@{$clear})
		{
			# Skip if not needed
			next if ($single eq $admin->{'TxtNone'});


			# Pull ID and Topic
			$single =~ /(\d+) - (.+) \(.+\)/;

			# Assign
			$ins   = $1;
			$topic = $2;


			# Evaluate block
			eval
			{
				# Remove
				$database->Write("DELETE FROM Weblog
					WHERE LogIDNumber=?", $ins);

				# Audit entry
				$audit->WeblogRem($ins . ':' . $topic);


				# Commit changes
				$database->Commit();
			};

			# If block failed
			if ($@)
			{
				# Issue warning
				warn("Transaction aborted: $@");

				# Undo incomplete changes
				$database->Rollback();
			}
		}
	}

	# Cleanup
	$param->Cleanup('CONFIRM');


	# Generate widgets
	# Pull values
	$database->GetListTopics(\@topics, 'system');
	$database->GetList(\@weblogs, "SELECT 
		  LogIDNumber || ' - ' ||
		  LogTitle || ' (' ||
		  getDate(LogTimestamp) || ')'
		FROM Weblog
		ORDER BY LogIDNumber DESC");

	# Append
	unshift(@topics,  $admin->{'TxtNone'});
	unshift(@weblogs, $admin->{'TxtNone'});

	# Generate
	$topic  = $cgi->popup_menu('CLONE', \@topics, $topics[0]);
	$weblog = $cgi->scrolling_list('CLEAR', \@weblogs, $weblogs[0], 10, 'true');
	$button = $cgi->submit($admin->{'TxtDelete'}) . '&nbsp;' . $cgi->checkbox(-name=>'CONFIRM', -label=>'');


	# Display forms
	# Form
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat());

	# Display template
	$tmpl->Show('TmplWeblog',
		MLINKS  => $links,
		WCLONE  => $topic,
		WDELETE => $button,
	        WFILTER => $cgi->checkbox(-name=>'DISABLE', -label=>''),
		WPOST   => $cgi->textarea(
				-name    => 'POST',
				-rows    => 10,
				-columns => 60,
				-wrap    => 'SOFT'),
		WSAVE   => $cgi->submit($admin->{'TxtSave'}),
		WSUBJ   => $cgi->textfield('SUBJ', '', 25, 100),
		WTOPC   => $cgi->textfield('TOPC', '', 15, 25),
		WWEBLOG => $weblog
	 );

	# Print form
	print $cgi->end_form();
}




#####################
# Navigation
#
# Used to consolidate navigational methods on the site employing OFFSET and LIMIT
# which are available on PostgreSQL.

sub Navigation
{
	#####################
	# Data members
	my $tmpl      = shift;					# Template handler
	my $admin     = shift;					# Administrative hash
	my $display   = shift;					# Entries to show off
	my $total     = shift;					# Total entries
	my $offset    = shift;					# Current position
	my $url       = shift;					# Link to display
	my $inline    = shift;					# Inline parameters

	my $count;						# Basic counter
	my $frst;						# First entry
	my $last;						# Last entry
	my $next;						# Next entry
	my $prev;						# Prev entry
	
	my $link;						# Generated link
	my $nav;						# Completed navigational page
	my $pass;						# What is passed
	

	#####################
	# Program area

	# Set values
	$count = 0;
	$nav   = '';
	$pass  = '&nbsp;';
	
	# Calculate next and previous
	$frst = 1;
	$last = (($total - ($total % $display)) / $display) + 1;
	$prev = (($offset - 1) > 0) ? $offset - 1 : 1;
	$next = (($offset + 1) <= $last) ? $offset + 1 : $last;
	
	# Loop
	do
	{
		# Remove mount to verify spacing
		$total -= $display;

		# Increment
		$count++;

		# Assign
		$link  = $url . $inline . '&PAGE=' . $count;

		# Append
		$nav .= ($offset == $count)
		  ? $tmpl->Pass('TagNavView', MNAME=>$count, MLINK=>$link)
		  : $tmpl->Pass('TagNav',     MNAME=>$count, MLINK=>$link);

	} while ($total > 0);

	# Generate template
	# Only if there is a need
	$pass = $tmpl->Pass('TagNav', MNAME=>$admin->{'TxtFirst'}, MLINK=>$url . $inline . '&PAGE=' . $frst)
	     .  $tmpl->Pass('TagNav', MNAME=>$admin->{'TxtPrev'},  MLINK=>$url . $inline . '&PAGE=' . $prev)
	     .  $nav
	     .  $tmpl->Pass('TagNav', MNAME=>$admin->{'TxtNext'},  MLINK=>$url . $inline . '&PAGE=' . $next)
	     .  $tmpl->Pass('TagNav', MNAME=>$admin->{'TxtLast'},  MLINK=>$url . $inline . '&PAGE=' . $last) if ($count > 1);

	# Return completed
	return $pass;
}
