package HaCi::HaCi;

use strict;
use CGI;
# use CGI::Pretty;
use CGI::Carp qw/fatalsToBrowser/;
use CGI::Session;
use CGI::Cookie;
use CGI::Ajax;
use Math::BigInt 1.87;
use Storable qw(lock_store lock_retrieve);
use Digest::MD5;
use Math::Base85;
use Encode;

use HaCi::GUI::init;
use HaCi::GUI::main qw/
	checkNet expandNetwork reduceNetwork reduceRoot expandRoot showPlugin mkShowStatus
	mkSubmitImportASNRoutes
/;
use HaCi::GUI::authentication;
use HaCi::GUI::gettext qw/_gettext/;
use HaCi::Log qw/warnl debug/;
use HaCi::Mathematics qw/
	net2dec dec2net getBroadcastFromNet getNetmaskFromCidr dec2ip ip2dec getNetaddress netv62Dec
	netv6Dec2net
/;
use HaCi::Utils qw/
	prWarnl delUser getRights importCSV compare checkDB newWindow prNewWindows setStatus removeStatus netv6Dec2ipv6ID
	addRoot addNet importASNRoutes getConfig checkSpelling_Net rootID2Name rootName2ID delNet genRandBranch editRoot
	delRoot copyNetsTo delNets search checkSpelling_IP checkSpelling_CIDR saveTmpl delTmpl saveGroup delGroup saveUser
	lwd dec2bin groupID2Name checkRight importDNSTrans importDNSLocal getNetworkParentFromDB importConfig rootID2ipv6
	expand splitNet combineNets updatePluginDB getTable checkTables checkNetworkTable checkNetworkACTable checkNetACL getRoots
	mkPluginConfig initTables initCache chOwnPW updSettings updateSettingsInSession flushACLCache flushNetCache exportSubnets
	searchAndGetFreeSubnets getParam changeTmplName checkNetworkLock getFreeSubnets getNetID getDBNetworkBefore
/;

local our $q				= undef;
local our $pjx			= undef;
local our $session	= undef;
local our $aclCache	= undef;
local our $netCache	= undef;
local our $cache		= undef;
our $conf; *conf		= \$HaCi::Conf::conf;
my $netCacheHandle	= undef;
my $aclCacheHandle	= undef;

sub run {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};

	my $soap				= shift || 0;
	my $soapArgs		= shift || [];
	my $soapPreRun	= shift;
	my $soapPostRun	= shift;

	$conf->{var}->{soap}	= $soap;

	&getConfig();
	&init();
	&soapInit($soapArgs) if $soap;

	if (1 && $conf->{static}->{misc}->{debug}) { 
		use Data::Dumper qw/Dumper/; # Loading Dumper for Debugging...
		foreach ($q->param) {
			my $k	= $_;
			next if $k eq 'password';
			foreach ($q->param($k)) {
				warn "  $k => $_ \n"; 
			}
		}
	} # Dump

	if (&getParam(1, 0, 'func')) {
		if (&getParam(1, '', 'func') eq 'logout') {
			if (defined $session && $session->param('authenticated')) {
				&debug("logging out");
				&removeStatus();
				$session->delete();
				$session->flush();
				$conf->{var}->{relogin}	= 1;
				# &genNewSession();
			} else {
				&debug("allready logged out!");
			}
		}
	}
	
	&HaCi::GUI::init::init();
	&HaCi::GUI::init::setVars();

	my $t	= $HaCi::GUI::init::t;

	if (
		!$conf->{var}->{relogin} && 
		&authentication(
			&getParam(1, undef, 'username', 0, 1), 
			&getParam(1, undef, 'password'), 
			&getParam(1, undef, 'login'), 
			$session->param('username')
		)
	) {
		&getRights();
		&$soapPreRun($soapArgs, $q) if $soap && defined $soapPreRun;
		&checkRights();
		&main();
	} else {
		&checkTables();
		&checkNetworkTable();
		&checkNetworkACTable();
		&HaCi::GUI::authentication::login();
		$t->{V}->{page}  = 'login';
	}

	my $warnings	= '';
	my $localQ		= $q;
	$warnings			= &finalize();
	return &$soapPostRun($soapArgs, $localQ, $t, $warnings) if $soap && defined $soapPostRun;
}

