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

#################################################################################
# Created       : Martin Foster
# Modified      : 17-Nov-2005
#################################################################################
#
# System Configuration - Harmonized script designed to behave as a control panel
# Copyright (C) 2000-2006  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 : 29-26503 TWP RD 511
#                 Spruce Grove, Alberta
#                 T7Y 1G4
#
#################################################################################

use CGI;							# 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::Home;						# Homepage handler
use Ethereal::Login;						# Login functionality
use Ethereal::Mail;						# Mail handler
use Ethereal::Param;						# Parameter control
use Ethereal::Table;						# Table handler
use Ethereal::Template;						# Template handler

#################################################################################
# Gobal override
#################################################################################
$CGI::POST_MAX=1024 * 150;					# 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 $param;							# Parameter handle
my $table;							# Table name
my $tmpl;							# Template name

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

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


# Script addresses and names
my %scripts = (
	'Access'    => 'access',
	'Attribute' => 'attrib',
	'Audit'     => 'audit',
	'Contact'   => 'contact',
	'FAQ'       => 'faq',
	'Font'      => 'font',
	'Genre'     => 'genre',
	'Gummy'     => 'gummy',
	'Poll'      => 'poll',
	'Template'  => 'template',
	'Realm'     => 'realm',
	'Rotate'    => 'rotate',
	'User'      => 'system',
	'Weblog'    => 'weblog',
	'Words'     => 'words'
);

# Administrative hash hash
my %admin;


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

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

	# 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);


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

	# Parameter handling
	$tmpl     = new Ethereal::Template(\%admin);
	$param    = new Ethereal::Param($database, $cgi);


	# Pull parameters
	$param->GetParam();

	# Login
	$login = new Ethereal::Login($database, $cgi, $param);

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

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

		# Pull script name
		$sname = $cgi->url(-full=>1);
		$sparam = $cgi->url(-path_info=>1);


		# Determine need
		# Escape
		$sname = quotemeta($sname);

		# Needed addition
		$sparam = ($sparam =~ /\/$/) ? $sparam : "$sparam/";

		# Truncate
		$sparam =~ s/^$sname\///;


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


		# Menu control
		# Preperation
		print "\n<DIV ALIGN=\"CENTER\">\n";

		# Loop
		foreach $address (sort(keys(%scripts)))
		{
			# Display
			$tmpl->Show('TagTabbed',
			      MLINK => $surl . '/' . $scripts{$address} . $inline,
			      MNAME => $address);
		}

		# Finish
		print "\n</DIV>\n<BR>\n";


		# Access administration
		if ($sparam =~ /^$scripts{'Access'}/i)
		{
			# Title display
			$tmpl->ShowMain($admin{'TagAccessTitle'});

			# Logic Tree
			# Access Control
			if (defined($param->{'ROOM'}))
			{ AccessControl($database, $cgi, $param, $table, $tmpl, \%admin, $purl, \@allow, \@block); }

			# Realm Listing
			else
			{ RealmSelect($database, $cgi, $param, $table, $tmpl, \%admin); }
		}

		# Attribute administrator
		elsif ($sparam =~ /^$scripts{'Attribute'}/i)
		{
			# List data
			DataCycler($database, $cgi, $param, $table, $tmpl, \%admin, $purl);
		}

		# Auditing access
		elsif ($sparam =~ /^$scripts{'Audit'}/i)
		{
			# Title
			$tmpl->ShowMain($admin{'TagAuditTitle'});


			# Display something
			if (defined($param->{$admin{'TxtSearch'}}))
			{
				# Display information
				AuditHandle($database, $cgi, $param, $tmpl, \%admin, $purl, \@ops, \@level);
			}

			else
			{
				# List data
				AuditSelect($database, $cgi, $param, $table, $tmpl, \%admin);
			}
		}

		# Contact elements
		elsif ($sparam =~ /^$scripts{'Contact'}/i)
		{
			# Handler
			ContactHandler($database, $cgi, $param, $table, $tmpl, \%admin);
		}

		# Frequently Asked Questions (FAQ)
		elsif ($sparam =~ /^$scripts{'FAQ'}/i)
		{
			# Title display
			$tmpl->ShowMain($admin{'TagFaqTitle'});
			
			# Detailed view
			if (defined($param->{'FAQID'}))
			{
				# Handling of questions themselves
				FaqQuestion($database, $cgi, $param, $table, $tmpl, \%admin);
			}

			# General overview
			else
			{
				# General Listing
				FaqListing($database, $cgi, $param, $table, $tmpl, \%admin, $purl);
			}
		}

		# Genre elements
		elsif ($sparam =~ /^$scripts{'Genre'}/i)
		{
			# Title
			$tmpl->ShowMain($admin{'TagGenreTitle'});


			# Which to do
			unless ((defined($param->{'Details'}))
			 || (defined($param->{'DESCR'})))
			{
				# Genre handler
				GenreViewer($database, $cgi, $param, $table, $tmpl, \%admin, $purl);
			}

			else
			{
				# Genre handler
				GenreHandler($database, $cgi, $param, $table, $tmpl, \%admin, $purl);
			}
		}

		# Document administration
		elsif ($sparam =~ /^$scripts{'Template'}/i)
		{
			# Title display
			$tmpl->ShowMain($admin{'TagDocTitle'});

	
			# Document selection and modification
			if (defined($param->{'DOCTYPE'}))
			{

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

				# 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();

					}
				}

				# Display and modify controls	
				DocumentSelect($database, $cgi, $param, $table, $tmpl, \%admin);
				DocumentModify($database, $cgi, $param, $table, $tmpl, \%admin);
			}

			# Simply select
			else
			{
				DocumentSelect($database, $cgi, $param, $table, $tmpl, \%admin);
			}
		}

		# Font handler
		elsif ($sparam =~ /^$scripts{'Font'}/i)
		{
			# Font handling subsystem
			FontHandler($database, $cgi, $param, $table, $tmpl, \%admin);
		}

		# Gummy handler
		elsif ($sparam =~ /^$scripts{'Gummy'}/i)
		{
			# Title
			$tmpl->ShowMain($admin{'TagGummyTitle'});

			# Class selection
			unless (defined($param->{'CLASS'}))
			{
				# Selection of class
				GummySelect($database, $cgi, $param, $table, $tmpl, \%admin);
			}

			else
			{
				# Handle addition and removal
				GummyHandler($database, $cgi, $param, $table, $tmpl, \%admin);
			}
		}

		
		# Poll handler
		elsif ($sparam =~ /^$scripts{'Poll'}/i)
		{
			# Title
			$tmpl->ShowMain($admin{'TagPollTitle'});

			# Poll selection
			if ((defined($param->{'POLL'}))
			 || (defined($param->{$admin{'TxtAlter'}})))
			{
				# Poll handler
				PollHandler($database, $cgi, $param, $table, $tmpl, \%admin);
			}

			else
			{
				# Select a poll
				PollSelect($database, $cgi, $param, $table, $tmpl, \%admin);
			}
		}


		# Realm administration
		elsif ($sparam =~ /^$scripts{'Realm'}/i)
		{
			# Title
			$tmpl->ShowMain($admin{'TagRealmTitle'});

			# Realm additions
			if (defined($param->{'ADDITION'}))
			{
				RealmAddition($database, $cgi, $param, $table, $tmpl, \%admin);
			}

			# Realms displays
			else
			{
				# Defaults to public if nothing specified
				RealmDisplay($database, $cgi, $param, $tmpl, \%admin, $purl);
			}
		}


		# Banner and HTML rotation
		elsif ($sparam =~ /^$scripts{'Rotate'}/i)
		{
			# Title
			$tmpl->ShowMain($admin {'TagRotateTitle'});

			# Rotation summary
			RotateSummary($database, $cgi, $param, $table, $tmpl, \%admin, $purl);

			# Rotation Details
			RotateDetails($database, $cgi, $param, $table, $tmpl, \%admin, $purl) if (defined($param->{'RENTRY'}));
		}


		# Weblog administration
		elsif ($sparam =~ /^$scripts{'Weblog'}/i)
		{
			# Weblog handling system
			WeblogHandler($database, $cgi, $param, $table, $tmpl, \%admin, \@clear);
		}


		# Words
		elsif ($sparam =~ /^$scripts{'Words'}/i)
		{
			# Administrative word filter
			WordHandler($database, $cgi, $param, $table, $tmpl, \%admin, \@clear);
		}


		# User administration
		else
		{
			# Title
			$tmpl->ShowMain($admin{'TagUserTitle'});

			# Handles the primary modifications of a user
			if (defined($param->{'LOGIN'}))
			{
				UserModify($database, $cgi, $param, $table, $tmpl, \%admin, $purl);
			}

			# Basic search with user selection and removal
			elsif (defined($param->{'SEARCH'}))
			{
				UserSelect($database, $cgi, $param, $tmpl, \%admin, $purl);
			}

			# Basic Prompt
			else
			{ 
				UserSearch($database, $cgi, $param, $table, $tmpl, \%admin);
			}
		}
		
		# 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 $table    = shift;					# Table handler
	my $template = shift;					# Template handling
	my $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self referencing link

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

	my $audit;						# Audit handle
	my $handle;						# Handle name
	my $instance;						# Instances

	my $badd     = '>>';					# Addition button
	my $brem     = '<<';					# Remove button
	my $super    = '16';					# Supervisory access

	my $inline;						# Inline parameter list

	my @allow;						# Allowed
	my @block;						# Blocked


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

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

	# Data handling 
	# Evaluation block
	eval
 	{
		# Additions
		if (defined($param->{$badd}))
		{
			# 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();
			}

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

		# Removal
		elsif (defined($param->{$brem}))
		{
			# 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();
			}

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


		# 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($badd) . "<BR><BR>" . $cgi->submit($brem);


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

	# Display realm
	print "<CENTER>"
	 . $tmpl->Pass('TagList', MLINK=>"$url$inline", MNAME=>$admin->{TxtBack})
	 . "</CENTER>\n<BR>\n";

	# Table display
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagAccessRealm'} . $param->{'ROOM'}));
	$table->MakeBlank();

	# Primary widgets
	$table->MakeSingle($tmpl->PassSub($admin->{'TagAccessPri'}));
	$table->MakeTertiary($wblock, $wcntrl, $wallow);
	$table->MakeBlank();

	# Custom addition
	$table->MakeSingle($tmpl->PassSub($admin->{'TagAccessAlt'}));
	$table->MakeSingle($cgi->textfield('NALLOW', '', 15, 10));

	# Close table
	$table->MakeBottom();


	# 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 $template = shift;					# Template handling
	my $admin    = shift;					# Administrative hash
	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 $audit;						# Audit handle
	my $res;						# Results set
	my $statement;						# Query handle

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

	my $inline;						# Inline parameter list

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

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

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


	# 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";

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


	# Add in
	$req = $req
	 . " 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 = $realm . $action . " (AuditCode >= ? AND AuditCode <= ?) ";

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

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

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

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

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

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


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

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

	# User
	if ((defined($param->{'AUSER'}))
	  && (length($param->{'AUSER'}) > 2))
	{
		# Append
		$req = $req . " 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 = $req . " AND (LOWER(AuditRealm)=? OR LOWER(AuditAdditional)=?) ";	

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

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

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


	# Inline handle
	$inline = $param->EmbedInline($param->Flat());

	# Back button
	print "<CENTER>"
	 . $tmpl->Pass('TagList', MLINK=>"$url$inline", MNAME=>$admin->{TxtBack})
	 . "</CENTER>\n<BR>\n";


	# Record attempt
	$audit->AccessAudit($param->{'USER'});

	# Pass through query
	if ($database->Pull(\$statement, $req, @pass))
	{
		# Reset counter
		$count = 0;


		# HTML
		print "<CENTER><TABLE WIDTH=\"95%\">\n";

		# Rows
		print " <TR>\n"
		    . "  <TD>" . $tmpl->PassSub($admin->{'TagAuditHeadUser'})   . "</TD>\n"
		    . "  <TD>" . $tmpl->PassSub($admin->{'TagAuditHeadHost'})   . "</TD>\n" 
		    . "  <TD>" . $tmpl->PassSub($admin->{'TagAuditHeadRealm'})  . "</TD>\n"
		    . "  <TD>" . $tmpl->PassSub($admin->{'TagAuditHeadAction'}) . "</TD>\n"
		    . "  <TD>" . $tmpl->PassSub($admin->{'TagAuditHeadEvent'})  . "</TD>\n"
		    . "  <TD WIDTH=\"150\">" . $tmpl->PassSub($admin->{'TagAuditHeadDate'})   . "</TD>\n"
                    . " </TR>\n";


		# 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));


			# Row
			print " <TR CLASS=\"$bgcolor\" VALIGN=\"TOP\">\n"
			    . "  <TD><B>" . $res->{'PuppeteerLogin'} . "</B></TD>\n"
			    . "  <TD ALIGN=\"CENTER\">" . $res->{'PuppeteerHostname'} . "</TD>\n"
			    . "  <TD>" . $res->{'AuditRealm'} . "</TD>\n"
			    . "  <TD ALIGN=\"CENTER\">" . $res->{'AuditAction'} . "</TD>\n"
			    . "  <TD>" . $err . "</TD>\n"
			    . "  <TD ALIGN=\"CENTER\">" . $res->{'AuditTimestamp'} . "</TD>\n"
			    . " </TR>\n";
		}


		# Finish
		$statement->finish();

		# Close table
		print "</TABLE></CENTER>\n<BR>\n";
	}
}


