You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

384 lines
10 KiB

<?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ório/pasta
*
* @param string $dir
*/
abstract function Mkdir($dir);
/**
* Acessa um diretó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á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ão foi possí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ã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á 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 última mensagem de erro lançada pelo gerenciador do backup
*
* @return string|null
*/
public function getExceptionMessage() {
return $this->exceptionMessage;
}
/**
* Checa a integridade do arquivo (no host do backup) apó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ão foi possí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á corrompida");
} else {
$this->Debug("Integridade validada, o arquivo está ok.");
}
return $sameHash;
}
protected final function Debug($msg) {
BackupConfig::log("Debug [{$this->TipoBackup()}]: {$msg}\n", false);
}
/**
* Acessa um diretó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ão foi possível acesar o diretório destino!");
return false;
}
$this->backup->dirAtual = $this->backup->VerBarraPath($dir);
return true;
}
/**
* Abre uma conexão SSH com o host remoto
*
* @return bool
*/
protected function ConectaSSH() {
$this->Debug("Abrindo conexão com o host {$this->GetInfo('host')}");
$this->_handler = ssh2_connect($this->GetInfo('host'), $this->GetInfo('port'));
if (!$this->_handler) {
$this->Error("Não foi possível se conectar ao servidor!");
return false;
}
$this->Debug("Conexã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ão foi possível autenticar o usuário no servidor!");
return false;
}
$this->Debug("Login efetuado com o host {$this->GetInfo('host')}");
return true;
}
/**
* Acessa as informaçõ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ó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ão foi possível criar o diretó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ão for backup local, apaga os arquivos gerados
// se for local, mantém para download
protected function unlink($localFile) {
if (file_exists($localFile)) {
@unlink($localFile);
}
}
}