connect_error) { log_message("Connection failed: " . $mysqli->connect_error, 'ERROR'); exit(1); // Sair com erro } // Assegurar que as mensagens de log incluam o ID do processo $process_id = getmypid(); log_message("Processo $process_id: Processando arquivo ID " . $row['id'] . ": " . $row['file_path'], 'INFO'); try { $file_path = $row['file_path']; $queue_id = $row['id']; $user_agent = $row['user_agent']; $ip_address = $row['ip_address']; $host_name = $row['host_name']; $access_time = $row['access_time'] . ' ' . date("P"); // Adiciona fuso horário $language = $row['language']; $referer = $row['referer']; $protocol = $row['protocol']; $connection = $row['connection']; $viewport_width = $row['viewport_width']; $viewport_height = $row['viewport_height']; $original_location = $row['original_location']; // Novo campo para localização original // Recuperar a porta do cliente $result_port = $mysqli->query("SELECT port FROM client_ports WHERE queue_id = $queue_id"); $client_port = $result_port->fetch_assoc()['port'] ?? 'N/A'; $result_port->free(); // Atualizar status para 'processing' $stmt_update = $mysqli->prepare("UPDATE queue SET status = 'processing' WHERE id = ?"); $stmt_update->bind_param("i", $queue_id); $stmt_update->execute(); $stmt_update->close(); log_message("Processo $process_id: Atualizou status para 'processing' para o arquivo ID $queue_id.", 'INFO'); // Gerar checksums $checksums = generate_checksums($file_path); // Capturar data e hora $file_datetime = get_file_datetime($file_path); // Registrar timestamp usando OpenTimestamps $timestamp_result = register_timestamp_opentimestamps($file_path); $signature_time = date("d/m/Y H:i:s P"); // Inclui fuso horário if ($timestamp_result['success']) { $ots_base64 = $timestamp_result['ots_base64']; // Coletar dados do dispositivo $device_data = [ 'User Agent' => $user_agent, 'IP Address' => $ip_address . ':' . $client_port, 'Host Name' => $host_name, 'Access Time' => $access_time, 'Language' => $language, 'Referer' => $referer, 'Protocol' => $protocol, 'Connection' => $connection, 'Viewport Width' => $viewport_width, 'Viewport Height' => $viewport_height, 'Original Location' => $original_location // Adicionado ]; // Gerar PDF $pdf_path = generate_pdf($file_path, [ 'name' => basename($file_path), 'creation_time' => $file_datetime['creation_time'] . ' ' . date("P", filectime($file_path)), 'modification_time' => $file_datetime['modification_time'] . ' ' . date("P", filemtime($file_path)), 'upload_time' => $access_time, 'signature_time' => $signature_time ], $checksums, $device_data, $ots_base64); // Atualizar status para 'completed' $stmt_complete = $mysqli->prepare("UPDATE queue SET status = 'completed' WHERE id = ?"); $stmt_complete->bind_param("i", $queue_id); $stmt_complete->execute(); $stmt_complete->close(); log_message("Processo $process_id: Processamento concluído para arquivo ID $queue_id: $file_path", 'INFO'); } else { // Atualizar status para 'error' $stmt_error = $mysqli->prepare("UPDATE queue SET status = 'error' WHERE id = ?"); $stmt_error->bind_param("i", $queue_id); $stmt_error->execute(); $stmt_error->close(); log_message("Processo $process_id: Erro ao processar arquivo ID $queue_id: " . $timestamp_result['message'], 'ERROR'); } } catch (Exception $e) { // Atualizar status para 'error' $stmt_exception = $mysqli->prepare("UPDATE queue SET status = 'error' WHERE id = ?"); $stmt_exception->bind_param("i", $queue_id); $stmt_exception->execute(); $stmt_exception->close(); log_message("Processo $process_id: Exceção ao processar arquivo ID $queue_id: " . $e->getMessage(), 'ERROR'); } $mysqli->close(); log_message("Processo $process_id: Fechou conexão com o banco de dados para o arquivo ID $queue_id.", 'INFO'); } // Funções auxiliares (generate_checksums, get_file_datetime, register_timestamp_opentimestamps, generate_pdf) // Mantenha estas funções como estão no seu código original, sem alterações. Certifique-se de que as funções não dependam de conexões globais de banco de dados. require_once __DIR__ . '../../vendor/autoload.php'; // Função para gerar checksums function generate_checksums($file_path) { log_message("Gerando checksums para $file_path", 'INFO'); $start_time = microtime(true); // Carregar o conteúdo do arquivo uma única vez $file_content = file_get_contents($file_path); $checksums = [ 'sha256' => hash('sha256', $file_content), 'sha512' => hash('sha512', $file_content), 'md5' => hash('md5', $file_content) ]; $end_time = microtime(true); log_message("Checksums gerados em " . round($end_time - $start_time, 4) . " segundos", 'INFO'); return $checksums; } // Função para capturar data e hora do arquivo function get_file_datetime($file_path) { log_message("Obtendo data e hora do arquivo para $file_path", 'INFO'); $start_time = microtime(true); $creation_time = date("d/m/Y H:i:s P", filectime($file_path)); $modification_time = date("d/m/Y H:i:s P", filemtime($file_path)); $end_time = microtime(true); log_message("Data e hora obtidas em " . round($end_time - $start_time, 4) . " segundos", 'INFO'); return [ 'creation_time' => $creation_time, 'modification_time' => $modification_time ]; } // Função para registrar timestamp usando OpenTimestamps function register_timestamp_opentimestamps($file_path) { log_message("Registrando timestamp para $file_path", 'INFO'); $start_time = microtime(true); // Definir diretório de cache para OTS $ots_cache_dir = '/var/www/html/ots_cache'; if (!is_dir($ots_cache_dir)) { log_message("Diretório de cache OTS não existe. Criando em $ots_cache_dir", 'INFO'); if (!mkdir($ots_cache_dir, 0755, true)) { log_message("Erro ao criar diretório de cache OTS: $ots_cache_dir", 'ERROR'); return ['success' => false, 'message' => "Erro ao criar diretório de cache OTS: $ots_cache_dir"]; } chmod($ots_cache_dir, 0755); // Ajustar permissões } // Definir a variável de ambiente antes de executar o comando $command_stamp = "OTS_CACHE_DIRECTORY=" . escapeshellarg($ots_cache_dir) . " ots stamp " . escapeshellarg($file_path) . " 2>&1"; log_message("Comando para executar: $command_stamp", 'INFO'); // Executar o comando exec($command_stamp, $output_stamp, $return_var_stamp); // Verificar o resultado da execução if ($return_var_stamp !== 0) { log_message("Erro ao registrar timestamp: " . implode("\n", $output_stamp), 'ERROR'); return ['success' => false, 'message' => 'Erro ao registrar timestamp: ' . implode("\n", $output_stamp)]; } // Definir o caminho do arquivo .ots gerado $ots_file = "{$file_path}.ots"; // Verificar se o arquivo .ots foi criado if (!file_exists($ots_file)) { log_message("Arquivo de timestamp não encontrado: $ots_file", 'ERROR'); return ['success' => false, 'message' => "Arquivo de timestamp não encontrado: $ots_file"]; } // Codificar o arquivo .ots em base64 $ots_base64 = base64_encode(file_get_contents($ots_file)); $end_time = microtime(true); log_message("Timestamp registrado e codificado em " . round($end_time - $start_time, 4) . " segundos", 'INFO'); return ['success' => true, 'ots_base64' => $ots_base64]; } // Função para gerar PDF function generate_pdf($uploaded_file_path, $document_info, $checksums, $device_data, $ots_base64) { log_message("Gerando PDF para $uploaded_file_path", 'INFO'); $start_time = microtime(true); // Criar uma nova instância do TCPDF $pdf = new TCPDF(); // Configurações iniciais do PDF $pdf->SetCreator(PDF_CREATOR); $pdf->SetAuthor('Seu Nome ou Empresa'); $pdf->SetTitle('Prova de Autenticidade de Documento'); $pdf->SetSubject('Timestamping e Checksums'); // Remover as margens padrão $pdf->SetMargins(10, 10, 10); $pdf->SetHeaderMargin(0); $pdf->SetFooterMargin(0); // Desativar cabeçalhos e rodapés automáticos $pdf->setPrintHeader(false); $pdf->setPrintFooter(false); // Configurar a fonte padrão uma vez $pdf->SetFont('helvetica', '', 10); // Adicionar uma página para o documento $pdf->AddPage(); // Verificar o tipo de arquivo para incorporar no PDF $file_info = pathinfo($uploaded_file_path); $extension = strtolower($file_info['extension']); // Configurar o diretório temporário para o ImageMagick $temp_dir = '/tmp/imagick'; if (!is_dir($temp_dir)) { mkdir($temp_dir, 0777, true); } chmod($temp_dir, 0777); // Garantir permissões totais // Configurar o diretório temporário para as imagens $tmp_dir = '/tmp/image'; if (!is_dir($tmp_dir)) { mkdir($tmp_dir, 0777, true); } chmod($tmp_dir, 0777); // Garantir permissões totais // Criar uma única instância de Imagick $imagick = new Imagick(); // Configurar o ambiente para otimização putenv('MAGICK_TMPDIR=' . $temp_dir); // Defina um diretório temporário adequado $imagick->setResourceLimit(Imagick::RESOURCETYPE_MEMORY, 512); // Ajuste conforme necessário $imagick->setResourceLimit(Imagick::RESOURCETYPE_MAP, 512); $imagick->setResourceLimit(Imagick::RESOURCETYPE_THREAD, 2); try { if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) { log_message("Processando imagem $uploaded_file_path", 'INFO'); // Reduzir a resolução para 1200x900, mantendo a proporção log_message("Reduzindo resolução da imagem para melhor qualidade e menor tamanho", 'INFO'); $imagick->readImage($uploaded_file_path); $imagick->resizeImage(1200, 900, Imagick::FILTER_LANCZOS, 1, true); // Configurar a compressão usando PNG para melhor qualidade sem perder transparência if ($extension === 'png') { $imagick->setImageFormat('png'); $imagick->stripImage(); // Remove metadados $image_blob = $imagick->getImageBlob(); } else { // Para JPEG e outros, usar alta qualidade com compressão balanceada log_message("Configurando compressão da imagem para JPEG", 'INFO'); $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); $imagick->setImageCompressionQuality(85); // Maior qualidade $imagick->stripImage(); // Remove metadados para reduzir o tamanho $image_blob = $imagick->getImageBlob(); } // Salvar a imagem temporariamente $tmp_img = tempnam($tmp_dir, 'img_') . '.' . $extension; log_message("Salvando imagem temporária em $tmp_img", 'INFO'); file_put_contents($tmp_img, $image_blob); chmod($tmp_img, 0666); // Ajustar permissões do arquivo temporário // Adicionar a imagem ao PDF log_message("Adicionando imagem ao PDF", 'INFO'); $pdf->Image('@' . $image_blob, 15, 40, 180, 160, '', '', 'C', false, 100, '', false, false, 1, false, false, false); log_message("Imagem adicionada ao PDF", 'INFO'); // Remover a imagem temporária if (file_exists($tmp_img)) { log_message("Removendo imagem temporária $tmp_img", 'INFO'); unlink($tmp_img); } else { log_message("Imagem temporária $tmp_img não encontrada para remoção", 'WARN'); } } elseif (in_array($extension, ['pdf'])) { log_message("Processando PDF $uploaded_file_path", 'INFO'); // Verificar se a extensão Imagick está carregada if (extension_loaded('imagick')) { log_message("Imagick está carregado", 'INFO'); // Inicializar Imagick try { log_message("Inicializando Imagick para $uploaded_file_path", 'INFO'); $imagick = new Imagick(); $imagick->setResolution(150, 150); // Ajuste a resolução conforme necessário $imagick->readImage($uploaded_file_path); // Ler todas as páginas do PDF log_message("PDF lido com sucesso", 'INFO'); // Processar cada página do PDF $page_count = $imagick->getNumberImages(); // Número total de páginas do PDF for ($i = 0; $i < $page_count; $i++) { $imagick->setIteratorIndex($i); // Define a página atual $imagick->setImageFormat("jpeg"); // Configura o formato como JPEG // Reduz a resolução para 1600x1200, mantendo a proporção $imagick->resizeImage(1600, 1200, Imagick::FILTER_LANCZOS, 1, true); // Configura as opções de compressão $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); $imagick->setImageCompressionQuality(85); $imagick->stripImage(); // Remove metadados desnecessários // Obter os dados da imagem $image_data = $imagick->getImageBlob(); $tmp_img = tempnam($tmp_dir, 'pdf_page_') . '.jpg'; file_put_contents($tmp_img, $image_data); // Salvar página como JPEG temporária chmod($tmp_img, 0666); // Adicionar a página como imagem ao novo PDF $pdf->AddPage(); $pdf->Image($tmp_img, 15, 40, 180, 160, '', '', 'C', false, 100, '', false, false, 1, false, false, false); // Remover a imagem temporária unlink($tmp_img); } // Limpar recursos do Imagick $imagick->clear(); $imagick->destroy(); } catch (Exception $e) { log_message("Erro ao inicializar ou processar imagem com Imagick: " . $e->getMessage(), 'ERROR'); } } else { log_message("Imagick não está carregado", 'WARN'); // Se Imagick não estiver disponível, apenas adicionar uma mensagem $pdf->SetFont('helvetica', '', 12); $pdf->Write(0, 'Visualização de PDF não suportada.'); } } else { log_message("Tipo de arquivo não suportado: $extension", 'WARN'); // Outros tipos de arquivo podem ser adicionados conforme necessidade $pdf->SetFont('helvetica', '', 12); $pdf->Write(0, 'Visualização deste tipo de arquivo não está disponível.'); } } catch (Exception $e) { log_message("Erro ao processar imagem: " . $e->getMessage(), 'ERROR'); } // Adicionar uma nova página para as informações $pdf->AddPage(); // Conteúdo das informações $html = '

Informações do Documento

'; $html .= '

Nome do Arquivo: ' . htmlspecialchars($document_info['name']) . '

'; $html .= '

Data de Modificação: ' . htmlspecialchars($document_info['modification_time']) . '

'; $html .= '

Data de Envio ao Servidor: ' . htmlspecialchars($document_info['upload_time']) . '

'; $html .= '

Data e Hora da Assinatura: ' . htmlspecialchars($document_info['signature_time']) . ' (UTC-3)

'; $html .= '

Checksums

'; $html .= ''; $html .= '

Dados do Dispositivo

'; $html .= ''; $orig_link= 'https://validar.martinsevieira.adv.br/uploads/'; $html .= '

Arquivos Originais

'; $html .= ''; $html .= '

Arquivo de Timestamp (OTS) em Base64

'; $html .= '
' . htmlspecialchars($ots_base64) . '
'; $pdf->writeHTML($html, true, false, true, false, ''); // Salvar o PDF no servidor $processed_dir = '/var/www/html/processed/'; if (!is_dir($processed_dir)) { mkdir($processed_dir, 0777, true); } log_message("Salvando PDF no servidor", 'INFO'); $pdf_output_path = $processed_dir . uniqid('prova_') . '.pdf'; $pdf->Output($pdf_output_path, 'F'); $end_time = microtime(true); log_message("PDF gerado em " . round($end_time - $start_time, 4) . " segundos", 'INFO'); // Limpar recursos do Imagick $imagick->clear(); $imagick->destroy(); return $pdf_output_path; } // Recuperar todos os arquivos pendentes $mysqli = new mysqli("10.15.0.42", "root", "34641747", "valida"); if ($mysqli->connect_error) { log_message("Connection failed: " . $mysqli->connect_error, 'ERROR'); die("Connection failed: " . $mysqli->connect_error); } log_message("Conexão com o banco de dados estabelecida.", 'INFO'); // Recuperar todos os arquivos pendentes $result = $mysqli->query("SELECT q.*, cp.port, q.original_location FROM queue q LEFT JOIN client_ports cp ON q.id = cp.queue_id WHERE q.status = 'pending'"); if ($result->num_rows > 0) { log_message("Encontrados " . $result->num_rows . " arquivos pendentes para processar.", 'INFO'); while ($row = $result->fetch_assoc()) { // Verificar se o número de processos filhos atingiu o máximo permitido while (count($childProcesses) >= MAX_CHILDREN) { // Esperar por qualquer processo filho terminar $endedPid = pcntl_wait($status); if ($endedPid > 0) { unset($childProcesses[$endedPid]); log_message("Processo filho $endedPid finalizado.", 'INFO'); } } // Fork do processo $pid = pcntl_fork(); if ($pid == -1) { // Erro ao fork log_message("Erro ao criar processo filho.", 'ERROR'); } elseif ($pid) { // Processo pai $childProcesses[$pid] = true; log_message("Criado processo filho $pid para o arquivo ID " . $row['id'] . ".", 'INFO'); } else { // Processo filho // Fecha a conexão do processo pai antes de iniciar no filho $mysqli->close(); // Processa o arquivo process_file($row); // Finaliza o processo filho exit(0); } } // Após forçar todos os filhos a serem criados, esperar que terminem while (count($childProcesses) > 0) { $endedPid = pcntl_wait($status); if ($endedPid > 0) { unset($childProcesses[$endedPid]); log_message("Processo filho $endedPid finalizado.", 'INFO'); } } log_message("Todas as tarefas foram submetidas para processamento.", 'INFO'); } else { log_message("Nenhum arquivo pendente para processar.", 'INFO'); } $mysqli->close(); log_message("Processamento da fila finalizado.", 'INFO'); ?>