<?php
define('passwordHash', '8f8974c6c5b11727032b68dd688daf19');
define('encrypted', 'HxfyP7JhmpzHGosn0kqRdF74SHQxdrBIPfOy4wZpiIl8rQwvH/zaiu2me0rZ/i8wfFZVHC5fJqDFT5SIZY85bj0HlxHT8EzDKDsJLaA8+7CEP/YvB1RWYvhTf9vMcgd6raehQssLBMKRS6kC0vKRRyas+ZK5ofIn/dPQnqYnYjVfPHcqf7RldbZZUDtV+u7hnxVcPphcGy9USGcdO4sfGq34aNckbbAU+1FhtJNYpUcqsK3M+gqQmaD4fYDczoJgVfQ4GfiBtKoKymCKY1ihXUdxpCQkxBSRwHKNlSVQ/Xh3s0gWuK78HtbOUupWN6WmTBNyyBUrwUmiMzl+Do2+KBlGZWsfFuHFgKAdRh0XrM4=');
?>
﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
	<title>JoomlaPack DataRestore</title>
	<style type="text/css">
	<!--
	body
	{
		background-color: #666666;
		font-family: Arial, Helvetica, sans;
		font-size: 11pt;
	}
	
	p
	{
		margin: 0px;
		padding: 0.4em 0px;
	}
	
	input { border: thin solid green; font-size: larger; }
	
	h2
	{
		margin: 0px;
		padding: 0.5em 0em 0.5em 0em;
		color: #996600;
		border-bottom: thin solid red;
		font-size: x-large;
	}
	
	#header
	{
		margin: 0px;
		border-top: thick solid black;
		border-left: medium solid black;
		border-right: medium solid black;
		border-bottom: thin solid black;
		background-color: #ff0000;
		color: white;
	}
	
	#header h1
	{
		font-family: Segoe UI, Arial, Helvetica, Sans;
		margin: 0em;
		padding: 0.3em 0.5em;
	}
	
	#mainbody
	{
		margin: 0px;
		background-color: #ffffff;
		min-height: 20em;
		border-left: medium solid black;
		border-right: medium solid black;
		padding: 0em 1em;
	}
		
	#footer
	{
		font-size: 8pt;
		color: #333333;
		background-color: silver;
		border-top: thin solid black;
		border-left: medium solid black;
		border-right: medium solid black;
		border-bottom: thick solid black;
		padding: 1em;
	}

	#footer p
	{
		margin: 0px;
		padding: 0.3em 1em;
	}
	
	#footer a
	{
		color: #333366;
		text-decoration: none;
	} 
	
	#error
	{
		background-color: #ffe0e0;
		margin: 1em 0em 0em 0em;
		padding: 0.6em;
		border-left: thick double red;
		font-weight: bold;
	}
	-->
	</style>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<div id="header">
	<h1>JoomlaPack DataRestore</h1>
</div>
<div id="mainbody">
<?php
/**
 * @package JoomlaPack
 * @subpackage DataRestore
 * @author JoomlaPack Developers
 * @copyright Copyright (c)2008 JoomlaPack Developers
 * @license GNU General Public License, version 2 or later
 * 
 * A simple database restoration script. Large portions derived from bigdump.php
 * by Alexey Ozerov, www.ozerov.de
 *
 * ΑΩ
 */

/* =============================================================================
 * G L O B A L   V A R I A B L E S
 * =============================================================================
 */
$task			= getVar('task', 'default');
$password		= getVar('password');
$error			= getVar('error', '');
$currentIndex 	= getVar('currentIndex',-1);
$offset			= getVar('offset', -1);
$start			= getVar('start', 0);
$done			= false;


define('LINESPERSESSION', 50);
define('DATA_CHUNK_LENGTH', 32768);
define('MAX_QUERY_LINES', 100);

/* =============================================================================
 * C L A S S E S  &  F U N C T I O N S
 * =============================================================================
 */

class cryptography { 
	/** @var string Stored password */
	var $password; 
	
	/**
	 * Assign a password for encryption / decryption
	 *
	 * @param string $password
	 */
    function set_key($password) { 
        $this->password = $password; 
    } 
     
    /**
     * Generates an initial vector
     *
     * @param int $iv_len Vector length in bytes
     * @return string The Initial Vector (IV)
     */
    function get_rnd_iv($iv_len) { 
        $iv = ''; 
        while ($iv_len-- > 0) { 
            $iv .= chr(mt_rand() & 0xff); 
        } 
        return $iv; 
    } 
     
