forked from SimplesIP/pabx-app
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
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); |
|
} |
|
} |
|
|
|
}
|
|
|