Файл: crons/dbbackup.php
Строк: 423
<?php
include("../config.php");
include("../autoconfig.php");
include("yandexdisk.php");
include("database.php");
if (!ini_get('zlib.output_compression') && function_exists('ob_gzhandler')) ob_start('ob_gzhandler');
if(STATUS_BUD)
{
$SDBU = new Santi_Dumper();
set_error_handler('sxd_error_handler');
chdir(dirname(__FILE__));
autopilots_add_time(7);
$archivename = $SDBU->init();
if(YADISK_BUD)
{
if(file_exists(SANTI_SERVERPATH."/".SANTI_PATH."/datas/backups/".$archivename))
send_to_yadisk($archivename);
unlink(SANTI_SERVERPATH."/".SANTI_PATH."/datas/backups/".$archivename);
}
}
else
{
die();
}
class Santi_Dumper {
function Santi_Dumper() {
define('TIMER', array_sum(explode(' ', microtime())));
define('V_SXD', 20010);
define('V_PHP', sxd_ver2int(phpversion()));
}
function init($args = false){
$CFG = array (
'charsets' => 'cp1251 utf8 latin1',
'lang' => 'ru',
'time_web' => '600',
'time_cron' => '600',
'backup_path' => '../datas/backups/',
'backup_url' => '../datas/backups/',
'only_create' => 'MRG_MyISAM MERGE HEAP MEMORY',
'globstat' => 0,
'my_host' => SANTI_DB_HOST,
'my_port' => 3306,
'my_user' => SANTI_DB_USER,
'my_pass' => SANTI_DB_PASSWORD,
'my_comp' => 0,
'my_db' => '',
'auth' => 'mysql cfg',
'user' => '',
'pass' => '',
'confirm' => '6',
'exitURL' => './',
);
$this->CFG = &$CFG;
$this->try = false;
$this->cron_mode = true;
set_time_limit(0);
$auth = $this->connect();
$this->ajax($this->loadJob());
if(file_exists($this->JOB['file_log'])) unlink($this->JOB['file_log']);
if(file_exists($this->JOB['file_rtl'])) unlink($this->JOB['file_rtl']);
return $this->JOB['file'];
}
function saveToFile($name, $content){
$fp = fopen($name, "w");
fwrite($fp, $content);
fclose($fp);
}
function connect($host = null, $port = null, $user = null, $pass = null){
$this->error = '';
$this->try = true;
if(!empty($user) && isset($pass)) {
$this->CFG['my_host'] = $host;
$this->CFG['my_port'] = $port;
$this->CFG['my_user'] = $user;
$this->CFG['my_pass'] = $pass;
}
if(mysql_connect($this->CFG['my_host'] . ($this->CFG['my_host']{0} != ':' ? ":{$this->CFG['my_port']}" : ''), $this->CFG['my_user'], $this->CFG['my_pass'])) {
if(V_PHP > 50202) mysql_set_charset('utf8') or sxd_my_error();
else mysql_query('SET NAMES utf8') or sxd_my_error();
define('V_MYSQL', sxd_ver2int(mysql_get_server_info()));
}
else {
define('V_MYSQL', 0);
$this->error = "sxd.actions.tab_connects();alert(" . sxd_esc(mysql_error()) . ");";
}
$this->try = false;
return V_MYSQL ? true: false;
}
function main(){
$this->db = 'temp';
$zip = array();
if (function_exists("gzopen")) {
for($i = 1; $i <10; $i++){
$zip[] = "GZip: {$i}";
}
}
if (function_exists("bzopen")) {
$zip[10] = "BZip";
}
end($zip);
//echo sxd_tpl_page();
}
function ajax($req){
$res = '';
$act = $req['act'];
if($req['act'] == 'run_savedjob'){
$req = $this->loadJob($req);
}
switch($req['act']){
case 'save_connect':
//$res = $this->saveConnect($req);
break;
case 'save_job':
unset($req['act']);
$this->saveJob('sj_' . $req['job'] , $req);
$res = $this->getSavedJobs();
break;
case 'backup':
$this->addBackupJob($req);
break;
case 'restore':
//$this->addRestoreJob($req);
break;
case 'exit':
setcookie('sxd', '', 0);
$res = "top.location.href = " . sxd_esc($this->CFG['exitURL']) . ";";
break;
}
echo $res;
}
function loadJob(){
$charset = '0';
if(SANTI_DB_CHARSET == '0')
$charset = '0';
elseif(SANTI_DB_CHARSET == '1')
$charset = 'utf8';
elseif(SANTI_DB_CHARSET == '2')
$charset = 'cp1251';
elseif(SANTI_DB_CHARSET == '3')
$charset = 'koi8';
$JOB = array (
'type' => 'backup',
'db' => SANTI_DB_NAME,
'charset' => $charset,
'zip' => '7',
'comment' => '',
'del_time' => '',
'del_count' => '',
'obj' =>
array (
'TA' =>
array (
0 => '*',
),
),
'job' => 'dbbackup',
'title' => '',
);
$JOB['act'] = $JOB['type'];
$JOB['type'] = 'run';
return $JOB;
}
function cfg2js($cfg){
foreach($cfg AS $k => $v){
$cfg[$k] = sxd_esc($v, false);
}
return $cfg;
}
function addDb($req){
$r = mysql_query('CREATE DATABASE `' . sxd_esc($req['name'], false) . '`' . (V_MYSQL > 40100 ? "CHARACTER SET {$req['charset']} COLLATE {$req['collate']}" : ''));
if($r)
echo "sxd.addOpt(" . sxd_php2json(array('db' => array($req['name'] => "{$req['name']} (0)"))) . ");";
else
sxd_my_error();
}
function createFilters(&$obj, &$filter, &$object){
$types = array('TA', 'TC', 'VI', 'PR', 'FU', 'TR', 'EV');
foreach($types AS $type){
$filter[$type] = array();
$object[$type] = array();
if(!empty($obj[$type])){
foreach($obj[$type] AS $v){
if(strpos($v, '*') !== false) {
$filter[$type][] = str_replace('*', '.*?', $v);
}
else {
$object[$type][$v] = true;
}
}
$filter[$type] = count($filter[$type]) > 0 ? '/^(' . implode('|', $filter[$type]) . ')$/i' : '';
}
}
}
function closeConnect(){
@ignore_user_abort(1);
header("Connection: close");
header("Content-Length: 0");
@ob_end_flush();
@flush();
}
function addBackupJob($job) {
$this->closeConnect();
// Создаем новое задание
$this->JOB = $job;
mysql_select_db($this->JOB['db']);
// Создаем список объектов и фильтр
$filter = $object = array();
$this->createFilters($this->JOB['obj'], $filter, $object);
$queries = array(
array('TABLE STATUS', 'Name', 'TA')
);
if (V_MYSQL > 50014) {
$queries[] = array("PROCEDURE STATUS WHERE db='{$this->JOB['db']}'", 'Name', 'PR');
$queries[] = array("FUNCTION STATUS WHERE db='{$this->JOB['db']}'", 'Name', 'FU');
$queries[] = array('TRIGGERS', 'Trigger', 'TR');
if(V_MYSQL > 50100) $queries[] = array('EVENTS', 'Name', 'EV');
}
$todo = $header = array();
$tabs = $rows = 0;
$only_create = explode(' ', $this->CFG['only_create']);
foreach($queries AS $query){
$t = $query[2];
if($t == 'TA' && (!empty($object['TC']) || !empty($filter['TC']))) {}
elseif(empty($object[$t]) && empty($filter[$t])) continue;
$r = mysql_query('SHOW ' . $query[0]) or sxd_my_error();
if (!$r) continue;
$todo[$t] = array();
$header[$t] = array();
while($item = mysql_fetch_assoc($r)){
$n = $item[$query[1]];
switch($t){
case 'TA':
case 'TC':
if(V_MYSQL > 40101 && is_null($item['Engine']) && preg_match('/^VIEW/i', $item['Comment'])) {
if(sxd_check($n, $object['VI'], $filter['VI'])){
$todo['VI'] = array();
$header['VI']= array();
}
continue;
}
elseif(sxd_check($n, $object['TA'], $filter['TA'])){
$engine = V_MYSQL > 40101 ? $item['Engine'] : $item['Type'];
$t = in_array($engine, $only_create) ? 'TC' : 'TA';
}
elseif(sxd_check($n, $object['TC'], $filter['TC'])) {
$t = 'TC';
$item['Rows'] = $item['Data_length'] = '';
}
else continue;
$todo['TA'][] = array($t, $n, !empty($item['Collation']) ? $item['Collation'] : '', $item['Auto_increment'], $item['Rows'], $item['Data_length']);
$header['TA'][] = "{$n}`{$item['Rows']}`{$item['Data_length']}";
$tabs++;
$rows += $item['Rows'];
break;
default:
if(sxd_check($n, $object[$t], $filter[$t])) {
$todo[$t][] = array($t, $n, !empty($item['collation_connection']) ? $item['collation_connection'] : '');
$header[$t][] = $n;
}
}
}
}
if (V_MYSQL > 50014 && (!empty($object['VI']) || !empty($filter['VI']))) {
// Бэкап обзоров, нужно отсортировать зависимые
$r = mysql_query("SELECT table_name, view_definition /*!50121 , collation_connection */ FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '{$this->JOB['db']}'") or sxd_my_error();
$views = $dumped = $views_collation = array();
$re = "/`{$this->JOB['db']}`.`(.+?)`/";
while($item = mysql_fetch_assoc($r)){
preg_match_all($re, preg_replace("/^select.+? from/i", '', $item['view_definition']), $m);
$used = $m[1];
$views_collation[$item['table_name']] = !empty($item['collation_connection']) ? $item['collation_connection'] : '';
$views[$item['table_name']] = $used;
}
while (count($views) > 0) {
foreach($views AS $n => $view) {
$can_dumped = true;
foreach($view AS $k) {
if (isset($views[$k]) && !isset($dumped[$k])) $can_dumped = false;
}
if ($can_dumped) {
if(sxd_check($n, $object['VI'], $filter['VI'])){
$todo['VI'][] = array('VI', $n, $views_collation[$n]);
$header['VI'][] = $n;
}
$dumped[$n] = 1;
unset($views[$n]);
}
}
}
unset($dumped);
unset($views);
unset($views_collation);
}
$this->JOB['file_tmp'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.tmp';
$this->JOB['file_rtl'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.rtl';
$this->JOB['file_log'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.log';
$this->JOB['file_stp'] = $this->CFG['backup_path'] . $this->JOB['job'] . '.stp';
if(file_exists($this->JOB['file_stp'])) unlink($this->JOB['file_stp']);
$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'w');
$this->JOB['file'] = sprintf('%s_%s.%s', (isset($this->JOB['title']) ? $this->JOB['job'] : $this->JOB['db']), date('Y-m-d_H-i-s'), $this->JOB['file_ext']);
$this->JOB['file_name'] = $this->CFG['backup_path'] . $this->JOB['file'];
$this->JOB['todo'] = $todo;
$this->saveJob($this->JOB['job'], $this->JOB);
$fcache = implode('|', array('#SXD20', V_SXD, V_MYSQL, V_PHP, date('Y.m.d H:i:s'), $this->JOB['db'], $this->JOB['charset'], $tabs, $rows, sxd_esc($this->JOB['comment'], false))) . "n";
foreach($header AS $t => $o){
if (!empty($o)) $fcache .= "#{$t} " . implode('|', $o) . "n";
}
$this->fh_rtl = fopen($this->JOB['file_rtl'], 'wb');
$this->fh_log = fopen($this->JOB['file_log'], 'wb');
$this->rtl = array(time(), time(), $rows, 0, '', '', '', 0, 0, 0, 0, TIMER, "n");
$fcache .= "#EOHnn";
$this->write($fcache);
$this->runBackupJob();
}
function runBackupJob($continue = false){
if($continue){
$this->fh_tmp = $this->openFile($this->JOB['file_tmp'], 'a');
mysql_select_db($this->JOB['db']);
}
mysql_query("SET SQL_QUOTE_SHOW_CREATE = 1");
$types = array('VI' => 'View', 'PR' => 'Procedure', 'FU' => 'Function', 'TR' => 'Trigger', 'EV' => 'Event');
$fcache = '';
$writes = 0;
if(V_MYSQL > 40101) mysql_query("SET SESSION character_set_results = '" . ($this->JOB['charset'] ? $this->JOB['charset'] : 'binary') ."'") or sxd_my_error();
$time_old = time();
$exit_time = $time_old + $this->CFG['time_web'] - 1;
$no_cache = V_MYSQL < 40101 ? 'SQL_NO_CACHE ' : '';
foreach($this->JOB['todo'] AS $t => $o){
if (empty($this->rtl[4])) $this->rtl[4] = $t;
elseif ($this->rtl[4] != $t) continue;
foreach($o AS $n){
if (empty($this->rtl[5])) {
$this->rtl[5] = $n[1];
$this->rtl[7] = 0;
$this->rtl[8] = !empty($n[4]) ? $n[4] : 0;
}
elseif ($this->rtl[5] != $n[1]) continue;
// Делаем бэкап
switch($n[0]){
case 'TC':
case 'TD':
case 'TA':
$from = '';
if ($n[0] == 'TC' || $this->rtl[7] == 0){
// Бэкап структуры таблицы
$r = mysql_query("SHOW CREATE TABLE `{$n[1]}`") or sxd_my_error();
$item = mysql_fetch_assoc($r);
$fcache .= "#tTC`{$n[1]}`{$n[2]}t;n{$item['Create Table']}t;n";
$this->rtl[7] = 0;
if($n[0] == 'TC' || !$n[4]) break;
// Бэкапим данные таблицы
$fcache .= "#tTD`{$n[1]}`{$n[2]}t;nINSERT INTO `{$n[1]}` VALUES n";
}
else {
$from = " LIMIT {$this->rtl[7]}, {$this->rtl[8]}";
}
// Определяем типы полей
$notNum = array();
$r = mysql_query("SHOW COLUMNS FROM `{$n[1]}`") or sxd_my_error();
$fields = 0;
while($col = mysql_fetch_array($r)) {
// TODO: проверить типы SET, ENUM и BIT
$notNum[$fields] = preg_match("/^(tinyint|smallint|mediumint|bigint|int|float|double|real|decimal|numeric|year)/", $col['Type']) ? 0 : 1;
$fields++;
}
$time_old = time();
$z = 0;
// Достаем данные
$r = mysql_unbuffered_query("SELECT {$no_cache}* FROM `{$n[1]}`{$from}");
while($row = mysql_fetch_row($r)) {
if (strlen($fcache) >= 61440) {
$z = 0;
if($time_old < time()) {
if(file_exists($this->JOB['file_stp'])){
$type = file_get_contents($this->JOB['file_stp']);
$this->rtl[9] = !empty($type) ? $type : 2;
$this->write($fcache);
if($type == 1) {
}
unset($this->rtl);
exit;
}
$time_old = time();
if($time_old >= $exit_time){
$this->rtl[9] = 3;
$this->write($fcache);
unset($this->rtl);
exit;
}
clearstatcache();
}
$this->write($fcache);
}
for($k = 0; $k < $fields; $k++){
if(!isset($row[$k])) {$row[$k] = 'N';}
elseif($notNum[$k]) {$row[$k] = ''' . mysql_real_escape_string($row[$k]) . ''';} // TODO: Потестить скорость эскэйпинга строк
}
$fcache .= '(' . implode(',', $row) . "),n";
$this->rtl[7]++;
$this->rtl[10]++;
}
unset($row);
mysql_free_result($r);
$fcache = substr_replace($fcache, "t;n", -2, 2);
break;
default:
if(V_MYSQL < 50121 && $n[0] == 'TR'){
// SHOW CREATE TRIGGER отсутствует до MySQL 5.1.21
$r = mysql_query("SELECT * FROM `INFORMATION_SCHEMA`.`TRIGGERS` WHERE `TRIGGER_SCHEMA` = '{$this->JOB['db']}' AND `TRIGGER_NAME` = '{$n[1]}'") or sxd_my_error();
$item = mysql_fetch_assoc($r);
$fcache .= "#tTR`{$n[1]}`{$n[2]}t;nCREATE TRIGGER `{$item['TRIGGER_NAME']}` {$item['ACTION_TIMING']} {$item['EVENT_MANIPULATION']} ON `{$item['EVENT_OBJECT_TABLE']}` FOR EACH ROW {$item['ACTION_STATEMENT']}t;n";
}
else {
$r = mysql_query("SHOW CREATE {$types[$n[0]]} `{$n[1]}`") or sxd_my_error();
$item = mysql_fetch_assoc($r);
$fcache .= "#t{$n[0]}`{$n[1]}`{$n[2]}t;n" . preg_replace("/DEFINER=`.+?`@`.+?` /", '', ($n[0] == 'TR' ? $item['SQL Original Statement'] : $item['Create ' . $types[$n[0]]])) . "t;n";
}
}
$this->rtl[5] = '';
}
$this->rtl[4] = '';
}
$this->rtl[4] = 'EOJ';
$this->rtl[5] = round(array_sum(explode(' ', microtime())) - $this->rtl[11], 4);
$this->rtl[6] = '';
$this->rtl[7] = 0;
$this->rtl[8] = 0;
$this->write($fcache);
fclose($this->fh_tmp);
rename($this->JOB['file_tmp'], $this->JOB['file_name']);
if ($this->JOB['del_time'] || $this->JOB['del_count']) {
$deldate = '';
if (!empty($this->JOB['del_time'])){ // Удаление по дням
$deldate = date("Y-m-d_H-i-s", time() - intval($this->JOB['del_time']) * 86400);
}
$deleted = false;
if ($dh = opendir($this->CFG['backup_path'])) {
$files = array();
$name = isset($this->JOB['title']) ? $this->JOB['job'] : $this->JOB['db'];
while (false !== ($file = readdir($dh))) {
if (preg_match("/^{$name}_(d{4}-d{2}-d{2}_d{2}-d{2}-d{2}).sql/", $file, $m)) {
if ($deldate && $m[1] < $deldate) {
$deleted = true;
}
else {$files[$m[1]] = $file;}
}
}
closedir($dh);
// Сортируем файлы по дате и удаляем самые старые
if (!empty($this->JOB['del_count'])){
ksort($files);
$file_to_delete = count($files) - $this->JOB['del_count'];
foreach ($files AS $file){
if ($file_to_delete-- > 0){
$deleted = true;
}
}
}
}
}
fclose($this->fh_log);
fclose($this->fh_rtl);
}
function write(&$str){
fseek($this->fh_rtl, 0);
$this->rtl[1] = time();
$this->rtl[3] += fwrite($this->fh_tmp, $str);
fwrite($this->fh_rtl, implode("t", $this->rtl));
$str = '';
}
function getSavedJobs(){
$sj = array('sj_backup' => array(), 'sj_restore' => array(),);
if (is_dir($this->CFG['backup_path']) && false !== ($handle = opendir($this->CFG['backup_path']))) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/^sj_(.+?).job.php$/", $file)) {
include($this->CFG['backup_path'] . $file);
$sj['sj_' . $JOB['type']][$JOB['job']] = "<b>{$JOB['job']}</b><br><i>{$JOB['title']} </i>";
}
}
closedir($handle);
}
if(count($sj['sj_backup']) > 0){
ksort($sj['sj_backup']);
}
else {
$sj['sj_backup'] = array();
}
if(count($sj['sj_restore']) > 0){
ksort($sj['sj_restore']);
}
else {
$sj['sj_restore'] = array();
}
return "sxd.clearOpt('sj_backup');sxd.clearOpt('sj_restore');sxd.addOpt(" . sxd_php2json($sj) . ");";
}
function saveJob($job, $config){
$this->saveToFile($this->CFG['backup_path'] . $job . '.job.php', "<?phpn$JOB = " . var_export($config, true) . ";n" . "?>");
}
function openFile($name, $mode){
if($mode == 'r') {
if(preg_match('/.(sql|sql.bz2|sql.gz)$/i', $name, $m)) $this->JOB['file_ext'] = strtolower($m[1]);
}
else{
switch($this->JOB['zip']) {
case 0 : $this->JOB['file_ext'] = 'sql'; break;
case 10: $this->JOB['file_ext'] = 'sql.bz2'; break;
default: $this->JOB['file_ext'] = 'sql.gz'; break;
}
}
switch ($this->JOB['file_ext']){
case 'sql':
return fopen($name, "{$mode}b");
break;
case 'sql.bz2':
return bzopen($name, $mode);
break;
case 'sql.gz':
return gzopen($name, $mode . ($mode == 'w' ? $this->JOB['zip'] : ''));
break;
default: return false;
}
}
}
function sxd_read_sql($f, &$seek, $ei, $delimiter = "t;", $eol = "n"){
static $l = '';
static $r = 0;
$fs = ftell($f);
$delim_len = strlen($delimiter . $eol);
while($r || $s = fread($f, 61440)){
if(!$r) $l .= $s;
$pos = strpos($l, $delimiter . $eol);
if ($pos !== false) {
// Есть окончание запроса
$q = substr($l, 0, $pos);
$l = substr($l, $pos+$delim_len);
$r = 1;
$seek = strlen($l);
return $q;
}
if($ei) {
$pos = strrpos($l, $eol);
if($pos > 0 && $l{$pos-1} === ',') {
// Окончание не найдено
$q = substr($l, 0, $pos-1);
$l = substr($l, $pos+ strlen($eol));
$seek = strlen($l);
$r = 0;
return $q;
}
}
$r = 0;
}
if (!empty($l)) {
return $l;
}
return false;
}
function sxd_error_handler($errno, $errmsg, $filename, $linenum, $vars){
global $SXD;
if($SXD->try) return;
if($errno == 8192) return;
if(strpos($errmsg, 'timezone settings')) return;
$errortype = array(1 => 'Error', 2 => 'Warning', 4 => 'Parsing Error', 8 => 'Notice', 16 => 'Core Error', 32 => 'Core Warning', 64 => 'Compile Error',
128 => 'Compile Warning', 256 => 'MySQL Error', 512 => 'Warning', 1024 => 'Notice',
2048 => 'Strict', 8192 => 'Deprecated', 16384 => 'Deprecated');
$str = sxd_esc("{$errortype[$errno]}: {$errmsg} ({$filename}:{$linenum})", false);
if(SXD_DEBUG) error_log("[index.php]n{$str}n", 3, "backup/error.log");
if($errno == 8 || $errno == 1024) {
if (!$SXD->fh_log && !$SXD->fh_rtl) echo isset($_POST['ajax']) ? "alert('" . ($str) . "');" : $str;
else {
fwrite($SXD->fh_log, date('Y.m.d H:i:s') . "t3t{$str}n");
}
}
elseif($errno < 1024) {
$SXD->error = true;
if (!$SXD->fh_log && !$SXD->fh_rtl) echo isset($_POST['ajax']) ? "alert('" . ($str) . "');" : $str;
else {
$SXD->rtl[1] = time();
$SXD->rtl[9] = 5;
fseek($SXD->fh_rtl, 0);
fwrite($SXD->fh_rtl, implode("t", $SXD->rtl));
fwrite($SXD->fh_log, date('Y.m.d H:i:s') . "t4t{$str}n");
unset($SXD->rtl);
}
die;
}
}
function sxd_check($n, $obj, $filt){
return isset($obj[$n]) || ($filt && preg_match($filt, $n));
}
function sxd_ver2int($ver){
return preg_match("/^(d+).(d+).(d+)/", $ver, $m) ? sprintf("%d%02d%02d", $m[1], $m[2], $m[3]) : 0;
}
function sxd_esc($str, $quoted = true){
return $quoted ? "'" . addcslashes($str, "\ nrt'") . "'" : addcslashes($str, "\ nrt'");
}
function sxd_my_error(){
trigger_error(mysql_error(), E_USER_ERROR);
}
function sxd_antimagic($arr){
return is_array($arr) ? array_map('sxd_antimagic', $arr) : stripslashes($arr);
}
?>