<?php

	/**
	 * AVE.cms
	 *
	 * Класс, предназначенный для создания и восстановления дампов БД через Панель управления
	 *
	 * @package AVE.cms
	 * @version 3.x
	 * @filesource
	 * @copyright © 2007-2014 AVE.cms, http://www.ave-cms.ru
	 *
	 */

	class AVE_DB_Service
	{
		/**
		 * Метка-разделитель SQL-запросов
		 *
		 * @public string
		 */
		public $_delimiter = '#####systemdump#####';

		/**
		 * Дамп базы данных
		 *
		 * @public string
		 */
		public $_database_dump = '';


		/**
		 * Метод, предназначенный для формирования файла дампа базы данных
		 *
		 * @return boolean
		 */
		function _databaseDumpCreate()
		{
			global $AVE_DB;

			if (! (! empty($_REQUEST['ta']) && is_array($_REQUEST['ta'])))
				return false;

			$search  = array("\x00", "\x0a", "\x0d", "\x1a");
			$replace = array('\0', '\n', '\r', '\Z');

			$this->_database_dump = '';

			// Циклически обрабатываем каждую таблицу
			foreach ($_REQUEST['ta'] as $table)
			{
				if (! DB_EXPORT_PREFIX)
					$table_export = preg_replace('/^' . PREFIX . '/', '%%PRFX%%', $table);

				// Если таблица имеет корректный префикс
				if (preg_match('/^' . preg_quote(PREFIX) . '_/', $table))
				{
					$row = $AVE_DB->Query("SHOW CREATE TABLE " . $table)->FetchArray();
					// Сохраняем CREATE и DROP запросы
					$this->_database_dump .= "DROP TABLE IF EXISTS `" . (! DB_EXPORT_PREFIX ? $table_export : $table) . "`;" . $this->_delimiter . "\n";

					if (! DB_EXPORT_PREFIX)
						$this->_database_dump .= str_replace('CREATE TABLE `' . PREFIX . '_', 'CREATE TABLE `%%PRFX%%_', $row[1]) . ";" . $this->_delimiter . "\n\n";
					else
						$this->_database_dump .= $row[1] . ";" . $this->_delimiter . "\n\n";

					$nums = 0;

					// Получаем данные, которые в дальнейшем будут вставлены в INSERT запросы.
					$sql = $AVE_DB->Query('SELECT * FROM `' . $table . '`');

					while ($row = $sql->FetchArray())
					{
						if ($nums == 0)
						{
							$nums = $sql->NumFields();

							$temp_array = array();

							for ($i = 0; $i < $nums; $i++)
							{
								$temp_array[] = $sql->FieldName($i);
							}

							$table_list = '(`' . implode('`, `', $temp_array) . '`)';
						}

						$temp_array = array();

						for ($i=0; $i<$nums; $i++)
						{
							if (! isset($row[$i]))
							{
								$temp_array[] = 'NULL';
							}
							elseif ($row[$i] != '')
							{
								$temp_array[] = "'" . str_replace($search, $replace, addslashes($row[$i])) . "'";
							}
							else
							{
								$temp_array[] = "''";
							}
						}

						// Сохряняем INSERT запросы
						$this->_database_dump .= 'INSERT INTO `' . (! DB_EXPORT_PREFIX ? $table_export : $table) . '` ' . $table_list . ' VALUES (' . implode(', ', $temp_array) . ");" . $this->_delimiter . "\n";
					}

					$this->_database_dump .= "\n";

					$sql->Close();
				}
			}

			return ! empty($this->_database_dump);
		}


		/**
		 * Метод, предназначенный для формирования файла дампа базы данных
		 *
		 * @return boolean
		 */
		function _databaseTopDumpCreate()
		{
			global $AVE_DB;

			$dbtables  = array();

			$sql = $AVE_DB->Query("SHOW TABLES LIKE '" . PREFIX . "_%'");

			while ($row = $sql->FetchArray())
			{
				array_push($dbtables, $row[0]);
			}

			$search  = array("\x00", "\x0a", "\x0d", "\x1a");
			$replace = array('\0', '\n', '\r', '\Z');

			$this->_database_dump = '';

			// Циклически обрабатываем каждую таблицу
			foreach ($dbtables as $table)
			{
				if (! DB_EXPORT_PREFIX)
					$table_export = preg_replace('/^' . PREFIX . '/', '%%PRFX%%', $table);

				// Если таблица имеет корректный префикс
				if (preg_match('/^' . preg_quote(PREFIX) . '_/', $table))
				{
					$row = $AVE_DB->Query("SHOW CREATE TABLE " . $table)->FetchArray();

					// Сохраняем CREATE и DROP запросы
					$this->_database_dump .= "DROP TABLE IF EXISTS `" . (! DB_EXPORT_PREFIX ? $table_export : $table) . "`;" . $this->_delimiter . "\n";

					if (! DB_EXPORT_PREFIX)
						$this->_database_dump .= str_replace('CREATE TABLE `' . PREFIX . '_', 'CREATE TABLE `%%PRFX%%_', $row[1]) . ";" . $this->_delimiter . "\n\n";
					else
						$this->_database_dump .= $row[1] . ";" . $this->_delimiter . "\n\n";

					$nums = 0;

					// Получаем данные, которые в дальнейшем будут вставлены в INSERT запросы.
					$sql = $AVE_DB->Query('SELECT * FROM `' . $table . '`');

					while ($row = $sql->FetchArray())
					{
						if ($nums==0)
						{
							$nums = $sql->NumFields();

							$temp_array = array();
							for ($i=0; $i<$nums; $i++)
							{
								$temp_array[] = $sql->FieldName($i);
							}
							$table_list = '(`' . implode('`, `', $temp_array) . '`)';
						}

						$temp_array = array();

						for ($i=0; $i<$nums; $i++)
						{
							if (!isset($row[$i]))
							{
								$temp_array[] = 'NULL';
							}
							elseif ($row[$i] != '')
							{
								$temp_array[] = "'" . str_replace($search, $replace, addslashes($row[$i])) . "'";
							}
							else
							{
								$temp_array[] = "''";
							}
						}

						// Сохряняем INSERT запросы
						$this->_database_dump .= 'INSERT INTO `' . (! DB_EXPORT_PREFIX ? $table_export : $table) . '` ' . $table_list . ' VALUES (' . implode(', ', $temp_array) . ");" . $this->_delimiter . "\n";
					}

					$this->_database_dump .= "\n";

					$sql->Close();
				}
			}

			return ! empty($this->_database_dump);
		}


	/**
	 *	Внешние методы класса
	 */

		/**
		 * Метод, предназначенный для сохранения файла дампа базы данных на жеский диск
		 *
		 */
		function databaseDumpExport($top = 0, $exit = 0)
		{
			global $AVE_Template;

			// Если дамп не удалось создать, тогда завершаем работу
			if ($top)
			{
				if (! $this->_databaseTopDumpCreate())
					exit;
			}
			else
			{
				if (! $this->_databaseDumpCreate())
					exit;
			}

			// Готовим шаблон имени файла
			if (isset($_REQUEST['file_name']) AND $_REQUEST['file_name'] != '')
				$file_name = prepare_fname($_REQUEST['file_name']);
			else
				$file_name = preg_replace_ru(array("/%SERVER%/", "/%DATE%/", "/%TIME%/"), array($_SERVER['SERVER_NAME'], date('d.m.y'), date('H.i.s')), DB_EXPORT_TPL);

			$dump = (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ
				? gzencode($this->_database_dump)
				: $this->_database_dump);

			if (isset($_REQUEST['server']) && $_REQUEST['server'] == 1)
			{
				if (! is_dir(BASE_DIR . '/tmp/backup/'))
				{
					@mkdir(BASE_DIR . '/tmp/backup/', 0777, true);
					write_htaccess_deny(BASE_DIR . '/tmp/backup/');
				}

				@file_put_contents(BASE_DIR . '/tmp/backup/'. $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : ''), $dump);

				@chmod(BASE_DIR . '/tmp/backup/'. $file_name . '.sql', 0777);

				if (! $exit)
					header('Location:index.php?do=dbsettings&cp=' . SESSION);
				else
					return BASE_DIR . '/tmp/backup/'. $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : '');
			}
			else
			{
				// Формируем заголовок
				header('Content-Type: text/plain');
				header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
				header('Content-Disposition: attachment; filename=' . $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : ''));
				header('Content-Length: ' . strlen($dump));
				header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
				header('Pragma: public');

				// Выводим данные
				echo $dump;

				$this->_database_dump = '';
			}

			// Выполняем запись системного сообщения в журнал
			reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP'));
			exit;
		}


		/**
		 * Метод, предназначенный для сохранения файла дампа базы данных на жеский диск
		 *
		 */
		function databaseDumpFileSave($file = '')
		{
			global $AVE_Template;

			$file = BASE_DIR . '/tmp/backup/'. $file;

			// Если дамп не удалось создать, тогда завершаем работу
			if (! is_file($file))
				return false;

				header('Content-Description: File Transfer');
				header('Content-Type: application/octet-stream');
				header('Content-Disposition: attachment; filename=' . basename($file));
				header('Content-Transfer-Encoding: binary');
				header('Expires: 0');
				header('Cache-Control: must-revalidate');
				header('Pragma: public');
				header('Content-Length: ' . filesize($file));

				ob_clean();

				flush();

				readfile($file);

			exit;
		}


		/**
		 * Метод, предназначенный для восстановления базы данных из дампа
		 *
		 * @param string $tempdir путь к папке в которую загружается файл дампа
		 */
		function databaseDumpImport($tempdir)
		{
			global $AVE_DB, $AVE_Template;

			$insert = false;

			// Если файл не пустой
			if ($_FILES['file']['size'] != 0)
			{
				// Получаем имя файла и его расширение (должно быть sql)
				$fupload_name = $_FILES['file']['name'];
				$gz = substr($fupload_name, -3) == '.gz';
				$end = substr($fupload_name, -3);

				// Если расширение sql, тогда
				if ($gz || $end == 'sql')
				{
					// Если файл не удалось загрузить, формируем сообщение с ошибкой
					if (! @move_uploaded_file($_FILES['file']['tmp_name'], $tempdir . $fupload_name))
						die('Ошибка при загрузке файла!');

					// Устанавливаем права чтения, записи, выполнения на файл
					@chmod($fupload_name, 0777);

					// Определяем флаг готовности к записи данных в БД
					$insert = true;
				}
				else
				{
					// В противном случае, если расширение файла НЕ sql, формируем сообщение с ошибкой
					$AVE_Template->assign('msg', '<li class="highlight red"><strong>Ошибка:</strong> ' . $AVE_Template->get_config_vars('MAIN_SQL_FILE_ERROR') . '</li>');
				}
			}

			// Если флаг готовности записи установлен, тогда
			if ($insert)
			{
				// Еще раз провреяем наличие загруженного файла
				if ($fupload_name != '' && file_exists($tempdir . $fupload_name))
				{
					// Читаем данные из файла
					$handle = @fopen($tempdir . $fupload_name, 'r');

					$db_q = @fread($handle, filesize($tempdir . $fupload_name));

					fclose($handle);

					if ($gz)
						$db_q = gzdecode($db_q);

					$m_ok = 0;

					$m_fail = 0;

					// Формируем массив запросов ориентируясь по разделителю указанному в свойстве _delimiter
					$querys = @explode($this->_delimiter, $db_q);

					// Циклически обрабатываем массив, выполняя каждый запрос
					foreach ($querys as $val)
					{
						if (chop($val) != '')
						{
							$q = str_replace("\n",'',$val);

							$q = $q . ';';

							if ($AVE_DB->Query($q))
							{
								$m_ok++;
							}
							else
							{
								$m_fail++;
							}
						}
					}

					// Удаляем файл дампа
					@unlink($tempdir . $fupload_name);

					// Формируем сопроводительные сообщения
					$msg = '<li class="highlight green"><strong>' . $AVE_Template->get_config_vars('MAIN_RESTORE_OK') . '</strong><br /><br />'
						. $AVE_Template->get_config_vars('MAIN_TABLE_SUCC')
						. '<strong>' . $m_ok . '</strong><br/> '
						. $AVE_Template->get_config_vars('MAIN_TABLE_ERROR')
						. '<strong><span style="color:red">' . $m_fail . '</span></strong></li>';

					$AVE_Template->assign('msg', $msg);
				}
				else // В противном случае, если файл не найден, формируем сообщение с ошибкой
				{
					$AVE_Template->assign('msg', '<li class="highlight red">'.$AVE_Template->get_config_vars('DB_REPORT_DUMP_ER').'</li>');
				}
			}

			// Выполняем запись системного сообщения в журнал
			reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_RECOVER'));
		}

		/**
		 * Метод, предназначенный для удаления файла дампа на сервере
		 *
		 * @param string $file путь к файлу дампа
		 */
		function databaseDumpFileDelete($file = '')
		{
			global $AVE_DB, $AVE_Template;

			$file = BASE_DIR . '/tmp/backup/'. $file;

			if (! is_file($file))
				return false;

			if (@unlink($file))
			{
				reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_DEL_OK') . ' ('.basename($file).')');
			}
			else
				{
					reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_DEL_ER') . ' ('.basename($file).')');
				}

			header('Location:index.php?do=dbsettings&cp=' . SESSION);
		}

		/**
		 * Метод, предназначенный для восстановления базы данных из дампа на сервере
		 *
		 * @param string $file путь к файлу дампа
		 */
		function databaseDumpFileImport($file = '')
		{
			global $AVE_DB, $AVE_Template;

			$insert = false;

			$file = BASE_DIR . '/tmp/backup/'. $file;

			// Если дамп не удалось создать, тогда завершаем работу
			if (! is_file($file)) $insert = false;

			// Если файл не пустой
			if (filesize($file) != 0)
			{
				// Получаем имя файла и его расширение (должно быть sql)
				$file_name = basename($file);
				$gz = substr($file_name, -3)=='.gz';
				$end = substr($file_name, -3);

				// Если расширение sql, тогда
				if ($gz || $end == 'sql')
				{
					// Определяем флаг готовности к записи данных в БД
					$insert = true;
				}
				else
					{
						// В противном случае, если расширение файла НЕ sql, формируем сообщение с ошибкой
						$AVE_Template->assign('msg', '<li class="highlight red"><strong>Ошибка:</strong> ' . $AVE_Template->get_config_vars('MAIN_SQL_FILE_ERROR') . '</li>');
					}
			}

			// Если флаг готовности записи установлен, тогда
			if ($insert)
			{
				// Еще раз провреяем наличие загруженного файла
				if ($file_name != '' && file_exists($file))
				{
					// Читаем данные из файла
					$handle = @fopen($file, 'r');

					$db_q = @fread($handle, filesize($file));

					fclose($handle);

					if($gz)$db_q=gzdecode($db_q);

					$m_ok = 0;

					$m_fail = 0;

					// Формируем массив запросов ориентируясь по разделителю указанному в свойстве _delimiter
					$querys = @explode($this->_delimiter, $db_q);

					// Циклически обрабатываем массив, выполняя каждый запрос
					foreach ($querys as $val)
					{
						if (chop($val) != '')
						{
							$q = str_replace("\n",'',$val);

							$q = $q . ';';

							@$q = str_replace('%%PRFX%%', PREFIX, $q);

							if ($AVE_DB->Query($q))
							{
								$m_ok++;
							}
							else
							{
								$m_fail++;
							}
						}
					}

					// Формируем сопроводительные сообщения
					$msg =  '<li class="highlight green"><strong>' . $AVE_Template->get_config_vars('MAIN_RESTORE_OK') . '</strong><br /><br />'
						. $AVE_Template->get_config_vars('MAIN_TABLE_SUCC')
						. '<strong>' . $m_ok . '</strong><br/> '
						. $AVE_Template->get_config_vars('MAIN_TABLE_ERROR')
						. '<strong><span style="color:red">' . $m_fail . '</span></strong></li>';

					$AVE_Template->assign('msg', $msg);
				}
				else // В противном случае, если файл не найден, формируем сообщение с ошибкой
				{
					$AVE_Template->assign('msg', '<li class="highlight red">'.$AVE_Template->get_config_vars('DB_REPORT_DUMP_ER').'</li>');
				}
			}

			// Выполняем запись системного сообщения в журнал
			reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_RECOVER') . ' ('.$file_name.')');
		}

		/**
		 * Метод, предназначенный для оптимизации таблиц базы данных
		 *
		 */
		function databaseTableOptimize()
		{
			global $AVE_DB, $AVE_Template;

			if (! empty($_POST['ta']) && is_array($_POST['ta']))
			{
				// Выполняем запрос на оптимизацию
				$AVE_DB->Query("OPTIMIZE TABLE `" . implode("`, `", $_POST['ta']) . "`");

				// Выполняем запись системного сообщения в журнал
				reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_OPTIM'));
			}
		}

		/**
		 * Метод, предназначенный для восстановления повреждённых таблиц базы данных
		 *
		 */
		function databaseTableRepair()
		{
			global $AVE_DB, $AVE_Template;

			if (! empty($_POST['ta']) && is_array($_POST['ta']))
			{
				// Выполняем запрос на восстановление
				$AVE_DB->Query("REPAIR TABLE `" . implode("`, `", $_POST['ta']) . "`");

				// Выполняем запись системного сообщения в журнал
				reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_TABLE'));
			}
		}

		/**
		 * Метод, предназначенный для формирования списка всех таблиц в БД
		 *
		 * @return string
		 */
		function databaseTableGet()
		{
			global $AVE_DB;

			$tables = '';

			// Получаем список всех таблиц, которые имею префикс, указанный в конфигурации системы
			$sql = $AVE_DB->Query("SHOW TABLES LIKE '" . PREFIX . "_%'");

			while ($row = $sql->FetchArray())
			{
				$tables .= '<option value="' . $row[0] . '" selected="selected">' . substr($row[0], 1+strlen(PREFIX)) . '</option>';
			}

			$sql->Close();

			// Возвращаем полученный список
			return $tables;
		}

		/**
		 * Метод, предназначенный для вывода всех sql файлов в папке backup
		 *
		 * @return string
		 */
		function databaseFilesGet()
		{
			$dir = BASE_DIR . '/tmp/backup/';

			if($handle = opendir($dir))
			{
				$files = array();

				while (false !== ($file = readdir($handle)))
				{
					if ($file != "." && $file != ".." && (substr($file, -3) == 'sql' || substr($file, -2) == 'gz'))
					{
						if(is_file($dir . '/' . $file))
						{
							$files[] = array(
								'name' => $file,
								'data' => (filectime($dir . '/' . $file)),
								'size' => (filesize($dir . '/' . $file))
							);
						}
					}
				}
				closedir($handle);
			}

			return msort($files, 'data', null, SORT_DESC);
		}
	}
?>