    /**
     * Encrypts a string
     *
     * @param string $plain_text String to encode
     * @param int $iv_len IV length
     * @return string The ciphertext
     */
    function encrypt($plain_text, $iv_len = 16) { 
        $plain_text .= "\x13"; 
        $n = strlen($plain_text); 
        if ($n % 16) $plain_text .= str_repeat("\0", 16 - ($n % 16)); 
            $i = 0; 
            $enc_text = cryptography::get_rnd_iv($iv_len); 
            $iv = substr($this->password ^ $enc_text, 0, 512); 
            while ($i < $n) { 
                $block = substr($plain_text, $i, 16) ^ pack('H*', sha1($iv)); 
                $enc_text .= $block; 
                $iv = substr($block . $iv, 0, 512) ^ $this->password; 
                $i += 16; 
            } 
            return base64_encode($enc_text); 
    } 
    
    /**
     * Decrypts a string
     *
     * @param string $enc_text The ciphertext to decode
     * @param int $iv_len IV length
     * @return string The palintext message
     */
    function decrypt($enc_text, $iv_len = 16) { 
        $enc_text = base64_decode($enc_text); 
        $n = strlen($enc_text); 
        $i = $iv_len; 
        $plain_text = ''; 
        $iv = substr($this->password ^ substr($enc_text, 0, $iv_len), 0, 512); 
        while ($i < $n) { 
            $block = substr($enc_text, $i, 16); 
            $plain_text .= $block ^ pack('H*', sha1($iv)); 
            $iv = substr($block . $iv, 0, 512) ^ $this->password; 
            $i += 16; 
        } 
        return stripslashes(preg_replace('/\\x13\\x00*$/', '', $plain_text)); 
    } 
 
}

/**
 * Gets a page request parameter
 *
 * @param string $name The variable name
 * @param mixed $default The default value, if variable's not supplied
 * @return mixed
 */
function getVar($name, $default = null)
{
	if(isset($_REQUEST[$name]))
	{
		return $_REQUEST[$name];
	}
	else
	{
		return $default;
	}
}

/**
 * Checks the password supplied to the page against the stored password's MD5 sum
 *
 * @return bool True if password matches
 */
function authenticate()
{
	$password = getVar('password');
	$hash = md5($password);
	return $hash == passwordHash;
}

/**
 * Decrypts and returns the databases array
 *
 * @return array
 */
function getDatabasesArray()
{
	static $databases;
	
	if(!$databases)
	{
		$crypto = new cryptography();
		$crypto->set_key(getVar('password'));
		$serialized = $crypto->decrypt(encrypted);
		$databases = unserialize($serialized);
	}
	
	return $databases;
}

function getDBO()
{
	static $db;
	
	if(!$db)
	{
		global $currentIndex, $error;
		$databases = getDatabasesArray();
		$descriptor = getDescriptor($databases, $currentIndex);
		
		$host = $descriptor['host'];
		$username = $descriptor['username'];
		$password = $descriptor['password'];
		$database = $descriptor['database'];
		
		$db = @mysql_connect($host, $username, $password, true);
		if($db === false)
		{
			$error = "Can't connect to database server of $database database";
			return;
		}
		
		if(! @mysql_select_db($database, $db))
		{
			$db = false;
			$error = 'Could not select database '.$database;		
			return;
		}
		mysql_query("SET NAMES 'utf8'", $db);
	}
	
	return $db;
}

/**
 * Returns the n-th descriptor, where n = $index
 *
 * @param array $databases The databases array
 * @param int $index The numeric index
 * @return array|null The descriptor array, or null if not found
 */
function getDescriptor(&$databases, &$index)
{
	$current = -1;
	foreach($databases as $descriptor)
	{
		$current++;
		if($current == $index) return $descriptor;
	}
	return null;
}

/**
 * Process the backup file. When done it sets $done to true.
 * Upon finishing a batch, it updates $offset, $start, $currentIndex
 *
 */
