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); } } }