sub main {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};

	&HaCi::GUI::init::setUserVars();

	if (&getParam(1, 0, 'fname')) {	# AJAX call
		return;
	}
	
	my $t	= $HaCi::GUI::init::t;
	my $q	= $HaCi::HaCi::q;

	if (&getParam(1, 0, 'submitAddRoot')) {
		if (&addRoot(
			&getParam(1, undef, 'name'),
			&getParam(1, undef, 'descr'),
			&getParam(1, 0, 'ipv6'),
			&getParam(1, undef, 'rootID'),
		)) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param(
				'func', 
				((&getParam(1, 0, 'editRoot')) ? 'editRoot' : 'addRoot')
			);
		}
	}
	elsif (&getParam(1, 0, 'submitAddNet')) {
		if (&addNet(
			&getParam(1, 0, 'netID'),
			&getParam(1, undef, 'rootID'),
			&getParam(1, undef, 'netaddress'),
			&getParam(1, undef, 'cidr'),
			&getParam(1, '', 'descr'),
			&getParam(1, 0, 'state'),
			&getParam(1, 0, 'tmplID'),
			&getParam(1, 0, 'defSubnetSize'),
			&getParam(1, 0, 'forceState'),
			0,
			0,
			&getParam(1, '', 'tags'),
		)) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'addNet');
		}
	}
	elsif (&getParam(1, 0, 'abortAddRoot')) {
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'abortAddNet')) {
		if (&getParam(1, 0, 'editNet')) {
			$q->delete('func'); $q->param('func', 'showNet');
		} else {
			$q->delete('func'); $q->param('func', 'showAllNets');
		}
	}
	elsif (&getParam(1, 0, 'checktAddNet')) {
		&checkNet(
			&getParam(1, undef, 'netaddress'), 
			&getParam(1, undef, 'cidr'),
		);
		if (&getParam(1, 0, 'editNet')) {
			$q->delete('func'); $q->param('func', 'editNet');
		} else {
			$q->delete('func'); $q->param('func', 'addNet');
		}
	}
	elsif (&getParam(1, 0, 'submitImportASNRoutes')) {
		if (
			&importASNRoutes(
				&getParam(1, undef, 'asn')
			)
		) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'importASNRoutes');
		}
	}
	elsif (&getParam(1, 0, 'submitImpDNSTrans')) {
		if (&importDNSTrans()) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'importDNS');
		}
	}
	elsif (&getParam(1, 0, 'submitImpDNSLocal')) {
		if (&importDNSLocal()) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'importDNS');
		}
	}
	elsif (&getParam(1, 0, 'submitImpConfig')) {
		if (&importConfig()) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'importConfig');
		}
	}
	elsif (&getParam(1, 0, 'impCSVChangeSep')) {
		$q->delete('func'); $q->param('func', 'importConfig');
	}
	elsif (&getParam(1, 0, 'impCSVChangeType')) {
		$q->delete('func'); $q->param('func', 'importConfig');
	}
	elsif (&getParam(1, 0, 'abortImpCSV')) {
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'submitImpCSV')) {
		if (&importCSV()) {
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'importConfig');
		}
	}
	elsif (&getParam(1, 0, 'editPluginConf')) {
		$q->delete('func'); $q->param('func', 'showPluginConf');
	}
	elsif (&getParam(1, 0, 'editNet')) {
		$q->delete('func'); $q->param('func', 'editNet');
	}
	elsif (&getParam(1, 0, 'splitNet')) {
		$q->delete('func'); $q->param('func', 'splitNet');
	}
	elsif (&getParam(1, 0, 'submitSplitNet')) {
		&splitNet(
			&getParam(1, undef, 'netID'),
			&getParam(1, undef, 'splitCidr'),
			&getParam(1, '', 'descrTemplate'),
			&getParam(1, 0, 'state'),
			&getParam(1, 0, 'tmplID'),
			&getParam(1, 0, 'delParentNet'),
		);
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'submitCombineNets')) {
		&combineNets();
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'abortCombineNets')) {
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'abortSplitNet')) {
		$q->delete('func'); $q->param('func', 'showNet');
	}
	elsif (&getParam(1, 0, 'combineNets')) {
		my $nets	= &getParam(0, [], 'selectedNetworks');
		if ($#{$nets} > 0) {
			$q->delete('func'); $q->param('func', 'combineNets');
		}
	}
	elsif (&getParam(1, 0, 'abortEditRoot')) {
		$q->delete('func'); $q->param('func', 'showRoot');
	}
	elsif (&getParam(1, 0, 'editRoot')) {
		$q->delete('func'); $q->param('func', 'editRoot');
	}
	elsif (&getParam(1, 0, 'abortDelRoot')) {
		$q->delete('func'); $q->param('func', 'showRoot');
	}
	elsif (&getParam(1, 0, 'abortDelNet')) {
		$q->delete('func'); $q->param('func', 'showNet');
	}
	elsif (&getParam(1, 0, 'delRoot')) {
		if (&getParam(1, 0, 'commitDelRoot')) {
			&delRoot(
				&getParam(1, undef, 'rootID')
			);
			$q->delete('func'); $q->param('func', 'showAllNets');
		} else {
			$q->delete('func'); $q->param('func', 'delRoot');
		}
	}
	elsif (&getParam(1, 0, 'delNet')) {
		if (&getParam(1, 0, 'commitDelNet')) {
			my $networkLock	= &getParam(1, 0, 'networkLock');
			if (defined $networkLock && int($networkLock) ne $networkLock) {
				&warnl(sprintf(_gettext("Network lock time must be provided in seconds not '%s'"), $networkLock));
				$q->delete('func'); $q->param('func', 'delNet');
			} else {
				&delNet(
					&getParam(1, undef, 'netID'), 
					((&getParam(1, 0, 'withSubnets')) ? 1 : 0),
					0,
					&getParam(1, 0, 'networkLock')
				);
				$q->delete('func'); $q->param('func', 'showAllNets');
			}
		} else {
			$q->delete('func'); $q->param('func', 'delNet');
		}
	}
	elsif (&getParam(1, 0, 'reduceRoot')) {
		&expand(
			'-', 
			'root', 
			&getParam(1, undef, 'reduceRoot')
		);
		$session->param('currNet', '');
		$session->param(
			'currRootID', 
			&getParam(1, undef, 'reduceRoot')
		);
	}
	elsif (&getParam(1, 0, 'expandRoot')) {
		&expand(
			'+', 
			'root', 
			&getParam(1, undef, 'expandRoot')
		);
		$session->param('currNet', '');
		$session->param(
			'currRootID', 
			&getParam(1, undef, 'expandRoot')
		);
	}
	elsif (&getParam(1, 0, 'reduceNetwork')) {
		&expand(
			'-', 
			'network', 
			&getParam(1, undef, 'reduceNetwork'), 
			&getParam(1, undef, 'rootID')
		);
		$session->param(
			'currNet', 
			&getParam(1, undef, 'reduceNetwork')
		);
		$session->param(
			'currRootID', 
			&getParam(1, undef, 'rootID')
		);
	}
	elsif (&getParam(1, 0, 'expandNetwork')) {
		&expand(
			'+', 
			'network', 
			&getParam(1, undef, 'expandNetwork'), 
			&getParam(1, undef, 'rootID')
		);
		$session->param(
			'currNet', 
			&getParam(1, undef, 'expandNetwork')
		);
		$session->param(
			'currRootID', 
			&getParam(1, undef, 'rootID')
		);
	}
	elsif (&getParam(1, 0, 'closeTree')) {
		&expand('-', 'ALL', 'ALL');
	}
	elsif (&getParam(1, 0, 'jumpToButton')) {
		&expandTo(
			&getParam(1, undef, 'rootIDJump'), 
			&getParam(1, undef, 'jumpTo')
		);
	}
	elsif (&getParam(1, 0, 'genRandBranch')) {
		&genRandBranch();
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'finishEditTree')) {
		$q->delete('editTree');
	}
	elsif (&getParam(1, 0, 'copyNetsTo')) {
		&copyNetsTo(
			&getParam(1, undef, 'copyToRootID'), 
			&getParam(0, [], 'selectedNetworks'), 
			0
		);
	}
	elsif (&getParam(1, 0, 'moveNetsTo')) {
		&copyNetsTo(
			&getParam(1, undef, 'copyToRootID'), 
			&getParam(0, [], 'selectedNetworks'), 
			1
		);
	}
	elsif (&getParam(1, 0, 'deleteNets')) {
		&delNets(&getParam(0, [], 'selectedNetworks'));
	}
	elsif (&getParam(1, 0, 'searchButton')) {
		if ((&getParam(1, '', 'func') eq 'getFreeSubnetsFromSearch')) {
			&searchAndGetFreeSubnets();
			$t->{V}->{searchResult}	= $t->{V}->{freeSubnets};
		} else {
			&search();
		}
	}
	elsif (&getParam(1, 0, 'compareButton')) {
		&compare();
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'newTmpl')) {
		my $tmplName	= &getParam(1, '', 'tmplName');

		my $exists		= 0;
		if ($tmplName) {
			$exists		= &checkIfExistsInDB('template', 'name', $tmplName);
			&warnl("Template '$tmplName' already exists!") if $exists;
		}	

		if ($tmplName && !$exists) {
			$q->delete('tmplID');
			my $tmplName	= &getParam(1, '', 'tmplName');
			$tmplName			=~ s/[<>&"']//g;
			$q->delete('tmplName'), $q->param('tmplName', $tmplName);
			$q->delete('func'); $q->param('func', 'editTmpl');
		} else {
			$q->delete('tmplID');
			$q->delete('func'); $q->param('func', 'showTemplates');
		}
	}
	elsif (&getParam(1, 0, 'editNetTypeTmpl')) {
		unless (defined &getParam(1, undef, 'tmplID')) {
			$q->delete('func'); $q->param('func', 'showTemplates');
		} else {
			$q->delete('func'); $q->param('func', 'editTmpl');
		}
	}
	elsif (&getParam(1, 0, 'editTemplName')) {
		&changeTmplName(&getParam(1, undef, 'tmplID', 1), &getParam(1, 0, 'tmplName'));
		$q->delete('func'); $q->param('func', 'editTmpl');
	}
	elsif (&getParam(1, 0, 'abortEditTmpl')) {
		$q->delete('func'); $q->param('func', 'showTemplates');
	}
	elsif (&getParam(1, 0, 'submitAddTmplEntry')) {
		my $tmplID	= &saveTmpl();
		$q->delete('tmplID'); $q->param('tmplID', $tmplID);
		$q->delete('func'); $q->param('func', 'editTmpl');
	}
	elsif (&getParam(1, 0, 'submitEditTmplEntry')) {
		my $tmplID	= &saveTmpl(1);
		$q->delete('tmplID'); $q->param('tmplID', $tmplID);
		$q->delete('func'); $q->param('func', 'editTmpl');
	}
	elsif (&getParam(1, 0, 'submitDeleteTmplEntry')) {
		my $tmplID	= &saveTmpl(2);
		$q->delete('tmplID'); $q->param('tmplID', $tmplID);
		$q->delete('func'); $q->param('func', 'editTmpl');
	}
	elsif (&getParam(1, 0, 'delTmpl')) {
		if (&getParam(1, 0, 'commitDelTmpl')) {
			&delTmpl(&getParam(1, undef, 'tmplID'));
			$q->delete('func'); $q->param('func', 'showTemplates');
		}
		elsif (&getParam(1, 0, 'abortDelTmpl')) {
			$q->delete('func'); $q->param('func', 'showTemplates');
		} else {
			if (&getParam(1, 0, 'tmplID')) {
				$q->delete('func'); $q->param('func', 'delTmpl');
			} else {
				$q->delete('func'); $q->param('func', 'showTemplates');
			}
		}
	}
	elsif (&getParam(1, 0, 'newGroup')) {
		my $groupName	= &getParam(1, '', 'groupName');
		#$groupName		=~ s/[<>&"']//g;

		my $exists		= 0;
		if ($groupName) {
			$exists		= &checkIfExistsInDB('group', 'name', $groupName);
			&warnl("Group '$groupName' already exists!") if $exists;
		}	

		if ($groupName && !$exists) {
			$q->delete('groupID');
			$q->delete('groupName'), $q->param('groupName', $groupName);
			$q->delete('func'); $q->param('func', 'editGroup');
		} else {
			$q->delete('func'); $q->param('func', 'showGroups');
		}
	}
	elsif (&getParam(1, 0, 'editGroup')) {
		unless (defined &getParam(1, undef, 'groupID')) {
			$q->delete('func'); $q->param('func', 'showGroups');
		} else {
			$q->delete('func'); $q->param('func', 'editGroup');
		}
	}
	elsif (&getParam(1, 0, 'submitEditGroup')) {
		my $groupID	= &saveGroup();
		if (defined $groupID) {
			$q->delete('groupID'); $q->param('groupID', $groupID);
			$q->delete('func'); $q->param('func', 'showGroups');
		} else {
			$q->delete('func'); $q->param('func', 'editGroup');
		}
	}
	elsif (&getParam(1, 0, 'abortEditGroup')) {
		$q->delete('func'); $q->param('func', 'showGroups');
	}
	elsif (&getParam(1, 0, 'delGroup')) {
		if (&getParam(1, 0, 'commitDelGroup')) {
			&delGroup(&getParam(1, undef, 'groupID'));
			$q->delete('func'); $q->param('func', 'showGroups');
		}
		elsif (&getParam(1, 0, 'abortDelGroup')) {
			$q->delete('func'); $q->param('func', 'showGroups');
		} else {
			if (&getParam(1, 0, 'groupID')) {
				my $groupName	= &groupID2Name(&getParam(1, undef, 'groupID'));
				if ($groupName eq 'Administrator') {
					&warnl(_gettext("You cannot delete the administrator group!"));
					$q->delete('func'); $q->param('func', 'showGroups');
				} else {
					$q->delete('func'); $q->param('func', 'delGroup');
				}
			} else {
				$q->delete('func'); $q->param('func', 'showGroups');
			}
		}
	}
	elsif (&getParam(1, 0, 'newUser')) {
		my $userName	= &getParam(1, '', 'userName');
		#$userName			=~ s/[<>&"']//g;

		my $exists		= 0;
		if ($userName) {
			$exists		= &checkIfExistsInDB('user', 'username', $userName);
			&warnl("User '$userName' already exists!") if $exists;
		}	

		if ($userName && !$exists) {
			$q->delete('userID');
			$q->delete('userName'), $q->param('userName', $userName);
			$q->delete('func'); $q->param('func', 'editUser');
		} else {
			$q->delete('func'); $q->param('func', 'showUsers');
		}
	}
	elsif (&getParam(1, 0, 'editUser')) {
		unless (defined &getParam(1, undef, 'userID')) {
			$q->delete('func'); $q->param('func', 'showUsers');
		} else {
			$q->delete('func'); $q->param('func', 'editUser');
		}
	}
	elsif (&getParam(1, 0, 'submitEditUser')) {
		my $userID	= &saveUser();
		if (defined $userID) {
			$q->delete('userID'); $q->param('userID', $userID);
			$q->delete('func'); $q->param('func', 'showUsers');
		} else {
			$q->delete('func'); $q->param('func', 'editUser');
		}
	}
	elsif (&getParam(1, 0, 'abortEditUser')) {
		$q->delete('func'); $q->param('func', 'showUsers');
	}
	elsif (&getParam(1, 0, 'delUser')) {
		if (&getParam(1, 0, 'commitDelUser')) {
			&delUser(&getParam(1, undef, 'userID'));
			$q->delete('func'); $q->param('func', 'showUsers');
		}
		elsif (&getParam(1, 0, 'abortDelUser')) {
			$q->delete('func'); $q->param('func', 'showUsers');
		} else {
			if (&getParam(1, 0, 'userID')) {
				$q->delete('func'); $q->param('func', 'delUser');
			} else {
				$q->delete('func'); $q->param('func', 'showUsers');
			}
		}
	}
	elsif (&getParam(1, 0, 'showSubnets')) {
		$q->delete('func'); $q->param('func', 'showSubnets');
	}
	elsif (&getParam(1, 0, 'abortShowSubnets')) {
		$q->delete('func'); $q->param('func', 'showNet');
	}
	elsif (&getParam(1, 0, 'checkDB')) {
		&checkDB();
	}
	elsif (&getParam(1, 0, 'submitshowPlugins')) {
		&updatePluginDB();
		$q->delete('func'); $q->param('func', 'showPlugins');
	}
	elsif (&getParam(1, 0, 'editPluginGlobConf')) {
		$q->delete('func'); $q->param('func', 'showPluginGlobConf');
	}
	elsif (&getParam(1, 0, 'submitPluginConfig')) {
		my $global	= &getParam(1, 0, 'global');
		&mkPluginConfig($global);
		if ($global) {
			$q->delete('func'); $q->param('func', 'showPlugins');
		} else {
			$q->delete('func'); $q->param('editNet', 'Bearbeiten'); $q->param('func', 'editNet');
		}
	}
	elsif (&getParam(1, 0, 'abortPluginConfig')) {
		my $global	= &getParam(1, 0, 'global');
		if ($global) {
			$q->delete('func'); $q->param('func', 'showPlugins');
		} else {
			$q->delete('func'); $q->param('func', 'editNet');
			$q->delete('editNet'); $q->param('editNet', 1);
		}
	}
	elsif (&getParam(1, 0, 'abortShowSettings')) {
		$q->delete('func'); $q->param('func', 'showAllNets');
	}
	elsif (&getParam(1, 0, 'commitChOwnPW')) {
		unless (&chOwnPW()) {
			$q->param('changeOwnPW', 1);
		}
	}
	elsif (&getParam(1, 0, 'commitShowAuditLogs')) {
			$q->delete('func'); $q->param('func', 'showAuditLogs');
	}
	elsif (&getParam(1, 0, 'showAuditLogsLeft')) {
			$q->delete('func'); $q->param('func', 'showAuditLogs');
	}
	elsif (&getParam(1, 0, 'showAuditLogsRight')) {
			$q->delete('func'); $q->param('func', 'showAuditLogs');
	}
	elsif (&getParam(1, 0, 'commitViewSettings')) {
		unless (&updSettings()) {
			$q->param('showViewSettings', 1);
		}
		&HaCi::GUI::init::setUserVars();
	}
	elsif (&getParam(1, 0, 'searchAndGetFreeSubnets')) {
		&searchAndGetFreeSubnets();
	}
	elsif (&getParam(1, 0, 'getFreeSubnets')) {
		my $netID				= &getParam(1, undef, 'netID');
		my $subnetSize	= &getParam(1, undef, 'subnetSize');
		my $amount			= &getParam(1, undef, 'amount');
		&getFreeSubnets($netID, 0, $subnetSize, $amount);
	}
	elsif (&getParam(1, 0, 'exportSubnets')) {
		my $rootID	= &getParam(1, undef, 'rootID');
		my $netID		= &getParam(1, undef, 'netID');
		my $ID			= (defined $rootID) ? $rootID : $netID;
		&exportSubnets($ID, (defined $rootID) ? 1 : 0);
	}
	
	if (defined &getParam(1, 0, 'func')) {
		if (&getParam(1, '', 'func') eq 'addNet') {
			my $roots	= &getRoots(0);
			if ($#{$roots} == -1) {
				&warnl("Cannot find any Root! Please create some before you create networks\n");
				$q->delete('func'); $q->param('func', 'addRoot');
			}
		}
		if (&getParam(1, '', 'func') eq 'addNet' && &getParam(1, 0, 'networkDec') && &getParam(1, 0, 'rootID') && !(&getParam(1, 0, 'editNet'))) {
			my $rootID			= &getParam(1, undef, 'rootID');
			my $networkDec	= &getParam(1, undef, 'networkDec');
			my $ipv6				= &rootID2ipv6($rootID);
			$networkDec			= Math::BigInt->new($networkDec) if $ipv6;
			my $parent			= &getNetworkParentFromDB($rootID, $networkDec, $ipv6);
			unless (defined $parent) {
				warn "No Parent found!\n";
				$q->delete('func'); $q->param('func', 'showAllNets');
			} else {
				my $parentDec		= $parent->{network};
				my $parentID		= $parent->{ID};
				unless (&checkNetACL($parentID, 'w')) {
					my $network	= ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec);
					my $parent	= ($ipv6) ? &netv6Dec2net($parentDec) : &dec2net($parentDec);
					&warnl(sprintf(_gettext("Not enough rights to add this Network '%s' under '%s'"), $network, $parent));
					$q->delete('func'); $q->param('func', 'showAllNets');
				}
			}

			unless (&checkNetworkLock($rootID, $networkDec, $ipv6)) {
				$q->delete('func'); $q->param('func', 'showAllNets');
			}
		}
		elsif (&getParam(1, '', 'func') eq 'flushCache') {
			&flushACLCache();
			&flushNetCache();
			$q->delete('func'); $q->param('func', 'showAllNets');
		}
	}
	
	$HaCi::GUI::init::t->{V}->{jumpTo}  = $session->param('currRootID') . '-' . $session->param('currNet') if $session->param('currRootID') && $session->param('currNet');
	&HaCi::GUI::main::start() unless $conf->{var}->{soap} || &getParam(1, 0, 'exportSubnet');

	if (&getParam(1, 0, 'newWindow') && &getParam(1, '', 'newWindow') eq 'showStatus') {
		$t->{V}->{page}  = 'showStatus';
	} else {
		$t->{V}->{page}  = 'main';
	}
}