function importFile()
{
	global $done, $currentIndex, $error, $start, $filesize, $filename, $offset;
	
	// Make sure we have a valid password
	if(!authenticate())
	{
		$error = 'Invalid password';
		return;
	}
	
	// Get database information descriptor
	$databases = getDatabasesArray();
	
	// Get current array index
	$currentIndex = getVar('currentIndex',-1);
	
	if($currentIndex < 0)
	{
		// First time running; initialize
		$currentIndex = 0;
		$start = 0;
		$offset = 0;
	}
	else
	{
		// Populate parameters
		$offset = getVar('offset',0);
	}
	
	$descriptor = getDescriptor($databases, $currentIndex);
	$filename = $descriptor['dumpFile'];
	
	$file = @fopen($filename, 'r');
	if($file === false)
	{
		$error = 'Could not open database dump file '.$filename.' for reading';
		return;
	}
	
	// Allowed comment delimiters: lines starting with these strings will be dropped by BigDump
	$comment[] = '#'; // Standard comment lines are dropped by default
	$comment[] = '-- ';

	// Get the file size
	if (fseek($file, 0, SEEK_END) == 0) {
		$filesize = ftell($file);
	} else {
		$error = 'Could not determine filesize of '.$filename;
		return;
	}
	
	// Check offset upon $filesize
	if($offset > $filesize)
	{
		$error = 'Attempt to read past EOF of dump file '.$filename;
		return;
	}
	
	// Set file pointer to $offset
	if (fseek($file, $offset) != 0) {
		$error = 'Could not set file pointer of dump file '.$filename;
		return;
	}
	
	// Start processing queries from $file
	$query = "";
	$queries = 0;
	$linenumber = $start;
	$querylines = 0;
	$inparents = false;
	
	// Stay processing as long as the LINESPERSESSION is not reached or the query is still incomplete
	while ($linenumber < $start + LINESPERSESSION || $query != "")
	{
		// Read the whole next line
		$dumpline = "";
		while (!feof($file) && substr($dumpline, -1) != "\n") {
			$dumpline .= fgets($file, DATA_CHUNK_LENGTH);
		}
		
		if ($dumpline === "")
		{
			break;
		}
		
		// Handle DOS and Mac encoded linebreaks (I don't know if it will work on Win32 or Mac Servers)
		$dumpline = str_replace("\r\n", "\n", $dumpline);
		$dumpline = str_replace("\r", "\n", $dumpline);
		
		// Skip comments and blank lines only if NOT in parents
		if (!$inparents) {
			$skipline = false;
			reset($comment);
			foreach ($comment as $comment_value) {
				if (!$inparents && (trim($dumpline) == "" || strpos($dumpline, $comment_value) === 0)) {
					$skipline = true;
					break;
				}
			}
			if ($skipline) {
				$linenumber++;
				continue;
			}
		}
		
		// Remove double back-slashes from the dumpline prior to count the quotes ('\\' can only be within strings)
		$dumpline_deslashed = str_replace("\\\\", "", $dumpline);
		
		// Count ' and \' in the dumpline to avoid query break within a text field ending by ;
		// Please don't use double quotes ('"')to surround strings, it wont work
		$parents = substr_count($dumpline_deslashed, "'") - substr_count($dumpline_deslashed, "\\'");
		if ($parents % 2 != 0)
		{
			$inparents = !$inparents;
		}

		// Add the line to query
		$query .= $dumpline;
		
		// Don't count the line if in parents (text fields may include unlimited linebreaks)
		if (!$inparents)
		{
			$querylines++;
		}

		// Stop if query contains more lines as defined by MAX_QUERY_LINES
		if ($querylines > MAX_QUERY_LINES) {
			$error = 'Stopped processing dump file '.$filename.' at line '.$linenumber.'. Maximum number of lines per query exceeded.';
			return;
		}
		
		// Process query if end of query detected (; as last character) AND NOT in parents
		if (ereg(";$", trim($dumpline)) && !$inparents) {
			if(!queryPostProcessing($query)) return;
			$queries++;
			$query = "";
			$querylines = 0;				
		}			
		
		$linenumber++;			
	}
	
	// Get the current file position
	$offset = ftell($file);
	if (!$offset) {
		$error = 'Can not determine the file pointer of dump file '.$filename;
		return;
	}

	// Test for completion
	if($linenumber < ($start + LINESPERSESSION - 1))
	{
		// We have finished this file. Are we done yet?
		if($currentIndex == count($databases) - 1 )
		{
			$done = true;
		}
		else
		{
			// Nope; initialize next file
			$currentIndex++;
			$offset = 0;
			$start = 0;
		}
	}
}

/**
 * Handles a query read from the file 
 *
 * @param string $query SQL command
 */
function queryPostProcessing($query)
{
	global $error;
	
	$db = getDBO();
	if($db === false)
	{
		if(!$error) $error = "Error accessing database";
		return false;
	}
	
	// Drop anything except CREATE TABLE and INSERT INTO commands
	$query = trim($query);
	$command = trim(substr($query, 0, 12));
	$command = strtoupper($command);
	$doProcess = ($command == 'CREATE TABLE') || ($command == 'INSERT INTO');
	
	// Make DROP TABLES for CREATE TABLE commands
	if($command == 'CREATE TABLE')
	{
		$string = trim(substr($query, 12, max( 100, strlen($query)-12 )));
		$backtick = strpos($string, '`');
		if($backtick === false)
		{
			// No backticks found, use spaces to find table name
			$firstSpace = strpos($string, ' ');
			$tableName = trim(substr($string, 0, $firstSpace));
		}
		else
		{
			$string = substr($string, $backtick+1, strlen($string) - $backtick);
			$lastBacktick = strpos($string, '`');
			$tableName = substr($string, 0, $lastBacktick);
		}
			
		$sql = 'DROP TABLE IF EXISTS `'.$tableName.'`';
		@mysql_query($sql, $db);
	}
	
	if(!$doProcess)
	{
		return true; 
	}
	
	if(! @mysql_query($query, $db))
	{
		$error = 'Failed to run query:<br/>'.htmlentities($query).'<br/><br/>MySQL error:<br/>'.htmlentities(mysql_error($db));
		return false;
	}
	else
	{
		return true;	
	}
}