#####################
# 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 $table    = shift;					# Table handler
	my $template = shift;					# Template handling
	my $admin    = shift;					# Administrative hash

	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()), "\n";

	# Information display
	$table->MakeTop();

	# Operations and level
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagAuditSearch'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditOps'}),
		$op
	 );
	$table->MakeBlank();

	# Level
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditLevel'}),
		$level
	 );
	$table->MakeBlank();
	

	# Restrictions for code, user and realm
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagAuditRestrict'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditCode'}),
		$cgi->textfield('ACODE', '', 4, 4)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditUser'}),
		$cgi->textfield('AUSER', '', 20, 10)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditRealm'}),
		$cgi->textfield('AROOM', '', 20, 30)
	 );
	$table->MakeBlank();


	# Timings
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagAuditTime'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditStart'}),
		$cgi->textfield('ASTART', $start, 15, 11)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagAuditEnd'}),
		$cgi->textfield('ASTOP', $end, 15, 11)
	 );
	$table->MakeBlank();


	# Finish up
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtSearch'}));
	$table->MakeBottom();
	

	# 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 $table    = shift;					# Auditing table
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative hash

	my $statement;						# Database statement

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

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


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

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


	# 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();

			}
		}
	}



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


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


	# Primary title
	$tmpl->ShowMain($admin->{'TagContactTitle'});

	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagContactList'}));

	# 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 (uc($contact->{'ContactType'}) ne uc($type))
			{
				# Massage
				$type = ucfirst($contact->{'ContactType'});

				# Display
				$table->MakeBlank();
				$table->MakeSingle($tmpl->PassSub($type));
			}	


			# Standard display
			$table->MakeValid(
				"<B>$contact->{ContactName}<B> (<I>$contact->{ContactFilter}</I>)",
				$cgi->checkbox(-name=>$name, -label=>'')
			 );
		}
	}

	# Finish Query
	$statement->finish();

	# Submit
	$table->MakeBlank();
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtDelete'}));


	# Spacer
	$table->MakeBlank();
	$table->MakeBlank();


	# Addition entries
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagContactAdd'}));

	# Widgets
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagContactName'}),
		$cgi->textfield('CONTACT', '', 15, 25)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagContactType'}),
		$cgi->textfield('TYPE', '', 10, 10)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagContactFilter'}),
		$filter
	 );
	$table->MakeBlank();
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtCreate'}));

	# End table
	$table->MakeBottom();


	# 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 $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative script
	my $url      = shift;					# Self referencing link
	my $choice   = shift;					# Choice of hash to open

	my $inline;						# Inline parameters
	my $section;						# Singular section
	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", 

	 $type
	);


	# Cycle through sections
	# Pre Display
	print "<DIV ALIGN=\"CENTER\">";

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

		# Display entry
		$tmpl->Show('TagList', MLINK=>"$url$inline",  MNAME=>ucfirst($section));
	}

	# Post display
	print "</DIV>\n<BR>\n";


	# Determine section to show
	$section = (defined($param->{'SECT'})) ? $param->{'SECT'} : $sections[0];


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



	# Information display
	# Title
	$tmpl->ShowMain(ucfirst($section));

	# Table top
	$table->MakeTop();

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

	# Submit and control
	$table->MakeValid($cgi->submit($admin->{'TxtAlter'}), $cgi->reset($admin->{'TxtReset'}));
	$table->MakeBottom();


	# 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 $table    = shift;					# Table handler
	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 $audit;						# Audit handle
	my $choice;						# Current selection
	my $data;						# Data of hash
	my $first;						# First template
	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/)
				{
					# Additional spaces
					unless (defined($first))
					{
						# Spacer
						$table->MakeBlank();

						# Define and ignore
						$first = 'true';
					}

					# Display of textareas
					$table->MakeSingle($tmpl->PassTitle($choice));
					$table->MakeSingle($cgi->textarea(
						-name    => "$dbm$choice",
						-rows    => 10,
						-columns => 85,
						-wrap    => 'OFF',
						-default => $data)
					 );
					$table->MakeBlank();
				}

				# Standard
				else
				{
					$table->MakeInvalid(
						$tmpl->PassSub($choice),
						$cgi->textfield("$dbm$choice",$data,50,4000)
					 );
				}


				# 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/)
			{
				# Additional spaces
				unless (defined($first))
				{
					# Spacer
					$table->MakeBlank();

					# Define and ignore
					$first = 'true';
				}

				# Display of textareas
				$table->MakeSingle($tmpl->PassTitle($choice));
				$table->MakeSingle($cgi->textarea(
					-name    => "$dbm$choice",
					-rows    => 10,
					-columns => 85,
					-wrap    => 'OFF',
					-default => $hash{$choice})
				 );
				$table->MakeBlank();
			}

			# Standard
			else
			{
				$table->MakeValid(
					$tmpl->PassSub($choice),
					$cgi->textfield("$dbm$choice",$hash{$choice},50,4000)
				 );
			}
		}
	}

	# 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);
	}
}


#####################
# Document Modify
#
# Simply modifies documents available and allows one to select them.

sub DocumentModify
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative script

	my $audit;						# Audit handle
	my $document;						# Document contents
	

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

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

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


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


	# HTML Display
	$table->MakeTop();
	$table->MakeSingle(
		$tmpl->PassSub($admin->{'TagDocCurrent'} . $param->{DOCTYPE})
	 );

	# Core information
	$table->MakeSingle($cgi->textarea(
		-name    => 'DOCDATA',
		-rows    => 25,
		-columns => 85,
		-wrap    => 'off',
		-default => $document)
	 );
	$table->MakeBlank();

	# Submit buttons
	$table->MakeSingle($cgi->submit($admin->{'TxtSave'}));
	$table->MakeBottom();


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


#####################
# DocumentSelect
#
# Simply lists documents available and allows one to select them.