sub expandTo {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	my $rootID	= shift;
	my $network	= shift || '';
	my $ipv6		= &rootID2ipv6($rootID);
	$network	=~ s/[^\w\.\:\/]//g;

	return 0 unless $network;

	unless (&checkSpelling_Net($network, $ipv6) || &checkSpelling_IP($network, $ipv6)) {
		warn "Cannot jump to. No IP Address / network: '$network'\n";
		return 0;
	}

	$network					.= ($ipv6) ? '/128' : '/32' unless $network =~ /\/\d+$/;
	my $networkDec		= ($ipv6) ? &netv62Dec($network) : &net2dec($network);
	my $s							= $HaCi::HaCi::session;
	my $expands				= $s->param('expands') || {};
	my $networkTable	= $conf->{var}->{TABLES}->{network};
	unless (defined $networkTable) {
		warn "Cannot jump to network. DB Error\n";
		return 0;
	}

	my $ipv6ID		= ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : '';
	my $netID			= &getNetID($rootID, $networkDec, $ipv6ID);
	my $bestMatch	= (defined $netID) ? $networkDec : 0;
	unless ($bestMatch) {
		$networkDec		= &getDBNetworkBefore($rootID, $networkDec, $ipv6);
		$netID				= &getNetID($rootID, $networkDec, $ipv6ID);
		$bestMatch		= (defined $netID) ? $networkDec : 0;
	}

	while (my $parent	= &getNetworkParentFromDB($rootID, $networkDec, $ipv6)) {
		my $parentDec	= $parent->{network};
		&expand('+', 'network', $parentDec, $rootID) unless $expands->{network}->{$rootID}->{$parentDec};
		$bestMatch	= $parentDec unless $bestMatch;
		$networkDec	= $parentDec;
	}

	if ($bestMatch) {
		&expand('+', 'root', $rootID);
		$session->param('jumpTo', 1);
		$session->param('jumpToNet', $bestMatch);
		$session->param('jumpToRoot', $rootID);
		$session->param('currNet', $bestMatch);
		$session->param('currRootID', $rootID);
	} else {
		$session->clear('jumpTo');
		$session->clear('jumpToNet');
		$session->clear('jumpToRoot');
	}
}