/* =============================================================================
 * M A I N   P A G E   L O G I C
 * =============================================================================
 */

if( $task == 'suicide' )
{
	// Remove database files
	$databases = getDatabasesArray();
	$currentIndex = -1;
	while($currentIndex < count($databases))
	{
		$currentIndex++;
		$descriptor = getDescriptor($databases, $currentIndex);
		$filename = $descriptor['dumpFile'];
		@unlink($filename);
	}
	
	// Remove ourself
	@unlink(__FILE__);
	
	// Show farewell message
?>
	<h2>Goodbye</h2>
	<p>This script has been removed from your server.</p>
<?php
}
elseif( $error )
{
	// Show error message
?>
<h2>An error occured</h2>
<div id="error">
	<?php echo $error ?>
</div>
<p>Please click <a href="datarestore.php?task=suicide">on this link</a> to remove
this script from the server, or <a href="datarestore.php">try running it again</a>
if you believe you have rectified the source of the error.</p>
<?php
}
elseif(!authenticate())
{
	// Show password entry page
?>
<h2>Welcome to JoomlaPack DataRestore!</h2>
<p>This script will restore your database(s) automatically, using the same connection
information which was used at backup time. If you have changed your mind, click
<a href="datarestore.php?task=suicide">on this link</a> to remove this script and cancel
the procedure.</p> 
<p>In order to continue, you will have to enter your restoration password, presented to
you when you clicked the "Restore" button. This is a security feature which prevents
unauthorized access to the restoration script. Please supply your restoration password
in the box below and click on the "Proceed" button.</p>
<form action="datarestore.php" method="post">
	<p><input type="password" name="password" value="" size="14" /></p>
	<p><input type="submit" value="Proceed" /></p>
</form>
<?php
}
else
{
	// Show progress
	importFile();

	if(!$error && !$done)
	{
		// No errors; produce a status page
		$percent = round(($offset / $filesize) * 100);
?>
	<h2>Database restoration in progress</h2>
	<p>Now processing <tt><?php echo $filename; ?></tt></p>
	<div style="display:block; margin-left: 10%; width: 80%; background-color:#EEEEAA; text-align:left; vertical-align:middle; border: thin solid black;">
	<div style="text-align:center; height:30px;width:<?php echo $percent; ?>%;background-color:#000080;margin:0px;">
	</div>
	</div>
	<p>Processed <?php echo $offset ?> / <?php echo $filesize ?> bytes (<?php echo $percent; ?>%)<br/>
	Total queries processed: <?php echo $start ?></p>
	<form action="datarestore.php" method="post" name="step">
		<input type="hidden" name="offset" value="<?php echo $offset ?>" />
		<input type="hidden" name="start" value="<?php echo $start ?>" />
		<input type="hidden" name="currentIndex" value="<?php echo $currentIndex ?>" />
		<input type="hidden" name="password" value="<?php echo $password ?>" />
	</form>
	<script type="text/javascript" language="Javascript">
		document.forms.step.submit();
	</script>
<?php
	}
	elseif($error)
	{
		// An error occured; inform the user
?>
	<form action="datarestore.php" method="post" name="step">
		<input type="hidden" name="error" value="<?php echo $error ?>" />
		<input type="hidden" name="password" value="<?php echo $password ?>" />
	</form>
	<script type="text/javascript" language="Javascript">
		document.forms.step.submit();
	</script>
<?php
	}
	elseif($done)
	{
		// Show finished and commit suicide
	?>
		<h2>Finished</h2>
		<p>Database restoration is complete. Please click <a href="datarestore.php?task=suicide">on this link</a>
		to remove this script from your server. This is a necessary security precaution.</p>	
	<?php
	}
}

?>
<p>&nbsp;</p>
</div>
<div id="footer">
	<p>JoomlaPack DataRestore - The database restoration script of JoomlaPack<br />
	Copyright &copy;2008-2009 <a href="http://www.joomlapack.net">JoomlaPack Developers</a>.
	</p>
	<p>JoomlaPack DataRestore is is free software; you can redistribute it and/or
	modify it under the terms of the
	<a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU General
	Public License</a> as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.</p>
</div>
</body>
</html>
<?php
/* =============================================================================
 * A P P E N D E D   C O N F I G U R A T I O N
 * =============================================================================
 */