sub DocumentSelect
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter list
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative script

	my $path     = $database->{'DBHASH'};			# Locale cache path
	my $statement;						# Query statement
	my $res;						# Query results
	
	my $list;						# Default list unit
	my $widget;						# Widget display

	my @list;						# List of fields
	
	my %copy;						# Copy hash
	my %docs;						# Hash of documents

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

	# 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())
	{
		# Push onto array
		push(@list, $res->{'TagName'});

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

	# Finish statement
	$statement->finish();


	# Berkely DB handling
	# Check for need
	if ((defined($path))
	 && (-d $path))
 	{
		# Adjust path
		$path .= '/' . $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);
	}
	

	# Determin defaults
	$list   = (defined($param->{'DOCTYPE'})) 
	  ? $param->{'DOCTYPE'}
	  : $list[0];	

	# Generate widget
	$widget = $cgi->popup_menu('DOCTYPE', \@list, $list);


        # Form data		
	print $cgi->start_form();
	print $param->EmbedNormal(
		USER  => $param->{'USER'}, 
		CRYPT => $param->{'CRYPT'}
	 ), "\n";


	# Selection display
	$table->MakeTop();

	# Display information
	$table->MakeSingle(
		$tmpl->PassSub($admin->{'TagDocSelect'}),
	 );
	$table->MakeValid($widget, $cgi->submit($admin->{'TxtOpen'}));
	$table->MakeBottom();

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


	# Spacer
	print "<BR><BR>\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 $table    = shift;					# Auditing table
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self referencing links

	my $audit;						# Auditing handle
	my $res;						# Results list
	my $statement;						# Database statement

	my $inline;						# Inline parameters
	my $ogroup;						# Old group


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

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

	# 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();
		}
	}


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

	# Table top
	$table->MakeTop();


	# 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())
	{
		# Title
		$table->MakeSingle($tmpl->PassTitle($admin->{'TagFaqListing'}));

		# 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'}))
				 	{
						# Print title
						$table->MakeBlank();
						$table->MakeSingle($tmpl->PassSub(ucfirst($res->{'FaqGroup'})));

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

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

					# Entry
					$table->MakeValid(
						"<A HREF=\"$url$inline\">$res->{FaqQuestion}</A>",
						$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();

		}

		# End table
		$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtDelete'}));
		$table->MakeBlank();
		$table->MakeBlank();
		$table->MakeBottom();
	}


	# Additions
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagFaqAdd'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagFaqNQuestion'}),
		$cgi->textfield('NFAQQ', '', 45, 255)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagFaqNGroup'}),
		$cgi->textfield('NFAQG', '', 25, 25)
	 );

	# Additions button
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtCreate'}));

	# Table bottom
	$table->MakeBottom();

	# 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 $table    = shift;					# Auditing table
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative hash

	my $audit;						# Auditing handle

	my $inline;						# Inline parameters
	my $url;						# Self referencing links

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


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

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

	# 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 title
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagFaqDetail'} . $param->{'FAQID'}));

	# Display information
	# Safety check
	if (defined($question))
	{
		# Question
		$table->MakeValid(
			$tmpl->PassSub($admin->{'TagFaqQuestion'}),
			$cgi->textfield('FAQQ', $question, 45, 255)
		 );

		# Group
		$table->MakeValid(
			$tmpl->PassSub($admin->{'TagFaqGroup'}),
			$cgi->textfield('FAQG', $group, 25, 25)
		 );
		$table->MakeBlank();

		# Answer
		$table->MakeSingle($tmpl->PassTitle($admin->{'TagFaqAnswer'}));
		$table->MakeSingle($cgi->textarea('FAQA', $answer, 10, 40));
		$table->MakeBlank();

		# Update button
		$table->MakeSingle($cgi->submit($admin->{'TxtAlter'}));
	}

	# Table bottom
	$table->MakeBottom();

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


#####################
# FontHandler
#
# Will simply display the font colour, face and size as to allow the administrator
# to add and remove them as need be.   Similiar in function to the gummy handler.

sub FontHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $table    = shift;					# Table parameter
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my $audit;						# Auditing handle
	my $res;						# Results			
	my $statement;						# Statement handle

	my $ctype;						# Current working type
	my $name;						# Font name
	my $type;						# Font type


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

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


	# Additions
	if ((defined($param->{'NAME'}))
	 && (defined($param->{'CODE'}))
	 && (length($param->{'NAME'}) > 0))
	{
		# Evaluation block
		eval
		{
			# Force lower case
			$param->{'TYPE'} = lc($param->{'TYPE'});

			# Insert information
			$database->Write("INSERT INTO Font
				(FontName,
				 FontType,
				 FontCode)
				VALUES (?,?,?)",

			 $param->{'NAME'},
			 lc($param->{'TYPE'}),
			 $param->{'CODE'}
			);


			# Audit
			$audit->FontAdd($param->{'TYPE'} . ':' . $param->{'NAME'});

			# Cleanup
			$param->Cleanup('CODE','NAME','TYPE');


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

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

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

		}
	}



	# Query management
	# Prepare and execute
	$database->Pull(\$statement, "SELECT 
		 FontName AS \"FontName\",
		 FontType AS \"FontType\",
		 FontCode AS \"FontCode\"
		FROM  Font
		ORDER BY FontType, FontName");


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


	# HTML display
	# Title
	$tmpl->ShowMain($admin->{'TagFontTitle'});


	# Table Top
	$table->MakeTop();

	# Cycle through gummies
	while ($res = $statement->fetchrow_hashref())
	{
		# Title
		if ((!defined($type))
		 or ($type ne $res->{'FontType'}))
		{
			# Uppercase
			$type = uc($res->{'FontType'});

			$table->MakeBlank();
			$table->MakeSingle($tmpl->PassTitle($type));


			# Alternate for next pass
			$type = $res->{'FontType'};
		}

		# Name handling
		$name = $res->{FontName};
		$name =~ s/\s//gs;

		# Handle removal
		if (defined($param->{$name}))
		{
			# Evaluation block
			eval
			{
				# Remove gummy
				$database->Write("DELETE FROM Font
					WHERE FontName=?
					  AND FontType=?",

				 $res->{'FontName'},
				 $res->{'FontType'}
				);

				# Commit changes
				$database->Commit();

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


				# Audit action
				$audit->FontRem($res->{'FontType'} . ':' . $res->{'FontName'});
			};

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

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

			}
		}

		# Display if not removed
		else
		{
			# Display row
			if ($type eq 'color')
			{
				# Display colour
				print " <TR>\n",
				      "  <TD><B>$res->{FontName}</B></TD>\n",
				      "  <TD BGCOLOR=\"$res->{FontCode}\" WIDTH=15>&nbsp;</TD>\n",
				      "  <TD><CENTER>", $cgi->checkbox(-name=>$name, -label=>''), "</CENTER></TD>\n",
				      " </TR>\n";
			}
			else
			{
				# Current working type
				$ctype = uc($type);

				# Simply show a blank space
				print " <TR>\n",
				      "  <TD><FONT $ctype=\"$res->{FontCode}\">$res->{FontName}</FONT></TD>\n",
				      "  <TD>&nbsp;</TD>\n",
				      "  <TD><CENTER>", $cgi->checkbox(-name=>$name, -label=>''), "</CENTER></TD>\n",
				      " </TR>\n";
			}
		}
	}

	# Remove button
	$table->MakeBlank();
	$table->MakeValid('&nbsp', $cgi->submit($admin->{'TxtDelete'}));

	# Table bottom
	$table->MakeBottom();

	
	# End query
	$statement->finish();


	# Widget
	my @radiotype  = qw(Color Face Size); 
	my $radiofield = $cgi->radio_group('TYPE', \@radiotype, $radiotype[0], 'true');


	# Spacer
	print "<BR>\n<BR>\n";

	# Form handle
	# Addition section
	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagFontAddition'}));
	$table->MakeBlank();

	# Display rows
	$table->MakeValid( 
		$tmpl->PassSub($admin->{'TagFontName'}),
		$cgi->textfield('NAME', '', 20, 20)
	 );
	$table->MakeValid( 
		$tmpl->PassSub($admin->{'TagFontCode'}),
		$cgi->textfield('CODE', '', 20, 35)
	 );
	$table->MakeBlank();


	# Type selection
	$table->MakeValid( 
		$tmpl->PassSub($admin->{'TagFontType'}),	
		$radiofield
	 );
	$table->MakeBlank();


	# Control buttons
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtCreate'}));
	$table->MakeBottom();
	
	
	# End form
	print $cgi->end_form(), "\n";
}


#####################
# Genre Handler
#
# 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.
#
# Being the handler, this will allow you to modify information specifically.