sub hook {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	my ($filename, $buffer, $bytes_read, $data) = @_;
	my $percent	= int(($bytes_read * 100) / $data);
	my $status	= {
		title   => "Retrieving File '$filename'",
		percent => $percent,
		detail  => "Read $bytes_read/$data bytes of $filename",
	};
	my $statFile  = $conf->{static}->{path}->{statusfile} . '_.stat';
	eval {
		Storable::lock_store($status, $statFile) or warn "Cannot store Status ($statFile)!\n";
	}; 
	if ($@) {
		warn $@; 
	};
}

sub getSession {
	my $sessID	= shift;

	eval {
		$session	= CGI::Session->load($sessID);
	};
	if ($@ || !defined $session) {
		warn $@ . ' ' . CGI::Session->errstr;
		$conf->{var}->{authenticationError} = _gettext("Session is broken");
		$conf->{var}->{relogin}	= 1;
		#Do not return 0 if sessID is undef, but force to gen new session, so that an API call didn't fail
		return 0 if defined $sessID && $sessID;
	}

	if ( defined $session && $session->is_expired ) {
		$conf->{var}->{authenticationError} = _gettext("Session is expired");
		$conf->{var}->{relogin}	= 1;
		return 0;
	}
	if ( !defined $session || $session->is_empty ) {
		if (defined $session) {
			&debug("Session " . $session->id() . " is empty. Generating new...");
		} else {
			&debug("Session cannot be loaded! Generating new...");
		}
		&genNewSession();
	}
	$session->expire($conf->{static}->{misc}->{sessiontimeout});

	return $session;
}

