|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @author Victor
|
|
|
|
|
*/
|
|
|
|
|
abstract class AbstractBackup {
|
|
|
|
|
|
|
|
|
|
public static $BACKUP_FINALIZADO = "Backup finalizado";
|
|
|
|
|
protected $exceptionMessage = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @var BackupInfo
|
|
|
|
|
*/
|
|
|
|
|
protected $backup;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var resource
|
|
|
|
|
*/
|
|
|
|
|
protected $_handler;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Conecta ao servidor de destino do backup
|
|
|
|
|
*
|
|
|
|
|
* @param BackupInfo $backup
|
|
|
|
|
*/
|
|
|
|
|
abstract function Connect(BackupInfo $backup);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cria um diret<EFBFBD>rio/pasta
|
|
|
|
|
*
|
|
|
|
|
* @param string $dir
|
|
|
|
|
*/
|
|
|
|
|
abstract function Mkdir($dir);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Acessa um diret<EFBFBD>rio
|
|
|
|
|
*
|
|
|
|
|
* @param string $dir
|
|
|
|
|
*/
|
|
|
|
|
abstract function Chdir($dir);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Grava a mensagem de erro
|
|
|
|
|
*
|
|
|
|
|
* @param string $msg
|
|
|
|
|
*/
|
|
|
|
|
public final function Except($msg) {
|
|
|
|
|
$this->backup->hasError = true;
|
|
|
|
|
$this->backup->Except($msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Faz o upload de v<EFBFBD>rios arquivos
|
|
|
|
|
*
|
|
|
|
|
* @param array $fileList
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public final function UploadList($fileList) {
|
|
|
|
|
foreach ($fileList as $remoteFile => $localFile) {
|
|
|
|
|
if ($this->Upload($localFile, $remoteFile)) {
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel gravar o arquivo remoto!");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Envia o(s) arquivo(s) para o destino
|
|
|
|
|
*
|
|
|
|
|
* @param array $dadosArquivo
|
|
|
|
|
* @param string $remoteFile
|
|
|
|
|
*
|
|
|
|
|
* @return bool true ou false se conseguiu ou n<EFBFBD>o enviar o arquivo ao host destino
|
|
|
|
|
*/
|
|
|
|
|
abstract function Upload(array $dadosArquivo, $remoteFile);
|
|
|
|
|
|
|
|
|
|
protected final function Error($msg) {
|
|
|
|
|
$this->backup->hasError = true;
|
|
|
|
|
BackupConfig::log("Erro [{$this->TipoBackup()}]: {$msg}\n", false);
|
|
|
|
|
$this->exceptionMessage = $msg;
|
|
|
|
|
$this->backup->Except($msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Retorna o tipo de backup que est<EFBFBD> sendo efetuado
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
protected final function TipoBackup() {
|
|
|
|
|
$className = get_called_class();
|
|
|
|
|
$classNameSize = strlen($className);
|
|
|
|
|
$tipoBackup = substr($className, 6, $classNameSize);
|
|
|
|
|
|
|
|
|
|
return $tipoBackup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public final function Pwd($realPath = 1) {
|
|
|
|
|
if ($realPath) {
|
|
|
|
|
return $this->backup->dirAtual;
|
|
|
|
|
}
|
|
|
|
|
if ($this->backup->proto == PROTO_REDE) {
|
|
|
|
|
return $this->backup->VerBarraPath($this->backup->host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->backup->dirAtual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Retorna a <EFBFBD>ltima mensagem de erro lan<EFBFBD>ada pelo gerenciador do backup
|
|
|
|
|
*
|
|
|
|
|
* @return string|null
|
|
|
|
|
*/
|
|
|
|
|
public function getExceptionMessage() {
|
|
|
|
|
return $this->exceptionMessage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checa a integridade do arquivo (no host do backup) ap<EFBFBD>s o upload
|
|
|
|
|
*
|
|
|
|
|
* @param string $hash hash do arquivo original
|
|
|
|
|
*
|
|
|
|
|
* @param string $remotePathFile path do arquivo no host do backup
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected function CheckHash($hash, $remotePathFile) {
|
|
|
|
|
$hashArquivoRemoto = null;
|
|
|
|
|
|
|
|
|
|
$this->Debug("Verificando integridade do arquivo remoto...");
|
|
|
|
|
$tipoBackup = strtolower($this->TipoBackup());
|
|
|
|
|
$hashCmd = "sha1sum {$remotePathFile}";
|
|
|
|
|
switch ($tipoBackup) {
|
|
|
|
|
case 'sftp':
|
|
|
|
|
case 'usb':
|
|
|
|
|
$stream = ssh2_exec($this->_handler, $hashCmd . " 2>&1");
|
|
|
|
|
if (!$stream) {
|
|
|
|
|
$this->backup->hasError = true;
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel verificar a integridade do arquivo {$remotePathFile}!");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
|
|
|
|
stream_set_blocking($stream, true);
|
|
|
|
|
$output = stream_get_contents($stream);
|
|
|
|
|
if (!empty($output)) {
|
|
|
|
|
$hashArquivoRemoto = substr($output, 0, 40);
|
|
|
|
|
$this->Debug(str_replace("\n", "", $output));
|
|
|
|
|
}
|
|
|
|
|
$error = stream_get_contents($errorStream);
|
|
|
|
|
if (!empty($error)) {
|
|
|
|
|
$this->Error($error);
|
|
|
|
|
fclose($stream);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'ftp':
|
|
|
|
|
$hashArquivoRemoto = $hash;
|
|
|
|
|
break;
|
|
|
|
|
default: // local e rede
|
|
|
|
|
$hashArquivoRemoto = hash_file('sha1', $remotePathFile);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$sameHash = ($hash === $hashArquivoRemoto);
|
|
|
|
|
|
|
|
|
|
if (!$sameHash) {
|
|
|
|
|
$this->Error("A integridade do arquivo no destino do backup est<EFBFBD> corrompida");
|
|
|
|
|
} else {
|
|
|
|
|
$this->Debug("Integridade validada, o arquivo est<EFBFBD> ok.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $sameHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected final function Debug($msg) {
|
|
|
|
|
BackupConfig::log("Debug [{$this->TipoBackup()}]: {$msg}\n", false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Acessa um diret<EFBFBD>rio
|
|
|
|
|
*
|
|
|
|
|
* @param string $dir
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected final function ChDirLocalNetwork($dir) {
|
|
|
|
|
$result = false;
|
|
|
|
|
$cmd = array();
|
|
|
|
|
$command = sprintf("cd %s", $dir);
|
|
|
|
|
$sshConectado = $this->ConectaSSH();
|
|
|
|
|
if (!$sshConectado) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
exec($command, $cmd, $result);
|
|
|
|
|
if (!file_exists($dir)) {
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel acesar o diret<EFBFBD>rio destino!");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->backup->dirAtual = $this->backup->VerBarraPath($dir);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Abre uma conex<EFBFBD>o SSH com o host remoto
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected function ConectaSSH() {
|
|
|
|
|
$this->Debug("Abrindo conex<EFBFBD>o com o host {$this->GetInfo('host')}");
|
|
|
|
|
$this->_handler = ssh2_connect($this->GetInfo('host'), $this->GetInfo('port'));
|
|
|
|
|
if (!$this->_handler) {
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel se conectar ao servidor!");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->Debug("Conex<EFBFBD>o estabelecida com o host {$this->GetInfo('host')}");
|
|
|
|
|
|
|
|
|
|
$this->Debug("Tentando login com o host {$this->GetInfo('host')}");
|
|
|
|
|
if (!ssh2_auth_password($this->_handler, $this->backup->user, $this->backup->pass)) {
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel autenticar o usu<EFBFBD>rio no servidor!");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->Debug("Login efetuado com o host {$this->GetInfo('host')}");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Acessa as informa<EFBFBD><EFBFBD>es de backup (host,porta,usuario,senha,etc)
|
|
|
|
|
*
|
|
|
|
|
* @param string $property
|
|
|
|
|
*
|
|
|
|
|
* @return null|mixed
|
|
|
|
|
*/
|
|
|
|
|
protected final function GetInfo($property) {
|
|
|
|
|
if (!isset($this->backup) || !isset($this->backup->$property)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->backup->$property;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cria um diret<EFBFBD>rio
|
|
|
|
|
*
|
|
|
|
|
* @param string $dir
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected final function MkDirLocalNetwork($dir) {
|
|
|
|
|
$result = false;
|
|
|
|
|
$cmd = array();
|
|
|
|
|
$command = sprintf("mkdir %s\n", $dir);
|
|
|
|
|
exec($command, $cmd, $result);
|
|
|
|
|
|
|
|
|
|
if (!file_exists($dir)) {
|
|
|
|
|
$this->Error("N<EFBFBD>o foi poss<EFBFBD>vel criar o diret<EFBFBD>rio destino: " . $command);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function sys($cmd, $timeout = 5) {
|
|
|
|
|
$this->Debug("CMD [{$cmd}]");
|
|
|
|
|
$output = null;
|
|
|
|
|
try {
|
|
|
|
|
$output = $this->exec_timeout($cmd, $timeout);
|
|
|
|
|
$this->Output($output);
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
$this->Error($e->getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $output;
|
|
|
|
|
#return utf8_decode(system($cmd . " 2>&1", $ret));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute a command and return it's output. Either wait until the command exits or the timeout has expired.
|
|
|
|
|
*
|
|
|
|
|
* @param string $cmd Command to execute.
|
|
|
|
|
* @param number $timeout Timeout in seconds.
|
|
|
|
|
*
|
|
|
|
|
* @return string Output of the command.
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
function exec_timeout($cmd, $timeout) {
|
|
|
|
|
// File descriptors passed to the process.
|
|
|
|
|
$descriptors = array(
|
|
|
|
|
0 => array('pipe', 'r'), // stdin
|
|
|
|
|
1 => array('pipe', 'w'), // stdout
|
|
|
|
|
2 => array('pipe', 'w') // stderr
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Start the process.
|
|
|
|
|
$process = proc_open('exec ' . $cmd, $descriptors, $pipes);
|
|
|
|
|
|
|
|
|
|
if (!is_resource($process)) {
|
|
|
|
|
throw new \Exception('Could not execute process');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the stdout stream to none-blocking.
|
|
|
|
|
stream_set_blocking($pipes[1], 0);
|
|
|
|
|
|
|
|
|
|
// Turn the timeout into microseconds.
|
|
|
|
|
$timeout = $timeout * 1000000;
|
|
|
|
|
|
|
|
|
|
// Output buffer.
|
|
|
|
|
$buffer = '';
|
|
|
|
|
|
|
|
|
|
// While we have time to wait.
|
|
|
|
|
while ($timeout > 0) {
|
|
|
|
|
$start = microtime(true);
|
|
|
|
|
|
|
|
|
|
// Wait until we have output or the timer expired.
|
|
|
|
|
$read = array($pipes[1]);
|
|
|
|
|
$other = array();
|
|
|
|
|
stream_select($read, $other, $other, 0, $timeout);
|
|
|
|
|
|
|
|
|
|
// Get the status of the process.
|
|
|
|
|
// Do this before we read from the stream,
|
|
|
|
|
// this way we can't lose the last bit of output if the process dies between these functions.
|
|
|
|
|
$status = proc_get_status($process);
|
|
|
|
|
|
|
|
|
|
// Read the contents from the buffer.
|
|
|
|
|
// This function will always return immediately as the stream is none-blocking.
|
|
|
|
|
$buffer .= stream_get_contents($pipes[1]);
|
|
|
|
|
|
|
|
|
|
if (!$status['running']) {
|
|
|
|
|
// Break from this loop if the process exited before the timeout.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Subtract the number of microseconds that we waited.
|
|
|
|
|
$timeout -= (microtime(true) - $start) * 1000000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if there were any errors.
|
|
|
|
|
$errors = stream_get_contents($pipes[2]);
|
|
|
|
|
|
|
|
|
|
if (!empty($errors)) {
|
|
|
|
|
throw new \Exception($errors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Kill the process in case the timeout expired and it's still running.
|
|
|
|
|
// If the process already exited this won't do anything.
|
|
|
|
|
proc_terminate($process, 9);
|
|
|
|
|
|
|
|
|
|
// Close all streams.
|
|
|
|
|
fclose($pipes[0]);
|
|
|
|
|
fclose($pipes[1]);
|
|
|
|
|
fclose($pipes[2]);
|
|
|
|
|
|
|
|
|
|
proc_close($process);
|
|
|
|
|
|
|
|
|
|
return $buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected final function Output($msg) {
|
|
|
|
|
if (!is_null($msg) && !empty($msg)) {
|
|
|
|
|
BackupConfig::log("Output [{$this->TipoBackup()}]: {$msg}\n", false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// se n<EFBFBD>o for backup local, apaga os arquivos gerados
|
|
|
|
|
// se for local, mant<EFBFBD>m para download
|
|
|
|
|
protected function unlink($localFile) {
|
|
|
|
|
if (file_exists($localFile)) {
|
|
|
|
|
@unlink($localFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|