sub GenreHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $table    = shift;					# Auditing table
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self-referenced url

	my $audit;						# Auditing handle
	my $inline;						# Inline parameters

	my $descr;						# Description
	my $genre;						# Genre name
	my $tsub = '<root>';					# Sub-types
	my $junk;
	my $type;						# Types/classifications

	my %args;						# Arguments


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

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


	# Split
	($type, $genre) = split(/ - /, $param->{'SGENRE'});


	# Go in deep
	# Swap around
	if (defined($param->{'TYPE'}))
	{
		# Different level
		$tsub = $type;
		$type = $param->{'TYPE'};
	}


	# Pull information
	if (defined($genre))
	{
		# Change description
		if (defined($param->{'DESCR'}))
		{
			# Evaluation block
			eval
			{
				# Confirmation
				$descr = $param->{'DESCR'};
				$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,
				 $genre,
				 lc($type),
				 $tsub
				);

				# Audit entry 
				$audit->GenreUpdate($type . '-' . $tsub . ':' . $genre);


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

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

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


		# Pull description
		($descr) = $database->DataGet("SELECT GenreDescription
			FROM Genre
			WHERE GenreName=?
			  AND GenreType=?
			  AND GenreSub=?",
		 $genre,
		 lc($type),
		 $tsub
		);


		# Parameter handling
		# Initial
		%args = (
			USER  => $param->{'USER'},
			CRYPT => $param->{'CRYPT'}
		 );

		# Additional
		$args{'TYPE'} = $param->{'TYPE'} if (defined($param->{'TYPE'}));

		# Embed parameters
		$inline = $param->EmbedInline(%args);

		# Display initial
		print "<DIV ALIGN=\"center\">"
		   .  $tmpl->Pass('TagTabbed', MLINK => $url . $inline, MNAME => $admin->{'TxtBack'})
		   .  "</DIV>\n<BR><BR>\n";


		# Information Emebed
		print $cgi->start_form();
		print $param->EmbedNormal($param->Flat(), DETAIL=>'True');

		# HTML
		$table->MakeTop();
		$table->MakeSingle($tmpl->PassTitle($admin->{'TagGenreDesc'} . ': ' . $genre));

		# Title
		# Widgets
		$table->MakeSingle($cgi->textarea('DESCR', $descr, 10, 70));
		$table->MakeBlank();

		$table->MakeValid($cgi->submit($admin->{'TxtSave'}), $cgi->reset($admin->{'TxtReset'}));
		$table->MakeBottom();


		# Form end
		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.
#
# This is the viewer, which allows for the creation of these things only.

sub GenreViewer
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $table    = shift;					# Auditing table
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self-referencing link

	my $audit;						# Auditing handle
	my $confirm;						# Confirmation of removal
	my $count;						# Image counter
	my $genre;						# Widget listing of genres
	my $type;						# Main types
	my $tsub;						# Sub-types

	my $inline;						# Inline parameters

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

	my %args;						# Aguments list


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

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


	# Data handling
	# Creation
	if (defined($param->{$admin->{'TxtCreate'}}))
	{
		# Addition
		if ((defined($param->{'NGENRE'}))
		 && (length($param->{'NGENRE'}) > 2))
		{
			# Evaluation
			eval
			{
				# Re-arrange accordingly
				if (defined($param->{'TYPE'}))
				{
					# Corrective measures
					$param->{'NSUB'}  = $param->{'NTYPE'};
					$param->{'NTYPE'} = $param->{'TYPE'};
				}

				# Safety
				$param->{'NSUB'} = (defined($param->{'NSUB'}))
			          ? $param->{'NSUB'}
				  : '<root>';
				
				# Insertion
				$database->Write("INSERT INTO Genre
					(GenreName,
					 GenreType,
					 GenreSub)
					VALUES(?,?,?)",

				 $param->{'NGENRE'},
				 lc($param->{'NTYPE'}),
				 $param->{'NSUB'}
				);

				# Auditing entry
				$audit->GenreAdd($param->{'NTYPE'} . 
					'-' .  $param->{'NSUB'} .
					':' .  $param->{'NGENRE'}
				 );

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

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

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

	# Removal
	elsif ((defined($param->{$admin->{'TxtDelete'}}))
	  && (defined($param->{'CONFIRM'})))
	{
		# Split
		($type, $genre) = split(/ - /, $param->{'SGENRE'});

		# Remove
		if (defined($genre))
		{
			# Evaluation
			eval
			{
				# Sub-type handling
				# Initial check
				if (defined($param->{'TYPE'}))
				{
					# Re-arrange variables
					$tsub = $type;
					$type = $param->{'TYPE'};
				}

				# Safety
				$tsub = (defined($tsub)) ? $tsub : '<root>';


				# Information handling
				# Remove entry
				if ($tsub ne '<root>')
				{
					# Specific non-destructive
					$database->Write("DELETE FROM Genre
						WHERE GenreName=?
						  AND GenreType=?
						  AND GenreSub=?",

					 $genre,
					 lc($type),
					 $tsub
					);
				}
				else
				{
					# Burn baby burn!
					$database->Write("DELETE FROM Genre
						WHERE GenreName=?
						  AND GenreType=?",

					 $genre,
					 lc($type),
					);
				}


				# Auditing entry
				$audit->GenreRem($param->{'$type'} . 
					'-' .  $tsub .
					':' .  $genre 
				 );


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

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

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

	# Cleanup
	$param->Cleanup('CONFIRM', 'NTYPE', 'NSUB', 'NGENRE');


	# Button handling
	# Initial parameters
	%args = (USER  => $param->{'USER'},
		 CRYPT => $param->{'CRYPT'}
	 );
	
	# Main button always wins
	$inline = $param->EmbedInline(%args);

	# Display initial
	print "<DIV ALIGN=\"center\">" .  $tmpl->Pass('TagTabbed',
		MLINK => $url . $inline,
		MNAME => $types[0]
	 );

	# Handle additional links
	for ($count=2; $count < @types; $count++)
	{
		# Display individual handles
		print " " . $tmpl->Pass('TagTabbed',
			MLINK => $url . $inline . '&TYPE=' . lc($types[$count]),
			MNAME => $types[$count] 
		 );
	}

	# Close center
	print "</DIV>\n<BR><BR>\n";


	# Work out lists
	# Pull list
	unless (defined($param->{'TYPE'}))
	{
		# This is for root level genres
		$database->GetList(\@genres, "SELECT UPPER(GenreType) || ' - ' || GenreName AS \"List\"
			FROM Genre
			WHERE GenreSub='<root>' 
			ORDER BY GenreType, GenreSub, GenreName");
	}

	# Now in a sub-section handling
	else
	{
		# This will getch sub-types genres
		$database->GetList(\@genres, "SELECT GenreSub || ' - ' || GenreName AS \"List\"
			FROM Genre
			WHERE GenreSub != '<root>'
			  AND GenreType=?
			ORDER BY GenreType, GenreSub, GenreName",

		 $param->{'TYPE'}
		);

		# Additionally fetch sub-types
		$database->GetList(\@types, "SELECT GenreName
			FROM Genre
			WHERE GenreSub='<root>'
			  AND GenreType=?
			ORDER BY GenreName",

		 $param->{'TYPE'}
		);
	}

	# Clear out
	shift(@types) unless (defined($param->{'TYPE'}));


	# Generate widgets
	$genre   = $cgi->scrolling_list('SGENRE', \@genres, $genres[0], 10, 'true');
	$type    = $cgi->radio_group('NTYPE', \@types, $types[0], 'true');

	# Generate confirmatory button
	$confirm = $cgi->submit($admin->{'TxtDetails'}) . '<BR><BR>' 
	  . $cgi->submit($admin->{'TxtDelete'}) . '&nbsp;'
	  . $cgi->checkbox(-name=>'CONFIRM', -label=>'');



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

	# Main window
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagGenreList'}));


	# Widget display
	$table->MakeValid($genre, $confirm, 'middle');

	# Spacing
	$table->MakeBlank();
	$table->MakeBlank();

	# Addition
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagGenreAdd'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagGenreName'}),
		$cgi->textfield('NGENRE', '', 20, 25)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagGenreOrder'}),
		$type
	 );
	$table->MakeBlank();

	$table->MakeValid($cgi->submit($admin->{'TxtCreate'}));
	$table->MakeBottom();

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


#####################
# GummyHandler
#
# Very simple sub-routine that will simply allow the user to select the class
# of gummies to search for.

sub GummyHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $table    = shift;					# Table parameter
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my $audit;						# Auditing handle
	my $res;						# Results			
	my $statement;						# Database statement

	my $ident;						# Gummy indentier
	my $group    = 'None';					# Gummy grouping 
	my $name;						# Name of gummy
	my $realm    = 'system';				# Realm search
	my $macro;						# Display of macros


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

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


	# Addition
	# Make sure gummy is defined
	if ((defined($param->{'GUMMY'}))
	 && (defined($param->{'GPATH'}))
	 && (length($param->{'GUMMY'}) > 2))
	{
		# Evaluate
		eval
		{
			# Group handler
			$param->{'GROUP'} = lc($param->{'GROUP'});
			$param->{'GROUP'} = (length($param->{'GROUP'}) > 2) ? $param->{'GROUP'} : undef;

			# Macro handler
			$param->{'MACRO'} = ((defined($param->{'MACRO'})) && (length($param->{'MACRO'}) > 2)) ? uc($param->{'MACRO'}) : undef;

			# Create entry
			$database->Write("INSERT INTO Gummy
				(GummyName,
				 GummyPath,
				 GummyClass,
				 GummyIdent,
				 GummyMacro,
				 RealmName)
				VALUES (?,?,?,?,?,?)",

			 $param->{'GUMMY'},
			 $param->{'GPATH'},
			 lc($param->{'CLASS'}),
			 $param->{'GROUP'},
			 $param->{'MACRO'},
			 $realm);


			# Audit action
			$audit->GummyAdd($param->{'GUMMY'}, $realm);

			# Cleanup
			$param->Cleanup('GUMMY','GROUP','GPATH','MACRO');


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

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

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



	# Query management
	# Prepare and execute
	$database->Pull(\$statement, "SELECT 
		 GummyName  AS \"GummyName\",
		 GummyMacro AS \"GummyMacro\",
		 GummyPath  AS \"GummyPath\",
		 GummyIdent AS \"GummyIdent\"
		FROM  Gummy
		WHERE GummyClass=?
		  AND RealmName=?
		ORDER BY GummyIdent, GummyName",

	 lc($param->{'CLASS'}),
	 $realm
	);


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


	# HTML display
	# Table Top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagGummyList'}));

	# Cycle through gummies
	while ($res = $statement->fetchrow_hashref())
	{
		# Padding
		$ident = (defined($res->{'GummyIdent'})) ? $res->{'GummyIdent'} : 'None';

		# Title
		if ($ident ne $group)
		{
			# Alternate
			$group = $ident;

			# Uppercase
			$ident = ucfirst($ident);


			# Display
			$table->MakeBlank();
			$table->MakeSingle($tmpl->PassSub($ident));
		}

		# Name handling
		$name = $res->{GummyName};
		$name =~ s/\s//gs;

		# Handle removal
		if (defined($param->{$name}))
		{
			# Evaluation
			eval
			{
				# Remove gummy
				$database->Write("DELETE FROM Gummy
					WHERE GummyName=?
					  AND RealmName=?",

				 $res->{'GummyName'},
				 $realm
				);


				# Audit action
				$audit->GummyRem($res->{'GummyName'}, $realm);

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


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

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

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

		# Display if not removed
		else
		{
			# Tune macro display
			$macro = (defined($res->{'GummyMacro'}))
			 ? '<B>' . $res->{'GummyName'} . '</B> ::' . $res->{'GummyMacro'} . '::'
			 : '<B>' . $res->{'GummyName'} . '</B>';
			
			# Display row
			$table->MakeTertiary(
				$macro,
				"<IMG SRC=\"$res->{GummyPath}\">",
				"<DIV ALIGN=\"CENTER\">" .  $cgi->checkbox(-name=>$name, -label=>'') . "</DIV>"
			);
		}
	}

	# Finish off
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtDelete'}));
	$table->MakeBottom();



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


	# Spacer
	print "<BR>\n<BR>\n";


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


	# Addition section
	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagGummyAdd'}));
	$table->MakeBlank();

	# Display rows
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagGummyName'}),
		$cgi->textfield('GUMMY', '', 20, 20)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagGummyGroup'}),
		$cgi->textfield('GROUP', '', 25, 25)
	 );

	# Injection code only appears for injections
	if ($param->{'CLASS'} =~ /^inject$/i)
	{
		$table->MakeValid(
			$tmpl->PassSub($admin->{'TagGummyInject'}),
			$cgi->textfield('MACRO', '', 10, 10)
		 );
	}

	# Spacer
	$table->MakeBlank();

	$table->MakeSingle($tmpl->PassSub($admin->{'TagGummyPath'}));
	$table->MakeSingle($cgi->textfield('GPATH', $admin->{'SetPath'}, 75, 200));
	$table->MakeBlank();

	$table->MakeSingle($cgi->submit($admin->{'TxtCreate'}));
	$table->MakeBottom();
	
	
	# End form
	print $cgi->end_form(), "\n";
}


#####################
# Gummy Selection
#
# Very simple sub-routine that will simply allow the user to select the class
# of gummies to search for.

sub GummySelect
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my $widget;						# Displayed widget

	my @class;						# Table array


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

	# Gummy list 
	@class  = qw(Default General Canon Contributor Admin Mod Super Inject);

	# Generate widget
	$widget = $cgi->popup_menu('CLASS', \@class, $class[0]);


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

	# Table display
	# Top and widget
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagGummySelect'}));
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagGummyClass'}),
		$widget
	 );
	$table->MakeBlank();

	# Submit and bottom
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtSelect'}));
	$table->MakeBottom();

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


#####################
# Poll Handler
#
# More specific information handler, will allow for the removel and addition
# of questions.

sub PollHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my $count;						# Number of entries
	my $exist;						# Option exists
	my $insert;						# Insert ID

	my $example;						# Poll example
	my $id;							# Poll ID
	my $name;						# Poll name
	my $question;						# Poll question

	my $range    = '127.0.0.';				# Fake address range
	my $final;						# Final three

	my $audit;						# Auditing handle
	my $res;						# Database results
	my $statement;						# Query handle

	my @ids;						# List of all identifiers.
	my @options;						# List of all options

	my %poll;						# Polling hash


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

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


	# Annoynace of textboxes
	# Pull list
	$database->GetList(\@ids, "SELECT PollID FROM Poll");
	

	# Find match
	foreach $id (@ids)
	{
		# Match found
		if ((defined($param->{"POLL$id"}))
		 || ((defined($param->{'POLL'}))
		  && ($param->{'POLL'} == $id)))
		{
			# Get pretty information
			($name, $question) = $database->DataGet("SELECT
				 PollName,
				 PollQuestion
				FROM Poll
				WHERE PollID=?", $id);

			# Data management
			# Insertion
			if (defined($param->{$admin->{'TxtCreate'}}))
			{
				# Valid information
				if ((defined($param->{'OPTION'}))
				 && (length($param->{'OPTION'}) > 1))
				{
					# Does this exist
					($exist) = $database->DataGet("SELECT OptionName
						FROM PollOption
						WHERE PollID=?
						  AND OptionName=?", 
					 $id, 
					 $param->{'OPTION'});


					# Not a duplicate
					unless (defined($exist))
					{
						# Evaluate
						eval
						{
							# Create
							$database->Write("INSERT INTO PollOption
								(PollID,
								 OptionName)
								VALUES(?,?)",

							 $id,
							 $param->{'OPTION'}
							);

							# What ID
							($insert) = $database->DataGet("SELECT CURRVAL('seqOption')");


							# Final three
							$final = $insert;

							# Insertion range
							if ($final > 255)
							{
								# Calculate the final three
								# Will always return as a valid IP address
								$final = ($final - (int($final / 255) * 255));
							}


							# Joining hack
							$database->Write("INSERT INTO PollVote
								(PollID,
								 VoteHostname,
								 OptionID)
								VALUES(?,?,?)",
	
							 $id,
							 $range . $final,
							 $insert);


							# Audit entry
							$audit->QuestionAdd($id . ':' . $param->{'OPTION'});



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

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

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


			# Management
			$database->Pull(\$statement, "SELECT
				  PollID     AS \"PollID\",
				  OptionID   AS \"OptionID\",
				  OptionName AS \"OptionName\"
				FROM PollOption
				WHERE PollID=?
				ORDER BY OptionID", $id);


			# Show options
			print $cgi->start_form();
			print $param->EmbedNormal($param->Flat(), POLL=>$id);
	
			# Prepare to display
			$table->MakeTop();
			$table->MakeSingle($tmpl->PassTitle($admin->{'TagPollManage'} . $name));

			# Loop and display
			while ($res = $statement->fetchrow_hashref())
			{
				# Remove and do not display
				if (defined($param->{"OPTION$res->{OptionID}"}))
				{
					# Evaluation
					eval
					{
						# Delete
						$database->Write("DELETE FROM PollOption
							WHERE PollID=?
							  AND OptionID=?",

						 $res->{'PollID'},
						 $res->{'OptionID'}
						);

						# Audit entry
						$audit->QuestionRem($res->{'PollID'} . ':' . $res->{'OptionName'});

						# Cleanup
						$param->Cleanup("OPTION$res->{OptionID}");

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

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

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

				else
				{
					# One row at a time
					$table->MakeValid(
						"<b>$res->{OptionName}</b>",
						$cgi->checkbox(-name=>"OPTION$res->{OptionID}", -label=>'')
					 );

					# Insertion
					push(@options, $res->{'OptionName'});
				}
			}
	
			# Options
			$table->MakeBlank();
			$table->MakeSingle($cgi->submit($admin->{'TxtDelete'}));


			# Make seperation
			$table->MakeBlank();
			$table->MakeBlank();

			# Additions
			$table->MakeSingle($tmpl->PassTitle($admin->{'TagPollAdd'}));

			# Questions
			$table->MakeValid(
				$tmpl->PassSub($admin->{'TagPollQuestion'}),
				$cgi->textfield('OPTION', '', 45, 50)
			 );
	
			# Options
			$table->MakeBlank();
			$table->MakeSingle($cgi->submit($admin->{'TxtCreate'}));

	
			# End form
			print $cgi->end_form();


			# Make seperation
			$table->MakeBlank();
			$table->MakeBlank();


			# What do we have
			$count = @options;

			# Something to do
			if ($count > 0)
			{
				# Link with hash
				$database->GetHashPoll(\%poll);

				# Generate options
				my $woption  = $cgi->radio_group('PRES', \@options, $options[0], 'True');
				my $wvote    = $cgi->submit($poll{'TxtVote'});
				my $wfstart  = $cgi->start_form(-action=>$poll{'LnkVote'});
				my $wfend    = $cgi->end_form();
				my $lresults = $poll{'LnkVote'} . '?PID=' . $id;
				  

				# Generate the template for use
				# Copy original
				$example = $poll{'TmplGenerate'};

				# Search and replace
				$example =~ s/MNAME/$name/gs;
				$example =~ s/MQUESTION/$question/gs;
				$example =~ s/WOPTIONS/$woption/gs;
				$example =~ s/WVOTE/$wvote/gs;
				$example =~ s/LRESULTS/$lresults/gs;
				$example =~ s/WSTARTFORM/$wfstart/gs;
				$example =~ s/WENDFORM/$wfend/gs;


				# Display
				print $cgi->start_form();

				# Descriptor
				$table->MakeSingle($tmpl->PassTitle($admin->{'TagPollSample'}));

				# Information
				$table->MakeSingle($cgi->textarea(
				  -name=>'CODE',
				  -default=>$example,
				  -rows=>10,
				  -columns=>60,
				  -wrap=>'OFF')
				 );
				$table->MakeBottom();

				print $cgi->end_form();
			}

			# Exit loop
			last;
		}
	}
}



#####################
# Poll Selection
#
# Straight forward method of first allowing a user to create a general poll
# and then allow for the selection.

sub PollSelect
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter reference
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my $audit;						# Auditing handle
	my $res;						# Database results
	my $statement;						# Query handle


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

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


	# Data management
	# Insertion
	if (defined($param->{$admin->{'TxtCreate'}}))
	{
		# Valid information
		if ((defined($param->{'POLLN'}))
		 && (defined($param->{'POLLQ'}))
		 && (length($param->{'POLLN'}) > 3)
		 && (length($param->{'POLLQ'}) > 3))
		{
			# Eval block
			eval
			{
				# Create
				$database->Write("INSERT INTO Poll
					(PollName,
					 PollQuestion)
					VALUES(?,?)",

				 $param->{'POLLN'},
				 $param->{'POLLQ'}
				);

				# Auditing
				$audit->PollAdd(substr($param->{'POLLN'} . ':' . $param->{'POLLQ'}, 0, 50));


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

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

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



	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		  PollID    AS \"PollID\",
		  PollName  AS \"PollName\"
		FROM Poll
		ORDER BY PollID");


	# Show polls
	print $cgi->start_form();
	print $param->EmbedNormal($param->Flat());
	
	# Prepare to display
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagPollAdmin'}));

	# Loop and display
	while ($res = $statement->fetchrow_hashref())
	{
		# Remove and do not display
		if (defined($param->{"POLL$res->{PollID}"}))
		{
			# Evaluate
			eval
			{
				# Delete
				$database->Write("DELETE FROM Poll
					WHERE PollID=?",

				 $res->{'PollID'}
				);

				# Cleanup
				$param->Cleanup("POLL$res->{PollID}");

				# Audit
				$audit->PollRem($res->{'PollName'});


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

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

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

		else
		{
			# One row at a time
			$table->MakeValid(
				"<b>" . $res->{PollName} . "</b>",
				$cgi->checkbox(-name=>"POLL$res->{PollID}", -label=>'')
			 );
		}
	}
	
	# Options
	$table->MakeBlank();
	$table->MakeValid($cgi->submit($admin->{'TxtAlter'}), $cgi->submit($admin->{'TxtDelete'}));


	# Make seperation
	$table->MakeBlank();
	$table->MakeBlank();

	# Additions
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagPollCreate'}));

	# Questions
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagPollName'}),
		$cgi->textfield('POLLN', '', 20, 20)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagPollQuestion'}),
		$cgi->textfield('POLLQ', '', 45, 250)
	 );
	
	# Options
	$table->MakeBlank();
	$table->MakeSingle($cgi->submit($admin->{'TxtCreate'}));

	# Close up
	$table->MakeBottom();
	
	# End form
	print $cgi->end_form();
}


#####################
# 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 $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash
	
	my @radiolang   = qw(relaxed restricted);		# Language
	my @radioaccess = qw(relaxed restricted list);		# Access
	my @radiotags   = qw(relaxed restricted);		# Tags
	my @radiotype	= qw(yes no hybrid alias);		# Realm type
	my @radiover	= qw(yes no);				# Verified type

	my $radiolang   = $cgi->radio_group('NEWLANG',  \@radiolang,  $radiolang[0],  'true');
	my $radioaccess = $cgi->radio_group('NEWACCESS',\@radioaccess,$radioaccess[0],'true');
	my $radiotags   = $cgi->radio_group('NEWTAGS',  \@radiotags,  $radiotags[0],  'true');
	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";

	# Prompt and display realm name
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagRealmAdd'}));

	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmName'}),
		$cgi->textfield('NEWNAME'  , '', 20, 30)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmAlias'}),
		$cgi->textfield('NEWALIAS'  , '', 20, 30)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmType'}),
		$radiotype
	 );
	$table->MakeBlank();


	# Image restrictions
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmImgHeight'}),
		$cgi->textfield('NEWHEIGHT', '400', 4, 4)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmImgWidth'}),
		$cgi->textfield('NEWWIDTH' , '400', 4, 4)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmImgSize'}),
		$cgi->textfield('NEWSIZE' , '50', 4, 3)
	 );
	$table->MakeBlank();

	# Language selection
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmLang'}),
		$radiolang
	 );
	$table->MakeBlank();


	# Verified
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmVerified'}),
		$radiover
	 );
	$table->MakeBlank();

	# Access type
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmAccess'}),
		$radioaccess
	 );
	$table->MakeBlank();

	# Tag enforcement
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmTag'}),
		$radiotags
	 );
	$table->MakeBlank();


	# Submit and reset
	$table->MakeValid($cgi->submit($admin->{'TxtCreate'}), $cgi->reset($admin->{'TxtReset'}));
	$table->MakeBottom();

	# 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 $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self referencing links
	
	my $audit;						# Audit handle 
	my $statement; 						# Query handle

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

	my $type;						# Realm type
	my $realm;						# Realm name modified
	my $status;						# Realm status

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

	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,
				 RealmLanguage,
				 RealmAccess,
				 RealmVerified,
				 RealmPublic,
				 RealmTags,
				 RealmImageHeight,
				 RealmImageWidth,
				 RealmImageSize)
				VALUES (?,?,?,?,?,?,?,?,?,?)", 

			 $param->{'NEWNAME'}, 
			 $param->{'NEWALIAS'}, 
			 $param->{'NEWLANG'},
			 $param->{'NEWACCESS'}, 
			 $param->{'NEWVER'},
			 $param->{'NEWTYPE'},
			 $param->{'NEWTAGS'}, 
			 $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"
	 );

	# Display links
	print "<CENTER>";

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

		# Display link
		$tmpl->Show('TagList', MLINK=>"$url$inline", MNAME=>ucfirst($type));
	}

	# Spacer
	print "&nbsp;";

	# Mandatory creation link
	$tmpl->Show('TagList', MLINK=>"$url$icreate", MNAME=>$admin->{TxtCreate});

	# Close displau
	print "</CENTER>\n<BR>\n";


	# 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'}
	);


	# Start form
	print $cgi->start_form();

	# Table name
	print "<CENTER><TABLE WIDTH=\"95%\">";

	# Display header
	print " <TR>\n";
	print "  <TD WIDTH=\"200\">" . $tmpl->PassSub($admin->{'TagRealmHeadName'}) . "</TD>\n";

	# Non aliased
	if ($param->{'TYPE'} ne 'alias') 
	{
		print "  <TD WIDTH=\"75\">" . $tmpl->PassSub($admin->{'TagRealmHeadAccess'}) . "</TD>\n";
		print "  <TD WIDTH=\"75\">" . $tmpl->PassSub($admin->{'TagRealmHeadVerified'}) . "</TD>\n";
		print "  <TD WIDTH=\"75\">" . $tmpl->PassSub($admin->{'TagRealmHeadLang'}) . "</TD>\n";
		print "  <TD WIDTH=\"75\">" . $tmpl->PassSub($admin->{'TagRealmHeadTags'}) . "</TD>\n";
		print "  <TD WIDTH=\"75\">" . $tmpl->PassSub($admin->{'TagRealmHeadCreate'}) . "</TD>\n";
	}

	# Aliased
	else
	{
		print "  <TD WIDTH=\"200\">" . $tmpl->PassSub($admin->{'TagRealmHeadAlias'}) . "</TD>\n";
	}

	# Close row
	print "  <TD>" . $tmpl->PassSub('&nbsp;') . "</TD>\n</TR>\n";


	# Fetch rows of data
	# Handle specific realms
	while ($values = $statement->fetchrow_hashref())
	{
		# Realm name
		$realm = $values->{'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,
						 $values->{'RealmName'}
						);

						# Audit entry
						$audit->RealmStatus($status, $values->{'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",

					 $values->{'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, $values->{'RealmName'});
						} 		
					}

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

					 $values->{'RealmName'}
					);

					# Record removal
					$audit->RealmRem($values->{'RealmName'}, $values->{'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++;
	

			# Realm name
			print " <TR CLASS=\"$bgcolor\">";
			print "  <TD><B>$values->{RealmName}</B></TD>\n";

			# Non aliased
			if ($param->{'TYPE'} ne 'alias') 
			{
				print "  <TD ALIGN=\"CENTER\">$values->{RealmAccess}</TD>\n";
				print "  <TD ALIGN=\"CENTER\">$values->{RealmVerified}</TD>\n";
				print "  <TD ALIGN=\"CENTER\">$values->{RealmLanguage}</TD>\n";
				print "  <TD ALIGN=\"CENTER\">$values->{RealmTags}</TD>\n";
				print "  <TD>$values->{RealmTimestamp}</TD>\n";
			}

			# Aliased
			else
			{
				print "  <TD>$values->{'RealmAlias'}</TD>\n";
			}


			# Realm handling
			print  "  <TD ALIGN=\"CENTER\">", $cgi->checkbox(-name=>$realm, -label=>''), "</TD>\n";

			# Close row
			print "  </TR>\n";
		}
	}

	# End query
	$statement->finish();

	# Close table
	print "</TABLE></CENTER>\n";


	# Final form elements
	if ($param->{'TYPE'} ne 'alias')
	{
		# Allow up upgrades and downgrades
		print "<BR><CENTER>",
		 $cgi->submit($admin->{'TxtDelete'}),  '&nbsp;&nbsp;&nbsp;',
		 $cgi->submit($admin->{'TxtPromote'}), '&nbsp;',
		 $cgi->submit($admin->{'TxtDemote'}),  '&nbsp;',
		 "</CENTER>\n";
	}
	else
	{
		# Only allow for removal
		print "<BR><CENTER>",
		 $cgi->submit($admin->{'TxtDelete'}),
		 "</CENTER>\n";
	}


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


#####################
# Realm Select
#
# Basic display and prompt user for a selection on which realm to adminster and
# make modifications of some type to.

sub RealmSelect
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI handle
	my $param    = shift;					# Parameter hash
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash

	my @realms;						# Array of realms
	my $realms;						# Popup menu

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

	# Prepare of drop down
	$database->GetListNonPrivate(\@realms);
	$realms = $cgi->popup_menu('ROOM', \@realms);


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

	# Start table
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagRealmSelect'}));

	# Realm selection
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRealmName'}),
		$realms
	 );
	$table->MakeBlank();

	# Submit and bottom
	$table->MakeSingle($cgi->submit($admin->{'TxtDetails'}));
	$table->MakeBottom();

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


#####################
# Rotate Details
#
# Gets to the heart of the rotating items and allows one to modify the
# templates used.

sub RotateDetails
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative entry
	my $url      = shift;					# Self referencing links

	my $audit;						# Auditing handle
	my $data;						# Pulled text
	my $inline;						# Inline parameters


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

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

	# Create inline
	$inline = $param->EmbedInline(
		USER   => $param->{'USER'},
		CRYPT  => $param->{'CRYPT'},
		RZONE  => $param->{'RZONE'}
	 );	


	# Information management
	if (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->{'RENTRY'},
			 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');
	}


	# Pull text
	($data) = $database->DataGetRotate($param->{'RENTRY'}, lc($param->{'RZONE'}));


	# Display
	# Return option
	print "<CENTER>" 
	    . $tmpl->Pass('TagList', MLINK=>"$url$inline",  MNAME=>$admin->{'TxtBack'})
	    . "</CENTER>\n<BR>\n";

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

	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagRotateTemplate'} . $param->{'RENTRY'}));

	# Textbox
	$table->MakeSingle($cgi->textarea(
		-name    => 'RTEXT',
		-rows    => 15,
		-columns => 85,
		-wrap    => 'OFF',
		-default => $data)
	 );
	$table->MakeBlank();
	
	# Submit and reset
	$table->MakeValid($cgi->submit($admin->{'TxtAlter'}), $cgi->reset($admin->{'TxtReset'}));
	$table->MakeBottom();

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


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

sub RotateSummary
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative entry
	my $url      = shift;					# Self referencing links

	my $audit;						# Auditing handle

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

	my $buttons;						# Buttons widget
	my $conduct;						# Conduct widget
	my $listing;						# Listing widget
	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	


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

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


	# Ensure defintiion
	$param->{'RZONE'} = (defined($param->{'RZONE'})) ? $param->{'RZONE'} : 'Portal';


	# Cycle through sections
	# Pre-display
	print "<CENTER>";

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

		# Display entry
		$tmpl->Show('TagList', MLINK=>"$url$inline",  MNAME=>$zone);
	}

	# Banner reset capability
	if ($param->{'RZONE'} eq 'Banner')
	{
		# Spacer
		print "&nbsp;&nbsp";
		
		# Create inline
		$inline = $param->EmbedInline(
			USER   => $param->{'USER'},
			CRYPT  => $param->{'CRYPT'},
			RZONE  => $param->{'RZONE'},
			RESET  => 'True');	

		# Display entry
		$tmpl->Show('TagList', MLINK=>"$url$inline",  MNAME=>$admin->{'TxtClear'});
	}	

	# Post disply
	print "</CENTER>\n<BR>\n";


	# Quick exit
	return 1 if (defined($param->{'RENTRY'}));

	# Set variables if needed
	if ((defined($param->{$admin->{'TxtDetails'}}))
	 && ($param->{'RNAME'} =~ /[A-Z]+\s\-\s(.+)/))
 	{
		# Assign name
		$name = $1;

		# Set variable and return
		$param->{'RENTRY'} = $name;


		# Cleanup
		$param->Cleanup('NCONDUCT', 'NNAME', 'RNAME');

		# Return and leave
		return 1;
	}


	# Information management
	# Addition
	if ((defined($param->{$admin->{'TxtCreate'}}))
	 && (defined($param->{'NCONDUCT'}))
	 && (defined($param->{'NNAME'}))
	 && (length($param->{'NNAME'}) > 3))
 	{
		# Evaluate before comitting
		eval
		{
			# Insert entry
			$database->Write("INSERT INTO Rotate
				(RotateName,
				 RotateConduct,
				 RotateZone)
				VALUES(?,?,?)",

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

			# Audit entry
			$audit->RotateAdd(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('NCONDUCT', 'NNAME');
	}

	# 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'})))
 	{
		# Ensure proper format
		if ($param->{'RNAME'} =~ /[A-Z]+\s\-\s(.+)/)
		{
			# Assign name
			$name = $1;
			
			# Evaluate before comitting
			eval
			{
				# Remove entry
				$database->Write("DELETE FROM Rotate
					WHERE RotateName=?
					  AND RotateZone=?",

				 $name,
				 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');
	}

	# Generate widgets
	# Pull values
	$database->GetList(\@items, "SELECT 
		  UPPER(RotateConduct) || ' - ' || RotateName
		FROM Rotate
		WHERE RotateZone=?
		ORDER BY RotateName", lc($param->{'RZONE'}));

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

	# Generate
	$buttons = $cgi->submit($admin->{'TxtDelete'}) . '&nbsp;'
        	. $cgi->checkbox(-name=>'CONFIRM', -label=>'') . "<BR><BR>"
		. $cgi->submit($admin->{'TxtDetails'});
	$conduct = $cgi->popup_menu('NCONDUCT', \@conducts, $conducts[0]);
	$listing = $cgi->scrolling_list('RNAME', \@items, $items[0], 5, 'true');


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

	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagRotateList'} . $param->{'RZONE'}));
	$table->MakeValid($listing, $buttons, 'middle');


	# Spacer
	$table->MakeBlank();
	$table->MakeBlank();


	# Addition
	# Title
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagWebAdd'}));

	# Creation widgets
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRotateName'}),
		$cgi->textfield('NNAME', '', 15, 25)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagRotateConduct'}),
		$conduct
	 );
	$table->MakeBlank();

	# Submit
	$table->MakeSingle($cgi->submit($admin->{'TxtCreate'}));
	$table->MakeBottom();


	# 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);


	#####################
	# 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.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"; }


		# 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)";
	}
	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)";
	}


	# 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;						# 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
	my %system;						# System variables


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

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

my $HEADER = '

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


	# Link with hash
	$database->GetHashSystem(\%system);

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


	# 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 $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative hash
	my $url      = shift;					# Self referencing links

	my $audit;						# Audit handle
	my $page;						# Page handle
	my $statement;						# Statement handle

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

	my $alias;						# Singular alias
	my $aliases;						# List of aliases

	my $hostname;						# Individual hostname
	my $hostnames= '';					# List of hostnames
	my $puppets  = '';					# List of puppets

	my $home;						# Homepage directory
	my $login;						# Login name
	my $salt;						# Password salt
	my $values;						# Hash reference

	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 @puppets;						# List of puppets

	my %contrib;						# Homepage handler


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

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

	# 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'
		 );
	}

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


	# 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');

	# Display
	print "<CENTER>" 
	 . $tmpl->Pass('TagList', MLINK=>"$url$iback", MNAME=>$admin->{'TxtBack'}) . '&nbsp;'
	 . $tmpl->Pass('TagList', MLINK=>"$url$inline&REFERENCE=True", MNAME=>$admin->{'TagUserCross'}) . '&nbsp;'
	 . $tmpl->Pass('TagList', MLINK=>"$url$inline&POS=True", MNAME=>$admin->{'TagUserPos'})
	 . $tmpl->Pass('TagList', MLINK=>"$url$inline&NED=True", MNAME=>$admin->{'TagUserNeg'})
	 . "</CENTER><BR>\n";	

	# Data display
	# Prepare and execute
	$database->Pull(\$statement, "SELECT
		 PuppeteerLogin      AS \"PuppeteerLogin\",
		 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 and finish
	$values = $statement->fetchrow_hashref();


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


	# Recover Aliases
	if ($values->{'PuppeteerBan'} eq 'yes')
	{
		# Get list
		$database->GetList(\@aliases, "SELECT PuppeteerLogin
			FROM Puppeteer
			WHERE PuppeteerAlias=?
			ORDER BY PuppeteerLogin", $login);

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

		# Join into singular line
		$aliases = join("\n", @aliases);


		# Recover Puppets
		foreach $alias (@aliases)
		{
			# Recover puppets
			$database->GetList(\@puppets, "SELECT PuppetName 
				FROM Puppet 
				WHERE PuppeteerLogin=?
				ORDER BY PuppetName",
			 $alias
			);

			# Title
			$puppets = "$puppets$alias:\n-----------\n";

			# Construct alias 
			while ($puppet = shift(@puppets))
			{
				# Add all hostnames
				$puppets = "$puppets\t$puppet\n";			
			}

			# Spacer 
			$puppets = "$puppets\n\n";			


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

			 $alias
			);

			# Title
			$hostnames = "$hostnames$alias:\n-----------\n";

			# Construct alias 
			while ($hostname = shift(@hostnames))
			{
				# Add all hostnames
				$hostnames = "$hostnames\t$hostname\n";			
			}

			# Spacer 
			$hostnames = "$hostnames\n\n";			
		}	
	}

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

		 $login
		);

		# Recover puppets
		$database->GetList(\@puppets, "SELECT PuppetName 
			FROM Puppet 
			WHERE PuppeteerLogin=?
			ORDER BY PuppetName",
		 $login
		);
	
		# Join into singular line
		$hostnames = join("\n", @hostnames);
		$puppets   = join("\n", @puppets);
	}


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


	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagUserDetails'} . $login));

	# Registration flag
	if (defined($reg))
	{
		# Invalid registraion notice
		$table->MakeSingle($admin->{'TagUserReg'});
		$table->MakeBlank();
	}


	# Email and Password
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserEmail'}),
		$cgi->textfield('EMAIL', $values->{'PuppeteerEMail'}, 25, 45)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserPass'}),
		$cgi->textfield('PASSWORD', $values->{'PuppeteerPassword'}, 25, 13)
	 );
	$table->MakeBlank();


	# Name, Alias
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserName'}),
		$cgi->textfield('FULLNAME', $values->{'PuppeteerName'}, 25, 30)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserAlias'}),
		$cgi->textfield('ALIAS', $values->{'PuppeteerAlias'}, 25, 10)
	 );
	$table->MakeBlank();


	# Timestamp, Hostname (cannot be changed)
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserLast'}),
		$values->{'PuppeteerTimestamp'}
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserHost'}),
		$values->{'PuppeteerHostname'}
	 );
	$table->MakeBlank();


	# Homepage Information
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserHome'}),
		$values->{'PuppeteerHome'}
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserExpires'}),
		$values->{'PuppeteerHomeSpan'}
	 );
	$table->MakeBlank();


	# Formatting, Chat Prefs and Javascript (cannot be changed)
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserChat'}),
		$values->{'PuppeteerChatPref'}
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserFormat'}),
		$values->{'PuppeteerFormatting'}
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserJavascript'}),
		$values->{'PuppeteerJavascript'}
	 );
	$table->MakeBlank();


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

	# Additional granted controls
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserAdmin'}),
		$radioadmin
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserSuper'}),
		$radiosuper
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserMod'}),
		$radiomod
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserVer'}),
		$radiover
	 );
	$table->MakeBlank();


	# User banning
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserBan'}),
		$radioban
	 );
	$table->MakeValid('&nbsp;', $cgi->checkbox(
		-name  => 'BANCONF',
		-label => $admin->{'TxtConf'})
	 );
	$table->MakeBlank();


	# Fields
	# List of aliases
	if ($values->{'PuppeteerBan'} eq 'yes')
	{
		$table->MakeSingle($tmpl->PassTitle($admin->{'TagUserAliases'}));
		$table->MakeSingle($cgi->textarea('ALIASES', $aliases, 10, 40));
		$table->MakeBlank();
	}

	# List of Puppets
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagUserPuppets'}));
	$table->MakeSingle($cgi->textarea('PUPPETS', $puppets, 10, 40));
	$table->MakeBlank();

	# IP History
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagUserHosts'}));
	$table->MakeSingle($cgi->textarea('HOSTNAMES', $hostnames, 10, 40));
	$table->MakeBlank();

	# Submit and Reset
	$table->MakeValid($cgi->submit($admin->{'TxtAlter'}), $cgi->reset($admin->{'Reset'}));
	$table->MakeBottom();

	# 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 $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handle
	my $admin    = shift;					# Administrative 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 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";


	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagUserSearch'}));

	# Search Range
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserRange'}),
		$radiorange
	 );
	$table->MakeBlank();

	# Field and keyword
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserField'}),
		$radiofield
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagUserKeyword'}),
		$cgi->textfield('KEYWORD', '', 25, 25)
	 );	
	$table->MakeBlank();

	# Submit and bottom
	$table->MakeValid('&nbsp;', $cgi->submit($admin->{'TxtSearch'})); 
	$table->MakeBottom();

	# 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 $admin    = shift;					# Administrative handle
	my $url	     = shift;					# Self referencing link

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

	my $audit;						# Auditing table
	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 @privs;						# Privilege list
	my @galleries;						# Galleries

	my %contrib;						# Home page hash
	my %system;						# System 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());

	# Center line
	print "<DIV ALIGN=\"CENTER\">";

	# Mandatory back link
	$tmpl->Show('TagList',
		 MLINK => $url . $iback,
		 MNAME => $admin->{'TxtBack'}
	  );

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

		# Increment
		$count++;

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

	} while ($page > 0);

	# Close tags page support
	print "</DIV>\n<BR>\n";


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


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


	# Table display
	print "<DIV ALIGN=\"CENTER\"><TABLE WIDTH=\"95%\">";

	# Display header
	print " <TR>\n";
	print "  <TD>" . $tmpl->PassSub($admin->{'TagUserLogin'}) . "</TD>\n";
	print "  <TD>" . $tmpl->PassSub($admin->{'TagUserName'})  . "</TD>\n";
	print "  <TD>" . $tmpl->PassSub($admin->{'TagUserEmail'}) . "</TD>\n";
	print "  <TD>" . $tmpl->PassSub($admin->{'TagUserPrivs'}) . "</TD>\n";
	print "  <TD>" . $tmpl->PassSub('&nbsp;') . "</TD>\n";
	print " </TR>\n";


	# Reset counter
	$count = 0;

	# 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);
				$database->GetHashSystem(\%system);

				# Create instances
				$page  = new Ethereal::Home($database, $contrib{'SetHomeRoot'});
				$audit = new Ethereal::Audit($database, $cgi, $param);


				# 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("$system{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, 'S') if ($res->{'PuppeteerSuperPrivs'} eq 'yes');
			push(@privs, 'M') if ($res->{'PuppeteerModPrivs'} 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'});

			# Display actual row
			print " <TR CLASS=\"$bgcolor\">\n";
			print "  <TD><B><A HREF=\"$url$inline\">$res->{PuppeteerLogin}</A></B></TD>\n";
			print "  <TD>$res->{PuppeteerName}</TD>\n";
			print "  <TD><A HREF=\"mailto:$res->{PuppeteerEMail}\">$res->{PuppeteerEMail}</A></TD>\n";
			print "  <TD>$privs</TD>\n";
			print "  <TD ALIGN=\"CENTER\">" . $cgi->checkbox(-name=>$login, -label=>'') . "</TD>\n";
			print " </TR>\n";
		}
	}

	# Finish statement
	$statement->finish();

	# Remove row
	print " <TR>\n  <TD COLSPAN=\"5\" HEIGHT=\"10\">&nbsp;</TD>\n </TR>\n";
	print " <TR>\n  <TD COLSPAN=\"5\" ALIGN=\"RIGHT\">" . $cgi->submit($admin->{'TxtDelete'}) . "</TD>\n </TR>\n";


	# End of table
	print "</TABLE></DIV>\n";


	# 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 $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative entry
	my $clear    = shift;					# Clear handle

	my $audit;						# Auditing 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
	my %system;						# Board hash


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

	# Define instances
	$audit  = new Ethereal::Audit($database, $cgi, $param);
	$filter = new Ethereal::Filter();

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


	# 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{'SetBoardAlias'},
 					 $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       => $system{'SetInfoServer'},
					link        => $system{'SetUrl'},
					description => $system{'SetDescription'},
					dc => {
						creator    => $system{'SetInfoContactAddress'},
						publisher  => $system{'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        => $system{'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($system{'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{'SetBoardAlias'});
	$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
	# Title
	$tmpl->ShowMain($admin->{'TagWebTitle'});

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

	# Table top
	$table->MakeTop();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagWebList'}));
	$table->MakeValid($weblog, $button, 'middle');


	# Spacer
	$table->MakeBlank();
	$table->MakeBlank();


	# Addition
	# Title
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagWebAdd'}));

	# Creation widgets
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagWebTopic'}),
		$cgi->textfield('TOPC', '', 15, 25)
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagWebSubject'}),
		$cgi->textfield('SUBJ', '', 25, 100)
	 );
	$table->MakeBlank();


	# Clone
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagWebClone'}),
		$topic
	 );
	$table->MakeValid(
		$tmpl->PassSub($admin->{'TagWebFilter'}),
		$cgi->checkbox(-name=>'DISABLE', -label=>'')
	 );
	$table->MakeBlank();


	# Text area
	$table->MakeBlank();
	$table->MakeSingle($tmpl->PassTitle($admin->{'TagWebPost'}));
	$table->MakeSingle($cgi->textarea(
		-name    => 'POST',
		-rows    => 10,
		-columns => 60,
		-wrap    => 'SOFT')
	 );

	# Submit
	$table->MakeBlank();
	$table->MakeSingle($cgi->submit($admin->{'TxtSave'}));
	$table->MakeBottom();


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

#####################
# Word handler
#
# Controls word restrictions, as a result one can add or remove words for which
# such words will be replaced from posts by ****.   Due to the abuse of hosts
# a administrative level version of this had to be created.

sub WordHandler
{
	#####################
	# Data members
	my $database = shift;					# Database handle
	my $cgi      = shift;					# CGI filehandle
	my $param    = shift;					# Hash of parameters					
	my $table    = shift;					# Table handler
	my $tmpl     = shift;					# Template handler
	my $admin    = shift;					# Administrative entry
	
	my $audit;						# Auditing handle
	my $system   = 'system';				# Administrative realm
	my $words;						# List of words

	my @words;						# List of words


	#####################
	# program area

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


	# Information management
	# Evaluate following
	eval
	{
		# Removal
		if ((defined($param->{$admin->{'TxtDelete'}}))
 		 && ($param->{'WORDREM'} ne $admin->{'TxtNone'}))
		{
			# Remove entry
			$database->Write("DELETE FROM Restriction
				WHERE RealmName=?
  				AND RestrictionType='word'
  				AND RestrictionValue=?",

 			 $system,
 			 $param->{'WORDREM'}
			);

			# Audit entry
			$audit->AdmWordRem($param->{'WORDREM'});
		}

		# Addition
		elsif (defined($param->{$admin->{'TxtCreate'}}))
		{
			# Only if a word
			if (($param->{'WORDADD'} !~ /^\W+/sg)
 			&& (length($param->{'WORDADD'}) > 2))
			{
				# Create new entry
				$database->Write("INSERT INTO Restriction
					(RealmName,
 					RestrictionType,
 					RestrictionValue)
					VALUES (?,'word',?)",

 				 $system,
 				 $param->{'WORDADD'}
				);

				# Audit entry
				$audit->AdmWordAdd($param->{'WORDADD'});
			}
		}
	};

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

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


	# Retreive list of banned and suspects
	$database->GetListWords(\@words, $system);
	unshift(@words, $admin->{'TxtNone'});

	# Create widget
	$words = $cgi->scrolling_list('WORDREM',  \@words, $words[0], 10);


	# HTML
	# Title
	$tmpl->ShowMain($admin->{'TagWordTitle'});

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

	# Table Top
	$table->MakeTop();

	# Remove
	$table->MakeSingle($tmpl->PassSub($admin->{'TagWordRem'}));
	$table->MakeValid($words, $cgi->submit($admin->{'TxtDelete'}), 'middle');
	$table->MakeBlank();

	# Addition
	$table->MakeSingle($tmpl->PassSub($admin->{'TagWordAdd'}));
	$table->MakeValid($cgi->textfield('WORDADD', '', 10, 10), $cgi->submit($admin->{'TxtCreate'}));
	$table->MakeBottom();

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