sub init {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	$q		= CGI->new(\&hook, $ENV{CONTENT_LENGTH});
	$pjx	= new CGI::Ajax(
		'reduceNetwork'	=> \&reduceNetwork,
		'expandNetwork'	=> \&expandNetwork,
		'reduceRoot'		=> \&reduceRoot,
		'expandRoot'		=> \&expandRoot,
		'showPlugin'		=> \&showPlugin,
		'mkShowStatus'	=> \&mkShowStatus,
	);
	$pjx->DEBUG(0);

	my %cookies = fetch CGI::Cookie;
	my $sessID	= (exists $cookies{CGISESSID}) ? $cookies{CGISESSID}->value : undef;

	return unless &getSession($sessID);

	($aclCacheHandle, $netCacheHandle)	= &initCache();
	if (defined $netCacheHandle) {
		$netCache->{DB}		= $netCacheHandle->get('DB');
		$netCache->{NET}	= $netCacheHandle->get('NET');
		$netCache->{FILL}	= $netCacheHandle->get('FILL');
	} else {
		$netCache->{DB}		= {};
		$netCache->{NET}	= {};
		$netCache->{FILL}	= {};
	}
	$aclCache	= (defined $aclCacheHandle) ? $aclCacheHandle->get('HASH') : {};

	&initTables();

	if (&getParam(1, 0, 'locale')) {
		$session->param('locale', &getParam(1, '', 'locale'));
	}
	$q->delete('jumpTo') if &getParam(1, 0, 'jumpTo') && &getParam(1, '', 'jumpTo') eq '<IP Adresse>';
}

