<?
/*
(C) 2004-2013 DatuX - info@datux.nl

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 3 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, see <http://www.gnu.org/licenses/>.
*/

	require_once("../common.php");
	require_once("../network/network.php");
	require_once("../proc.php");
	IncludeLang(__FILE__);

	$VPN_IPSEC_DIR="/etc/ipsec.d";
	$VPN_PUB="$VPN_IPSEC_DIR/ipsec.pub";
	$VPN_TUNNEL_DIR="$VPN_IPSEC_DIR/tunnels";
	$VPN_LOG="/var/log/secure";
	$VPN_STARTERRORS="$VPN_IPSEC_DIR/starterrors";

	function vpn_Stop()
	{
		if (SvcIsUp("ipsec"))
		{
			SvcDown("ipsec");
		}
	}

	//RET: false als het werk, anders error text.
	function vpn_Restart()
	{
		//only works on internet server
		if (!ModuleMatch("I"))
			return false; //return ok-code

		// global $VPN_STARTERRORS;
		vpn_Stop();
		// LogInfo(_LogVPNStarting());
		SvcUp("ipsec");
		// FileWriteRaw("/etc/sysconfig/pluto_updown", "DEFAULTSOURCE=".GetFirstLanIp()."\n");
		// Cmd("pgrphack closefd /usr/sbin/ipsec setup start |grep 'FATAL:' 1>&2",'',$out,$err);

		// if ($err)
		// {
		// 	FileWrite($VPN_STARTERRORS,$err);
		// 	LogError(_LogVPNstartErrors(implode(" ",$err)));
		// 	return ($err);
		// }
		// else
		// {
		// 	FileWrite($VPN_STARTERRORS,'');
		// 	sleep(2); //wacht zodat VPN subsysteem up is
		// 	//kijk of er tunnels up moeten
		// 	$tunnels=vpn_ReadTunnels();
		// 	foreach ($tunnels as $name=>$conf)
		// 	{
		// 		//hij moet up zijn, EN hij is reeds getest
		// 		if ((!vpn_TunnelIsDownOnBoot($name)) && vpn_TunnelIsTested($name))
		// 			vpn_TunnelUp($name);
		// 	}
		// 	return false;
		// }
	}

	function vpn_StartErrors()
	{
		global $VPN_STARTERRORS;
		if (is_file($VPN_STARTERRORS))
		{
			$raw=FileReadRaw($VPN_STARTERRORS);
		}
		return ($raw);
	}

	/*function vpn_Failed()
	{
		global $VPN_IPSEC_DIR;
		return (is_file("$VPN_IPSEC_DIR/failed"));
	}

	function vpn_Tested()
	{
		global $VPN_IPSEC_DIR;
		return (is_file("$VPN_IPSEC_DIR/tested"));
	}
		*/


	//force vpn monitoring check. ( this is also done every minute with a crontab)
	function vpn_MonUpdate($name)
	{
		Progress("Checking tunnel status $name...");
		SafeExec("syn3-ipseccheck '$name'");
		Progress("");
	}

	function vpn_TunnelUp($name)
	{

		$err=Cmd("
			(
			ipsec auto --down '$name' 2>/dev/null;
			ipsec auto --rereadsecrets;
			ipsec auto --replace '$name' &&
			ipsec auto --asynchronous --up '$name'
			) 2>&1
		",'',$out,$errout);
		if ($err!=0)
		{
			$out[]="Exit code $err";
			LogError(_LogTunnelUpError($name,implode(" ",$out)));
			return false;
		}
		else
		{
			LogInfo(_LogTunnelUp($name));
			return true;
		}
	}

	function vpn_TunnelDown($name)
	{
		vpn_TunnelSetDownOnBoot($name,1);

		$err=Cmd("ipsec auto --delete '$name' 2>&1",'',$out,$errout);
		vpn_MonUpdate($name);
		if ($err!=0)
		{
			LogError(_TunnelDownError($name,implode(" ",$out)));
			return false;
		}
		else
		{
			LogInfo(_LogTunnelDown($name));
			return true;
		}
	}

	function vpn_TunnelIsTested($name)
	{
		global $VPN_TUNNEL_DIR;
		return (!is_file("$VPN_TUNNEL_DIR/$name.untested"));
	}

	function vpn_TunnelSetTested($name,$tested)
	{
		global $VPN_TUNNEL_DIR;

		if ($tested)
			FileDelete("$VPN_TUNNEL_DIR/$name.untested");
		else
			FileWriteRaw("$VPN_TUNNEL_DIR/$name.untested","");
	}

	function vpn_TunnelIsDownOnBoot($name)
	{
		$tunnel=vpn_ReadTunnel($name);
		return ($tunnel["auto"]=="up");
	}

	function vpn_TunnelSetDownOnBoot($name,$down)
	{
		$tunnel=vpn_ReadTunnel($name);
		if ($down)
			$tunnel["auto"]="ignore";
		else
			$tunnel["auto"]="up";
		vpn_WriteTunnel($name, $tunnel, false);
	}


	//gooi tunnel up, maar run backup script voor het geval er netwerk
	//conflict is. in dit geval gaat tunnel weer down
	function vpn_TunnelTestStart($name)
	{
		//test loopt nog?
		if ($_SESSION[testtunnel] && posix_kill($_SESSION[testtunnel][pid],0))
			Error(_TestAlreadyRunning());

		//start het backup script en onthoudt PID
		$_SESSION[testtunnel][pid]=BgExec("exec testtunnel.sh '$name'");
		$_SESSION[testtunnel][name]=$name;
		vpn_TunnelSetTested($name,1); //het backup script zet hem weer op 0 als het misgaat
		vpn_TunnelUp($name); //niet controleren op errors
		return true;
	}

	//Controleer of er nog een test loopt, en of deze geslaagd is.
	//Roep deze functie aan NA het refreshen van de pagina, zodat je weet
	//dat de netwerk settings niet verstoord zijn.
	function vpn_TunnelTestEnd()
	{
		//is there a test running?
		if ($_SESSION[testtunnel])
		{
			//kill het backup script BOEM
			posix_kill($_SESSION[testtunnel][pid],9);

			$name=$_SESSION[testtunnel][name];
			//test is klaar nu
			unset($_SESSION[testtunnel]);

			//kijk of test gelukt of mislukt is en wijzig config
			if (!vpn_TunnelIsTested($name))
			{
				LogError(_LogTunnelTestFailed($name));
			}
			else
			{
				vpn_TunnelSetDownOnBoot($name, 0);
				vpn_MonUpdate($name);
			}
		}
	}

	function vpn_ReadSecret($name)
	{
		global $VPN_TUNNEL_DIR;
		$line=FileReadRaw("$VPN_TUNNEL_DIR/$name.key");
		preg_match('/"(.*)"/',$line,$matches);
		return ($matches[1]);
	}

	function vpn_WriteSecret($name,$secret)
	{
		global $VPN_TUNNEL_DIR;
		$tunnel=vpn_ReadTunnel($name);

		$line="$tunnel[leftid] $tunnel[right]: PSK \"$secret\"\n";
		FileWriteRaw("$VPN_TUNNEL_DIR/$name.key",$line);
	}

	function vpn_ValidName($name)
	{
		return (
			(!preg_match("/[^a-z0-9-]/",$name)) &&
			(strlen($name)<25) &&
			(strlen($name)>=3)
		);
	}

	function vpn_ValidSecret($secret)
	{
		return (
			(strlen($secret)>=8)
		);
	}


	function vpn_ValidPubKey($key)
	{
		$key=vpn_ReadableToPub($key);
		return (
			(strlen($key)==370) &&
			(substr($key,0,2)=='0s') &&
			(!preg_match("/[^A-Za-z0-9+\/]/",substr($key,2)))
		);
	}

	function vpn_SameKey($key1,$key2)
	{
		$key1=vpn_ReadableToPub($key1);
		$key2=vpn_ReadableToPub($key2);
		return ($key1==$key2);
	}


	function vpn_ReadPubKey()
	{
		global $VPN_PUB;
		$raw=FileReadMatches($VPN_PUB,"/#pubkey=/");
		preg_match("/.*pubkey=(.*)/",$raw[0],$matches);
		return ($matches[1]);
	}

	//make key readable (insert new-lines etc)
	function vpn_PubToReadable($pubkey)
	{
		return(preg_replace("/(.{50})/","\$0\n",$pubkey));
	}

	//filter all the crap from a readable key
	function vpn_ReadableToPub($pubkey)
	{
		return(preg_replace("/[^A-Za-z0-9+\/=]/","",$pubkey));
	}

	//returns corresponding tunnel filename
	function vpn_GetTunnelFile($name)
	{
		global $VPN_TUNNEL_DIR;
		return ("$VPN_TUNNEL_DIR/$name.conf");
	}

	function vpn_ReadTunnel($name)
	{
		$raw=FileRead(vpn_GetTunnelFile($name));
		foreach ($raw as $line)
		{
			if (preg_match("/^[^a-z]*([a-z]*)=(.*)$/",$line,$matches))
				$ret[$matches[1]]=$matches[2];
		}
		return ($ret);
	}

	function vpn_ReadTunnels()
	{
		global $VPN_TUNNEL_DIR;
		$dh=opendir($VPN_TUNNEL_DIR);
		while ($file=readdir($dh))
		{
			if (preg_match("/^(.*).conf$/",$file,$matches))
			{
				if ($matches[1] != "dummy")
					$ret[$matches[1]]=vpn_ReadTunnel($matches[1]);
			}
		}
		return ($ret);
	}

	function vpn_WriteTunnel($name,$data, $needs_testing=true)
	{
		global $VPN_IPSEC_DIR;
		global $VPN_TUNNEL_DIR;
		$raw[]="conn $name";
		foreach ($data as $varname=>$varvalue)
		{
			$raw[]="\t$varname=$varvalue";
		}
		//mark configuration as untested
		if ($needs_testing)
			FileWriteRaw("$VPN_TUNNEL_DIR/$name.untested");

		FileWrite(vpn_GetTunnelFile($name),$raw);
		LogInfo(_LogVPNSettingsChanged($name));
	}

	function vpn_DelTunnel($name)
	{
		global $VPN_TUNNEL_DIR;
		vpn_TunnelDown($name);

		FileDelete("$VPN_TUNNEL_DIR/$name.conf");
		FileDelete("$VPN_TUNNEL_DIR/$name.mon");
		FileDelete("$VPN_TUNNEL_DIR/$name.key");
		FileDelete("$VPN_TUNNEL_DIR/$name.down");
		FileDelete("$VPN_TUNNEL_DIR/$name.untested");
		LogInfo(_LogVPNTunnelDeleted($name));
	}


	$VPN_STATES=array(
		'Main'=>"Connecting...",
		'STATE_MAIN_I1'=>"Connecting...",
			'STATE_MAIN_R1'=>"Connection accepted...",
		'STATE_MAIN_I2'=>"Connection complete...",
		'STATE_MAIN_I3'=>"Checking keys...",
			'STATE_MAIN_R2'=>"Checking keys...",
		'STATE_MAIN_I4'=>"Keys accepted...",
			'STATE_MAIN_R3'=>"Waiting for tunnel request...",
		'Quick'=>"Requesting tunnel...",
		'STATE_QUICK_I1'=>"Requesting tunnel...",
			'STATE_QUICK_R1'=>"Tunnel request received...",
		'STATE_QUICK_I2'=>"Tunnel established",
			'STATE_QUICK_R2'=>"Tunnel established",
	);


	$VPN_PRIO=array(
			'Main'=>0,
		'STATE_MAIN_I1'=>1,
			'STATE_MAIN_R1'=>2,
		'STATE_MAIN_I2'=>3,
		'STATE_MAIN_I3'=>4,
			'STATE_MAIN_R2'=>5,
		'STATE_MAIN_I4'=>6,
			'STATE_MAIN_R3'=>7,
		'Quick'=>8,
		'STATE_QUICK_I1'=>9,
			'STATE_QUICK_R1'=>10,
		'STATE_QUICK_I2'=>11,
			'STATE_QUICK_R2'=>12,
	);

	//tries to determine state from a logline.
	//returns false on no state found
	function vpn_LogToState($logline)
	{
		global $VPN_STATES;

		if (preg_match("/to state (.*)$/",$logline,$matches))
		{
			if (isset($VPN_STATES[$matches[1]]))
				return ($matches[1]);
		}
		elseif (preg_match("/initiating (.*) Mode/",$logline,$matches))
		{
			if (isset($VPN_STATES[$matches[1]]))
				return ($matches[1]);
		}
		else
		{
			return false;
		}
	}
	//read logging output of a tunnel
	//returns: $ret[]=array($state,$line);
	function vpn_GetLog($name)
	{
		global $VPN_LOG;
		$lines=SafeExec("grep '\]: \"$name\"' '$VPN_LOG'|tail -1000");
		foreach ($lines as $line)
		{
			preg_match("/\]: \".*?\".(.*)/",$line,$matches);
			$ret[]=array(vpn_LogToState($matches[1]),$matches[1]);
		}
		return $ret;
	}

	//gets state of all tunnels
	//ret[tunnelname][state]=$state
	//ret[tunnelname][info][]=infolines
	function vpn_GetStatus()
	{
		global $VPN_STATES;
		global $VPN_PRIO;

		if (Cmd("ipsec whack --status",'',$output)!=0)
		{
			LogError("Error reading ipsec status. Please check if ipsec-service is running");
			return (false);
		}
		else {
			$ret=Array();
			foreach ($output as $line)
			{
				if (preg_match('/^.*#([0-9])*:.*"(.*?)"[:0-9]* ([A-Z0-9_]+) /',$line,$matches))
				{
					$nr=$matches[1];
					$name=$matches[2];
					$state=$matches[3];
					if (
						$name &&
						$state &&
						$VPN_PRIO[$state]>=$VPN_PRIO[$ret[$name]['state']]
					)
					{
						$ret[$name]['state']=$state;
						$ret[$name]['info'][]=$line;
					}
				}
				elseif (preg_match('/^.*"(.*?)": *(.*)$/',$line,$matches))
				{
					$name=$matches[1];
					$info=$matches[2];
					if ($name && $info)
					{
						$ret[$name]['info'][]=$info;
					}
				}
			}

			return $ret;
		}
	}

	/* zit nu in network.php
	if ($_SERVER['argv'][1]=="restart")
	{
		vpn_Restart();
	}
*/


?>
