<?php
/**
 * Implements the backup on demand system.
 *
 * @author Camilo Carromeu <camilo@carromeu.com>
 * @category class
 * @package core
 * @subpackage util
 * @copyright 2005-2017 Titan Framework
 * @license http://www.titanframework.com/license/ BSD License (3 Clause)
 * @see Instance
 * @link http://www.titanframework.com/docs/tutorials/backup/
 */
class Backup
{
	static private $backup = FALSE;

	static private $lock = FALSE;

	private $path = FALSE;

	private $validity = 86400;

	private $timeout = 86400;

	private $folder = FALSE;

	private final function __construct ()
	{
		$array = Instance::singleton ()->getBackup ();

		if (!is_array ($array) || !array_key_exists ('path', $array) || trim ($array ['path']) == '')
			return;

		$this->path = $array ['path'];

		if (!file_exists ($this->path) && !@mkdir ($this->path, 0777))
			throw new Exception ('Impossible to create folder ['. $this->path .'].');

		if (array_key_exists ('validity', $array) && is_numeric ($array ['validity']) && (int) $array ['validity'])
			$this->validity = (int) $array ['validity'];

		if (array_key_exists ('timeout', $array) && is_numeric ($array ['timeout']) && (int) $array ['timeout'])
			$this->timeout = (int) $array ['timeout'];

		self::$lock = uniqid (rand (), TRUE);
	}

	static public function singleton ()
	{
		if (self::$backup !== FALSE)
			return self::$backup;

		$class = __CLASS__;

		self::$backup = new $class ();

		return self::$backup;
	}

	public function isActive ()
	{
		return self::$lock !== FALSE;
	}

	public function getPath ()
	{
		return $this->path;
	}

	public function getRealPath ()
	{
		return realpath ($this->path);
	}

	public function getLockPath ()
	{
		return $this->getRealPath () . DIRECTORY_SEPARATOR .'.lock';
	}

	public function getLockId ()
	{
		return self::$lock;
	}

	public function getValidity ()
	{
		return (string) $this->validity;
	}

	public function getTimeout ()
	{
		return (string) $this->timeout;
	}

	public function getLock ()
	{
		$handle = fopen ($this->getLockPath (), 'a+');

		if ($handle === FALSE)
			throw new Exception ('Impossible to read/create lock ['. $this->getLockPath () .'].');

		if (flock ($handle, LOCK_EX) && (filemtime ($this->getLockPath ()) < strtotime ('-'. $this->getTimeout () .' seconds') || !filesize ($this->getLockPath ())))
		{
			ftruncate ($handle, 0);

			rewind ($handle);

			fwrite ($handle, (string) self::$lock);

			flock ($handle, LOCK_UN);

			fclose ($handle);

			return TRUE;
		}

		fclose ($handle);

		return FALSE;
	}

	public function releaseLock ()
	{
		if (file_exists ($this->getLockPath ()) && self::$lock === trim (@file_get_contents ($this->getLockPath ())))
			return @unlink ($this->getLockPath ());

		return FALSE;
	}

	public function getFolder ()
	{
		if ($this->folder === FALSE)
		{
			do
			{
				$folder = 'B'. shortlyHash (md5 (uniqid (rand (), TRUE)));
			} while (file_exists ($this->getRealPath () . DIRECTORY_SEPARATOR . $folder));

			if (!@mkdir ($this->getRealPath () . DIRECTORY_SEPARATOR . $folder, 0777))
				throw new Exception ('Impossible to create unique backup folder ['. $this->getRealPath () . DIRECTORY_SEPARATOR . $folder .'].');

			$this->folder = $folder;
		}

		return $this->folder;
	}

	static public function clear ()
	{
		$folders = glob (self::singleton ()->getRealPath () . DIRECTORY_SEPARATOR .'B*');

		if (is_array ($folders))
			foreach ($folders as $trash => $folder)
				if (is_dir ($folder) && filemtime ($folder) < strtotime ('-'. self::singleton ()->getValidity () .' seconds'))
				{
					unlink ($folder . DIRECTORY_SEPARATOR .'.htaccess');
					unlink ($folder . DIRECTORY_SEPARATOR .'.htpasswd');

					removeDir ($folder);

					toLog ('Backup folder deleted ['. $folder .'].');
				}

		return TRUE;
	}
}