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/public_html/api/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/public_html/api/download_crate_zip.php
<?php
require_once '../config/database.php';
session_start();

// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    echo 'Unauthorized';
    exit;
}

// SECURITY: Validate and sanitize crate_id parameter
$crate_id_raw = $_GET['crate_id'] ?? null;

if (!$crate_id_raw) {
    http_response_code(400);
    echo 'Crate ID is required';
    exit;
}

// SECURITY: Validate that crate_id is a positive integer
if (!is_numeric($crate_id_raw) || (int)$crate_id_raw <= 0) {
    error_log("SECURITY: Invalid crate_id attempt: " . htmlspecialchars($crate_id_raw, ENT_QUOTES, 'UTF-8'));
    http_response_code(400);
    echo 'Invalid crate ID';
    exit;
}

$crate_id = (int)$crate_id_raw;
$user_id = $_SESSION['user_id'];

try {
    $pdo = getDBConnection();
    
    // Get crate details with artist name
    $stmt = $pdo->prepare("
        SELECT 
            ap.id,
            ap.name,
            ap.user_id,
            u.name as artist_name
        FROM artist_playlists ap
        JOIN users u ON ap.user_id = u.id
        WHERE ap.id = ? AND ap.user_id = ?
    ");
    $stmt->execute([$crate_id, $user_id]);
    $crate = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$crate) {
        http_response_code(404);
        echo 'Crate not found or access denied';
        exit;
    }
    
    // Get all tracks in crate that user owns or has purchased (including metadata for BPM/key)
    $stmt = $pdo->prepare("
        SELECT 
            mt.id,
            mt.title,
            mt.audio_url,
            mt.metadata,
            mt.user_id as track_owner_id,
            pt.position,
            CASE WHEN mt.user_id = ? THEN 1 ELSE 0 END as user_owns_track,
            CASE WHEN EXISTS(SELECT 1 FROM track_purchases WHERE user_id = ? AND track_id = mt.id) THEN 1 ELSE 0 END as user_purchased_track
        FROM playlist_tracks pt
        JOIN music_tracks mt ON pt.track_id = mt.id
        WHERE pt.playlist_id = ?
        AND mt.status = 'complete'
        AND (
            mt.user_id = ? 
            OR EXISTS(SELECT 1 FROM track_purchases WHERE user_id = ? AND track_id = mt.id)
        )
        ORDER BY pt.position ASC
    ");
    $stmt->execute([$user_id, $user_id, $crate_id, $user_id, $user_id]);
    $tracks = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    if (empty($tracks)) {
        http_response_code(404);
        echo 'No downloadable tracks found in this crate. You need to own or purchase tracks to download them.';
        exit;
    }
    
    // SECURITY: Validate audio URLs
    require_once '../includes/file_security.php';
    
    // Create ZIP file in temp directory (will be automatically cleaned up)
    $zip = new ZipArchive();
    $zip_filename = tempnam(sys_get_temp_dir(), 'crate_download_') . '.zip';
    
    // Register cleanup function to ensure ZIP is deleted even on errors
    register_shutdown_function(function() use ($zip_filename) {
        if (file_exists($zip_filename)) {
            @unlink($zip_filename);
        }
    });
    
    if ($zip->open($zip_filename, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
        @unlink($zip_filename);
        http_response_code(500);
        echo 'Failed to create ZIP file';
        exit;
    }
    
    $added_count = 0;
    foreach ($tracks as $track) {
        // Validate audio URL
        $audio_validation = validateAudioUrl($track['audio_url']);
        
        if ($audio_validation['type'] === 'invalid') {
            error_log("SECURITY: Invalid audio URL for track {$track['id']}: " . $track['audio_url']);
            continue;
        }
        
        $file_path = null;
        $file_content = null;
        
        if ($audio_validation['type'] === 'external') {
            // Download external file
            $file_content = @file_get_contents($audio_validation['url']);
            if ($file_content === false) {
                error_log("Failed to download external audio file for track {$track['id']}");
                continue;
            }
        } else {
            // Local file
            $file_path = $audio_validation['path'];
            if (!$file_path || !file_exists($file_path)) {
                error_log("Local audio file not found for track {$track['id']}: " . ($file_path ?: 'null'));
                continue;
            }
        }
        
        // Parse metadata to get numerical key (Camelot notation)
        $numerical_key = '';
        if (!empty($track['metadata'])) {
            $metadata = json_decode($track['metadata'], true);
            if (is_array($metadata) && isset($metadata['numerical_key']) && !empty($metadata['numerical_key'])) {
                $numerical_key = $metadata['numerical_key'];
            }
        }
        
        // Sanitize filename: "Artist - Crate Title - 01 - [1A] Track Title.mp3" (with key if available)
        $track_title = $track['title'] ?: 'Untitled Track';
        $position = $track['position'] ?: ($added_count + 1);
        $artist_name = sanitizeDownloadFilename($crate['artist_name']);
        $crate_name = sanitizeDownloadFilename($crate['name']);
        
        // Format: "Artist - Crate Title - 01 - [1A] Track Title.mp3" or "Artist - Crate Title - 01 - Track Title.mp3" (if no key)
        if ($numerical_key) {
            $filename = sanitizeDownloadFilename(sprintf('%s - %s - %02d - [%s] %s.mp3', $artist_name, $crate_name, $position, $numerical_key, $track_title));
        } else {
            $filename = sanitizeDownloadFilename(sprintf('%s - %s - %02d - %s.mp3', $artist_name, $crate_name, $position, $track_title));
        }
        
        // Add to ZIP
        if ($file_content !== null) {
            $zip->addFromString($filename, $file_content);
        } else {
            $zip->addFile($file_path, $filename);
        }
        
        $added_count++;
    }
    
    $zip->close();
    
    if ($added_count === 0) {
        @unlink($zip_filename);
        http_response_code(404);
        echo 'No valid audio files found to download';
        exit;
    }
    
    // Generate download filename: "Artist - Crate Title.zip"
    $artist_name = sanitizeDownloadFilename($crate['artist_name']);
    $crate_name = sanitizeDownloadFilename($crate['name']);
    $download_filename = $artist_name . ' - ' . $crate_name . '.zip';
    
    // Send ZIP file
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $download_filename . '"');
    header('Content-Length: ' . filesize($zip_filename));
    header('Cache-Control: no-cache, must-revalidate');
    header('Pragma: no-cache');
    
    readfile($zip_filename);
    
    // Clean up immediately after sending (file is streamed, so safe to delete)
    @unlink($zip_filename);
    
} catch (Exception $e) {
    // Ensure cleanup on error
    if (isset($zip_filename) && file_exists($zip_filename)) {
        @unlink($zip_filename);
    }
    error_log("Error downloading crate ZIP: " . $e->getMessage());
    http_response_code(500);
    echo 'Internal server error. Please try again.';
}


CasperSecurity Mini