sub soapInit {
	my $soapArgs	= shift;
	my $user			= shift @{$soapArgs};
	my $pass			= shift @{$soapArgs};

	$q->delete('login'), $q->param('login', 1);
	$q->delete('username'), $q->param('username', $user);
	$q->delete('password'), $q->param('password', $pass);
}

sub genNewSession {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	$session	= CGI::Session->new() or die CGI::Session->errstr;
	$session->expire($conf->{static}->{misc}->{sessiontimeout});
	
	if (ref($q) && &getParam(1, 0, 'locale')) {
		$session->param('locale', &getParam(1, '', 'locale'));
	}
}

sub authentication {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};

	my $user				= shift;
	my $pass				= shift;
	my $bLogin			= shift;
	my $sessionUser	= shift;

	if ($bLogin) {
		unless (defined $user) {
			$conf->{var}->{authenticationError}	= _gettext("No User given");
			return 0;
		}

		my @authModules	= (($bLogin && $user eq 'admin') || ($sessionUser eq 'admin'))
			? 'internal'
			: (exists $conf->{user}->{auth}->{authmodule} && $conf->{user}->{auth}->{authmodule} ne '') ? split(/\s*,\s*/, $conf->{user}->{auth}->{authmodule}) : ('internal');

		my $authErrors	= [];
		foreach (@authModules) {
			my $authModule	= $_;
			$authModule			= 'internal' if $authModule eq 'HaCi'; # fix to be backward compatible

			my $authModuleFile	= $conf->{static}->{path}->{authmodules} . '/' . $authModule . '.pm';
			eval {
				require $authModuleFile;
			};
			if ($@) {
				$conf->{var}->{authenticationError}	= _gettext("Authentication Error");
				&warnl($@);
				return 0;
			}

			my $auth	= "HaCi::Authentication::$authModule"->new();
			$auth->session($session);
			$auth->init() if $authModule eq 'internal';

			$auth->user($user);
			$auth->pass($pass);
			
			if ($auth->authenticate()) {
				$conf->{var}->{authenticated}	= 1;
				&updateSettingsInSession();
				&debug("Successfully authenticated by '$authModule'") if 1;
				return 1;
			} else {
				push @{$authErrors}, $conf->{var}->{authenticationError};
				$conf->{var}->{authenticationError}	= '';
				&debug("Not authenticated by '$authModule'") if 1;
			}
		}

		$conf->{var}->{authenticationError}	= join('<br>', @{$authErrors});
		return 0;
	} else {
		if (defined $session->param('authenticated') && $session->param('authenticated')) {
			&debug("Allready authenticated") if 0;
			$conf->{var}->{authenticated}	= 1;
			return 1;
		} else {
			&debug("Not authenticated");
			return 0;
		}
	}
}

