From 349dcff45eb6f2cbe49deab2a83165ff1f926837 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 21 Feb 2026 09:32:03 -0500 Subject: [PATCH] Update MediaService.php --- classes/MediaService.php | 118 ++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 34 deletions(-) diff --git a/classes/MediaService.php b/classes/MediaService.php index cdd307c..0f4e809 100644 --- a/classes/MediaService.php +++ b/classes/MediaService.php @@ -23,7 +23,7 @@ class MediaService { } /** - * Hacer petición GET al Graph API con fallback a wget si curl falla + * Hacer petición GET al Graph API con fallback si curl falla */ private function graphApiRequest($url) { // Intentar con curl primero @@ -45,24 +45,9 @@ class MediaService { return $resp; } - // Fallback a wget para la petición al Graph API - error_log("[MediaService::graphApiRequest] curl failed (http={$http} err={$err}), trying wget..."); - $tmpFile = tempnam(sys_get_temp_dir(), 'graph_'); - $cmd = 'wget -q --prefer-family=IPv4 -O ' . escapeshellarg($tmpFile) - . ' --header=' . escapeshellarg('Authorization: Bearer ' . $this->token) - . ' ' . escapeshellarg($url) . ' 2>&1'; - exec($cmd, $output, $exitCode); - - if ($exitCode === 0 && file_exists($tmpFile) && filesize($tmpFile) > 0) { - $content = file_get_contents($tmpFile); - unlink($tmpFile); - error_log("[MediaService::graphApiRequest] wget OK, " . strlen($content) . " bytes"); - return $content; - } - - error_log("[MediaService::graphApiRequest] wget also failed exit={$exitCode}"); - if (file_exists($tmpFile)) unlink($tmpFile); - return false; + // Fallback: file_get_contents con stream context + error_log("[MediaService::graphApiRequest] curl failed (http={$http} err={$err}), trying file_get_contents..."); + return $this->fileGetContentsWithAuth($url); } public function fetchAndStoreFromGraph($mediaId, $subdir = '') { @@ -180,50 +165,115 @@ class MediaService { return $content; } - // Fallback a wget cuando curl falla (OpenSSL incompatibilidad con Facebook CDN) - error_log("[MediaService::downloadUrl] curl failed (http={$http} errno={$errno} err={$err}), trying wget fallback..."); - $content = $this->downloadWithWget($url, $headers); + // Fallback: file_get_contents con stream context (usa implementación SSL diferente a curl) + error_log("[MediaService::downloadUrl] curl failed (http={$http} errno={$errno} err={$err}), trying file_get_contents fallback..."); + $content = $this->fileGetContentsWithAuth($url); + if ($content !== false && strlen($content) > 0) { + error_log("[MediaService::downloadUrl] file_get_contents OK, " . strlen($content) . " bytes"); + return $content; + } + + // Fallback: exec wget (solo funciona en CLI, no en php-fpm con exec deshabilitado) + $content = $this->shellDownload($url, $headers); if ($content !== false) { return $content; } - error_log("[MediaService::downloadUrl] FAIL url=" . substr($url, 0, 120) . " http={$http} err={$err} wget=also_failed"); + error_log("[MediaService::downloadUrl] FAIL url=" . substr($url, 0, 120) . " http={$http} err={$err} all_fallbacks_failed"); return false; } /** - * Fallback: descargar con wget (usa GnuTLS en vez de OpenSSL, más compatible con Facebook CDN) + * Descargar usando file_get_contents con stream context (usa implementación SSL nativa de PHP, diferente a curl) */ - private function downloadWithWget($url, $headers = []) { + private function fileGetContentsWithAuth($url) { + $opts = [ + 'http' => [ + 'method' => 'GET', + 'header' => "Authorization: Bearer {$this->token}\r\nUser-Agent: WhatsAppMediaFetcher/1.0\r\nAccept: */*\r\n", + 'follow_location' => true, + 'max_redirects' => 5, + 'timeout' => 120, + 'ignore_errors' => false, + ], + 'ssl' => [ + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + ]; + $ctx = stream_context_create($opts); + $content = @file_get_contents($url, false, $ctx); + if ($content === false) { + $lastErr = error_get_last(); + error_log("[MediaService::fileGetContentsWithAuth] FAIL: " . ($lastErr['message'] ?? 'unknown')); + return false; + } + return $content; + } + + /** + * Fallback: descargar con wget via exec/proc_open (solo funciona en CLI) + */ + private function shellDownload($url, $headers = []) { + // Verificar si exec está disponible + if (!function_exists('exec') || in_array('exec', array_map('trim', explode(',', ini_get('disable_functions'))))) { + // Intentar proc_open como alternativa + if (function_exists('proc_open')) { + return $this->procOpenDownload($url, $headers); + } + error_log("[MediaService::shellDownload] Neither exec nor proc_open available"); + return false; + } + $tmpFile = tempnam(sys_get_temp_dir(), 'wget_'); $headerArgs = ''; foreach ($headers as $h) { - // Solo pasar headers que wget soporte $headerArgs .= ' --header=' . escapeshellarg($h); } - // Intentar con GNU wget primero (soporta --prefer-family=IPv4) $cmd = 'wget -q --prefer-family=IPv4 -O ' . escapeshellarg($tmpFile) . $headerArgs . ' ' . escapeshellarg($url) . ' 2>&1'; exec($cmd, $output, $exitCode); if ($exitCode === 0 && file_exists($tmpFile) && filesize($tmpFile) > 0) { $content = file_get_contents($tmpFile); unlink($tmpFile); - error_log("[MediaService::downloadWithWget] OK, " . strlen($content) . " bytes"); + error_log("[MediaService::shellDownload] wget OK, " . strlen($content) . " bytes"); return $content; } - // Fallback sin --prefer-family (BusyBox wget no lo soporta) - $cmd2 = 'wget -q -O ' . escapeshellarg($tmpFile) . $headerArgs . ' ' . escapeshellarg($url) . ' 2>&1'; - exec($cmd2, $output2, $exitCode2); + error_log("[MediaService::shellDownload] wget FAIL exit={$exitCode}"); + if (file_exists($tmpFile)) unlink($tmpFile); + return false; + } - if ($exitCode2 === 0 && file_exists($tmpFile) && filesize($tmpFile) > 0) { + /** + * Descargar con proc_open (alternativa a exec) + */ + private function procOpenDownload($url, $headers = []) { + $tmpFile = tempnam(sys_get_temp_dir(), 'wget_'); + $headerArgs = ''; + foreach ($headers as $h) { + $headerArgs .= ' --header=' . escapeshellarg($h); + } + $cmd = 'wget -q --prefer-family=IPv4 -O ' . escapeshellarg($tmpFile) . $headerArgs . ' ' . escapeshellarg($url); + + $proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes); + if (!is_resource($proc)) { + error_log("[MediaService::procOpenDownload] proc_open failed"); + if (file_exists($tmpFile)) unlink($tmpFile); + return false; + } + $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); + $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); + $exitCode = proc_close($proc); + + if ($exitCode === 0 && file_exists($tmpFile) && filesize($tmpFile) > 0) { $content = file_get_contents($tmpFile); unlink($tmpFile); - error_log("[MediaService::downloadWithWget] OK (basic), " . strlen($content) . " bytes"); + error_log("[MediaService::procOpenDownload] OK, " . strlen($content) . " bytes"); return $content; } - error_log("[MediaService::downloadWithWget] FAIL exit={$exitCode}/{$exitCode2} output=" . implode(' ', array_merge($output, $output2))); + error_log("[MediaService::procOpenDownload] FAIL exit={$exitCode} err={$stderr}"); if (file_exists($tmpFile)) unlink($tmpFile); return false; }