<?php

class backup
{
	public $backupPath = null;
	public $sqlBackupFilename = null;
	public $zipBackupFilename = null;
	public $skipDatabaseTableData = array('sessions', 'cross_site_action', 'download_token');
	public $skipFolderContents = array('files', '.svn', 'cache', 'logs');

    function __construct()
    {
		// make sure the script can handle large folders/files
		ini_set('max_execution_time', 600);
		ini_set('memory_limit', '1024M');

        include_once(CORE_ROOT.'/includes/mysqldump/Mysqldump.php');
		
		// setup paths
		$this->backupPath = _CONFIG_FILE_STORAGE_PATH.'_backup';
		
		// make sure the backup folder exists
		if (!file_exists($this->backupPath))
        {
            @mkdir($this->backupPath);
        }
    }
	
	public function getBackupPath()
	{
		return $this->backupPath;
	}
	
	public function backupDatabase()
	{
		// setup database backup filename
		$this->sqlBackupFilename = 'database_'.date('YmdHis').'_'.MD5(microtime()).'.sql';
		
		// setup the sql backup path
		$sqlBackupFilePath = $this->getBackupPath().'/'.$this->sqlBackupFilename;
		
		// setup the options
		$dumpSettings = array(
			'add-drop-table' => true,
			'exclude-table-data' => $this->skipDatabaseTableData,
		);

		// dump the database
		$dump = new Mysqldump('mysql:host='._CONFIG_DB_HOST.';dbname='._CONFIG_DB_NAME, _CONFIG_DB_USER, _CONFIG_DB_PASS, $dumpSettings);
		$dump->start($sqlBackupFilePath);
		
		return file_exists($sqlBackupFilePath);
	}
	
	public function backupCode()
	{
		return $this->zipPath(_CONFIG_SCRIPT_ROOT);
	}
	
	public function zipPath($source)
	{
		// setup database backup filename
		$this->zipBackupFilename = 'code_'.date('YmdHis').'_'.MD5(microtime()).'.zip';
		
		// setup the zip backup path
		$sqlBackupFilePath = $this->getBackupPath().'/'.$this->zipBackupFilename;
        if (extension_loaded('zip'))
		{
			$zip = new ZipArchive();
			if ($zip->open($sqlBackupFilePath, ZIPARCHIVE::CREATE))
			{
				$source = realpath($source);
				if (is_dir($source))
				{
					$iterator = new RecursiveDirectoryIterator($source);
					$filtered = new DirFilter($iterator, $this->skipFolderContents); 

					// skip dot files while iterating 
					$files = new RecursiveIteratorIterator($filtered, RecursiveIteratorIterator::SELF_FIRST);
					foreach ($files as $file)
					{
						// double check path
						$file = realpath($file);
						if($source != substr($file, 0, strlen($source)))
						{
							continue;
						}
						
						if (is_dir($file))
						{
							$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
						}
						else if (is_file($file))
						{
							$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
						}
					}
				}
				else if (is_file($source))
				{
					$zip->addFromString(basename($source), file_get_contents($source));
				}
			}

			// add empty directories back in
			$zip->addEmptyDir('files');
			$zip->addEmptyDir('core/cache');
			$zip->addEmptyDir('core/logs');
			
			return $zip->close();
        }

        return false;
    }
}

class DirFilter extends RecursiveFilterIterator
{
    protected $exclude;
    public function __construct($iterator, array $exclude)
    {
        parent::__construct($iterator);
        $this->exclude = $exclude;
    }
    public function accept()
    {
        return !($this->isDir() && in_array($this->getFilename(), $this->exclude));
    }
    public function getChildren()
    {
        return new DirFilter($this->getInnerIterator()->getChildren(), $this->exclude);
    }
}