sub finalize {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	my $template	= $HaCi::GUI::init::t;
	my $cookie;
	if (defined $session) {
		$cookie	= $q->cookie(
			-name		=> $session->name,
			-value	=> $session->id 
		);
	} else {
		&debug("No session available while finalizing. Setting session-id to 0");
		$cookie	= $q->cookie(
			-name		=> 'CGISESSID',
			-value	=> 0
		);
	}

	unless ($conf->{var}->{soap}) {
		my $html_output	= '';
		my $warnl				= &prWarnl();
		my $prWarnl			= ($warnl) ? '<script>showWarnl()</script>' : '';
		$template->{T}->process($conf->{static}->{path}->{templateinit}, $template->{V}, \$html_output)
			|| die $template->{T}->error();
		$html_output	= &prNewWindows(1) . $html_output . $prWarnl;
	
		if ($ENV{MOD_PERL}) {
			print $q->header(
				-cookie		=> $cookie,
				-charset	=> 'UTF-8'
			);
			print $pjx->build_html($q, $html_output);
		} else {
			$q->header();	# Bad Fix for CGI - AJAX - Cookie Bug
			print $pjx->build_html($q, $html_output, {
				-cookie		=> $cookie,
				-charset	=> 'UTF-8'
			});
		}

		print $q->Dump() if 0;
		if (0) { map { warn " $_ -> " . &getParam(1, '', $_); } $q->param; } # Dump

		print '<script>var aktiv = setTimeout("refresh()", 500);</script>' if $conf->{var}->{reloadPage};
		print '<script>window.close();</script>' if $conf->{var}->{closePage};
	}
	undef $HaCi::HaCi::q;

	# Cache Statistics
	if (0) {
		warn "Netcache-DB  : $conf->{var}->{CACHESTATS}->{DB}->{FAIL}/$conf->{var}->{CACHESTATS}->{DB}->{TOTAL}\n";
		warn "Netcache-NET : $conf->{var}->{CACHESTATS}->{NET}->{FAIL}/$conf->{var}->{CACHESTATS}->{NET}->{TOTAL}\n";
		warn "Netcache-FILL: $conf->{var}->{CACHESTATS}->{FILL}->{FAIL}/$conf->{var}->{CACHESTATS}->{FILL}->{TOTAL}\n";
	}

	if (defined $netCacheHandle) {
		warn "Cannot set Cache (netCache-DB!)\n" unless $netCacheHandle->set('DB', $netCache->{DB});
		warn "Cannot set Cache (netCache-FILL!)\n" unless $netCacheHandle->set('FILL', $netCache->{FILL});
		warn "Cannot set Cache (netCache-NET!)\n" unless $netCacheHandle->set('NET', $netCache->{NET});
	}

	if (defined $aclCacheHandle) {
		warn "Cannot set Cache (aclCache!)\n" unless $aclCacheHandle->set('HASH', $aclCache);
	}

	if (defined $session) {
		$session->clear('jumpTo');
		$session->clear('jumpToNet');
		$session->clear('jumpToRoot');
		$session->flush();
	}

	&warnl($conf->{var}->{authenticationError}) if $conf->{var}->{authenticationError} && $conf->{var}->{soap};
	return &prWarnl(1) if $conf->{var}->{soap};
}

sub checkRights {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	my $q	= $HaCi::HaCi::q;
	foreach ($q->param()) {
		&delParam($_) if 
			(
				/^abortAddNet$/ ||
				/^abortDelNet$/ ||
				/^checktAddNet$/ ||
				/^submitAddNet$/
			) && !&checkRight('addNet') ||
			(
				/^commitDelNet$/ ||
				/^delNet$/ ||
				/^splitNet$/ ||
				/^submitSplitNet$/ ||
				/^abortSplitNet$/ ||
				/^editNet$/ ||
				/^editPluginConf$/ ||
				/^netFuncType$/
			) && !&checkRight('editNet') ||
			(
				/^submitAddRoot$/
			) && !&checkRight('addRoot') ||
			(
				/^abortDelRoot$/ ||
				/^abortEditRoot$/ ||
				/^commitDelRoot$/ ||
				/^delRoot$/ ||
				/^editRoot$/
			) && !&checkRight('editRoot') ||
			(
				/^abortDelGroup$/ ||
				/^abortEditGroup$/ ||
				/^commitDelGroup$/ ||
				/^delGroup$/ ||
				/^editGroup$/ ||
				/^newGroup$/ ||
				/^submitEditGroup$/
			) && !&checkRight('groupMgmt') ||
			(
				/^abortDelTmpl$/ ||
				/^abortEditTmpl$/ ||
				/^commitDelTmpl$/ ||
				/^delTmpl$/ ||
				/^editNetTypeTmpl$/ ||
				/^newTmpl$/ ||
				/^submitAddTmplEntry$/ ||
				/^submitEditTmplEntry$/ ||
				/^submitDeleteTmplEntry$/
			) && !&checkRight('tmplMgmt') ||
			(
				/^abortDelUser$/ ||
				/^abortEditUser$/ ||
				/^commitDelUser$/ ||
				/^delUser$/ ||
				/^editUser$/ ||
				/^newUser$/ ||
				/^submitEditUser$/
			) && !&checkRight('userMgmt') ||
			(
				/^submitshowPlugins$/ ||
				/^submitPluginGlobConfig$/ ||
				/^abortPluginGlobConfig$/ ||
				/^editPluginGlobConf$/
			) && !&checkRight('pluginMgmt') ||
			(
				/^copyNetsTo$/ ||
				/^moveNetsTo$/ ||
				/^copyToRootID$/ ||
				/^deleteNets$/ ||
				/^combineNets$/ ||
				/^submitCombineNets$/ ||
				/^abortCombineNets$/ ||
				/^editTree$/ ||
				/^finishEditTree$/
			) && !&checkRight('editTree') ||
			(
				/^search$/ ||
				/^compare$/
			) && !&checkRight('search') ||
			(
				/^getFreeSubnetsFromSearch$/
			) && (!&checkRight('search') || !&checkRight('addNet')) ||
			(
				/^getFreeSubnets$/
			) && (!&checkRight('search')) ||
			(
				/^submitImportASNRoutes$/ 
			) && !&checkRight('impASNRoutes') ||
			(
				/^getFreeSubnets$/
			) && !&checkRight('showNetDet') ||
			(
				/^submitImpDNSTrans$/ ||
				/^submitImpDNSLocal$/
			) && !&checkRight('impDNS') ||
			(
				/^submitImpConfig$/ ||
				/^impCSVChangeSep$/ ||
				/^submitImpCSV$/
			) && !&checkRight('impConfig') ||
			(
				/^showAditLogs$/ ||
				/^showAditLogsLeft$/ ||
				/^showAditLogsRight$/ ||
				/^commitShowAuditLogs$/
			) && !&checkRight('showAuditLogs')
	}
}

sub delParam {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};
	my $param	= shift;
	my $q			= $HaCi::HaCi::q;

	&debug("Deleting Parameter '$param'! Not enough rights!!!");
	$q->delete($param);
}

sub checkIfExistsInDB {
	warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs};

	my $table	= shift;
	my $col		= shift;
	my $val		= shift;

	my $table  = $conf->{var}->{TABLES}->{$table};
	unless (defined $table) {
		&warn("Cannot check. DB Error ($table)");
		return 1;
	}
	my $DB  = ($table->search(['ID'], {$col => $val}))[0];

	return (defined $DB) ? 1 : 0;
}

1;

# vim:ts=2:sw=2:sws=2
