MagicWP Docs

Download Backup

Download and manage your WordPress backup files securely

Download Backup

Access and download your WordPress backup files securely. Choose from various download options and manage your backup archive effectively.

Download Options

Direct Browser Download

Single Backup Download

// Generate secure download link for backup
function generate_backup_download_link($backup_id, $expiration_hours = 24) {
    // Verify user has permission to download
    if (!current_user_can('manage_options')) {
        return new WP_Error('insufficient_permissions', 'You do not have permission to download backups.');
    }

    // Get backup information
    $backup_info = get_backup_info($backup_id);
    if (!$backup_info) {
        return new WP_Error('backup_not_found', 'Backup not found.');
    }

    // Generate secure token
    $download_token = wp_generate_password(32, false);
    $expires = time() + ($expiration_hours * 3600);

    // Store download token temporarily
    set_transient('backup_download_' . $download_token, array(
        'backup_id' => $backup_id,
        'backup_path' => $backup_info['path'],
        'expires' => $expires,
        'user_id' => get_current_user_id(),
        'ip_address' => $_SERVER['REMOTE_ADDR']
    ), $expiration_hours * 3600);

    // Generate download URL
    $download_url = add_query_arg(array(
        'action' => 'download_backup',
        'token' => $download_token
    ), admin_url('admin-ajax.php'));

    return array(
        'url' => $download_url,
        'expires' => $expires,
        'filename' => basename($backup_info['path'])
    );
}

Handle Download Request

// Process backup download request
function handle_backup_download_request() {
    $token = sanitize_text_field($_GET['token']);

    if (empty($token)) {
        wp_die('Invalid download request.');
    }

    // Get download information
    $download_info = get_transient('backup_download_' . $token);

    if (!$download_info) {
        wp_die('Download link has expired or is invalid.');
    }

    // Security checks
    if ($download_info['user_id'] !== get_current_user_id()) {
        wp_die('You do not have permission to download this backup.');
    }

    if ($download_info['ip_address'] !== $_SERVER['REMOTE_ADDR']) {
        // Optional: Allow download from same IP range
        $allowed_range = get_option('backup_download_ip_range', false);
        if (!$allowed_range || !ip_in_range($_SERVER['REMOTE_ADDR'], $allowed_range)) {
            wp_die('Download request must come from the same IP address.');
        }
    }

    if (time() > $download_info['expires']) {
        wp_die('Download link has expired.');
    }

    $backup_path = $download_info['backup_path'];

    if (!file_exists($backup_path)) {
        wp_die('Backup file not found.');
    }

    // Delete token after single use
    delete_transient('backup_download_' . $token);

    // Serve file for download
    serve_backup_file($backup_path);
}
add_action('wp_ajax_download_backup', 'handle_backup_download_request');
add_action('wp_ajax_nopriv_download_backup', 'handle_backup_download_request');

Batch Download

Multiple Backup Download

function create_batch_download($backup_ids) {
    if (empty($backup_ids)) {
        return new WP_Error('no_backups_selected', 'No backups selected for download.');
    }

    // Verify user permissions
    if (!current_user_can('manage_options')) {
        return new WP_Error('insufficient_permissions', 'You do not have permission to download backups.');
    }

    // Create temporary directory for batch
    $batch_dir = WP_CONTENT_DIR . '/temp-batch-' . time();
    wp_mkdir_p($batch_dir);

    $included_backups = array();

    // Copy selected backups to batch directory
    foreach ($backup_ids as $backup_id) {
        $backup_info = get_backup_info($backup_id);

        if ($backup_info && file_exists($backup_info['path'])) {
            $filename = basename($backup_info['path']);
            copy($backup_info['path'], $batch_dir . '/' . $filename);
            $included_backups[] = $filename;
        }
    }

    if (empty($included_backups)) {
        remove_directory_recursive($batch_dir);
        return new WP_Error('no_valid_backups', 'No valid backups found for download.');
    }

    // Create batch archive
    $batch_archive = create_batch_archive($batch_dir, $included_backups);

    // Clean up temporary directory
    remove_directory_recursive($batch_dir);

    // Generate download link for batch archive
    return generate_backup_download_link_for_file($batch_archive, 'batch_backup_' . date('Y-m-d-H-i-s') . '.zip');
}

Create Batch Archive

function create_batch_archive($source_dir, $files) {
    $archive_name = 'batch_backup_' . date('Y-m-d-H-i-s') . '.zip';
    $archive_path = WP_CONTENT_DIR . '/temp-archives/' . $archive_name;

    // Ensure archive directory exists
    wp_mkdir_p(dirname($archive_path));

    // Create ZIP archive
    $zip = new ZipArchive();

    if ($zip->open($archive_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
        foreach ($files as $file) {
            $file_path = $source_dir . '/' . $file;
            if (file_exists($file_path)) {
                $zip->addFile($file_path, $file);
            }
        }
        $zip->close();
    } else {
        return new WP_Error('archive_creation_failed', 'Failed to create batch archive.');
    }

    return $archive_path;
}

File Serving Functions

Serve Backup File

function serve_backup_file($file_path) {
    if (!file_exists($file_path)) {
        wp_die('Backup file not found.');
    }

    // Get file information
    $file_name = basename($file_path);
    $file_size = filesize($file_path);
    $file_mime = mime_content_type($file_path);

    // Set headers for download
    header('Content-Type: ' . $file_mime);
    header('Content-Disposition: attachment; filename="' . $file_name . '"');
    header('Content-Length: ' . $file_size);
    header('Cache-Control: no-cache, no-store, must-revalidate');
    header('Pragma: no-cache');
    header('Expires: 0');

    // Handle large files efficiently
    if ($file_size > 10485760) { // 10MB
        serve_large_file($file_path);
    } else {
        // Read and output file content
        readfile($file_path);
    }

    exit;
}

function serve_large_file($file_path) {
    $handle = fopen($file_path, 'rb');

    if ($handle === false) {
        wp_die('Unable to open backup file.');
    }

    // Output file in chunks to handle large files
    $chunk_size = 8192; // 8KB chunks

    while (!feof($handle)) {
        echo fread($handle, $chunk_size);
        flush(); // Flush output buffer
    }

    fclose($handle);
}

Download Progress Tracking

function track_download_progress($file_path, $user_id) {
    $file_size = filesize($file_path);
    $download_id = md5($file_path . $user_id . time());

    // Store download information
    set_transient('download_progress_' . $download_id, array(
        'file_path' => $file_path,
        'file_size' => $file_size,
        'user_id' => $user_id,
        'start_time' => time(),
        'bytes_sent' => 0
    ), 3600); // 1 hour expiration

    return $download_id;
}

function update_download_progress($download_id, $bytes_sent) {
    $progress_info = get_transient('download_progress_' . $download_id);

    if ($progress_info) {
        $progress_info['bytes_sent'] = $bytes_sent;
        $progress_info['last_update'] = time();

        set_transient('download_progress_' . $download_id, $progress_info, 3600);
    }
}

function get_download_progress($download_id) {
    $progress_info = get_transient('download_progress_' . $download_id);

    if (!$progress_info) {
        return null;
    }

    $percentage = ($progress_info['bytes_sent'] / $progress_info['file_size']) * 100;
    $elapsed_time = time() - $progress_info['start_time'];
    $speed = $elapsed_time > 0 ? $progress_info['bytes_sent'] / $elapsed_time : 0;

    return array(
        'percentage' => round($percentage, 2),
        'bytes_sent' => $progress_info['bytes_sent'],
        'total_bytes' => $progress_info['file_size'],
        'speed_bps' => round($speed, 2),
        'elapsed_time' => $elapsed_time
    );
}

Security Measures

Access Control

function verify_backup_download_permissions($backup_id, $user_id) {
    // Check if user has permission to download backups
    if (!current_user_can('manage_options')) {
        return new WP_Error('insufficient_permissions', 'You do not have permission to download backups.');
    }

    // Check backup ownership
    $backup_info = get_backup_info($backup_id);
    if (!$backup_info) {
        return new WP_Error('backup_not_found', 'Backup not found.');
    }

    // Check if backup is not too old (optional security measure)
    $backup_age_days = (time() - filemtime($backup_info['path'])) / (60 * 60 * 24);
    $max_age_days = get_option('backup_max_download_age', 30);

    if ($backup_age_days > $max_age_days) {
        return new WP_Error('backup_too_old', 'This backup is too old to download.');
    }

    return true;
}

Rate Limiting

function check_download_rate_limit($user_id) {
    $rate_limit_key = 'download_rate_limit_' . $user_id;
    $current_usage = get_transient($rate_limit_key);

    if (!$current_usage) {
        $current_usage = array(
            'count' => 0,
            'reset_time' => time() + 3600 // 1 hour window
        );
    }

    // Check if rate limit exceeded
    $max_downloads_per_hour = get_option('max_downloads_per_hour', 10);

    if ($current_usage['count'] >= $max_downloads_per_hour) {
        return new WP_Error('rate_limit_exceeded', 'Download rate limit exceeded. Please try again later.');
    }

    // Update usage count
    $current_usage['count']++;
    set_transient($rate_limit_key, $current_usage, 3600);

    return true;
}

File Integrity Verification

function verify_backup_file_integrity($file_path) {
    if (!file_exists($file_path)) {
        return new WP_Error('file_not_found', 'Backup file not found.');
    }

    // Check file size
    $file_size = filesize($file_path);
    if ($file_size === 0) {
        return new WP_Error('empty_file', 'Backup file is empty.');
    }

    // Check if file is readable
    if (!is_readable($file_path)) {
        return new WP_Error('file_not_readable', 'Backup file is not readable.');
    }

    // Verify file type based on extension
    $file_extension = pathinfo($file_path, PATHINFO_EXTENSION);
    $allowed_extensions = array('zip', 'tar', 'gz', 'sql', 'tar.gz');

    if (!in_array($file_extension, $allowed_extensions)) {
        return new WP_Error('invalid_file_type', 'Invalid backup file type.');
    }

    // Additional checks for compressed files
    if (in_array($file_extension, array('zip', 'tar', 'gz', 'tar.gz'))) {
        if (!is_backup_archive_valid($file_path)) {
            return new WP_Error('corrupted_archive', 'Backup archive appears to be corrupted.');
        }
    }

    return true;
}

function is_backup_archive_valid($file_path) {
    $file_extension = pathinfo($file_path, PATHINFO_EXTENSION);

    if ($file_extension === 'zip') {
        $zip = new ZipArchive();
        $result = $zip->open($file_path);
        if ($result === true) {
            $zip->close();
            return true;
        }
    } elseif (in_array($file_extension, array('tar', 'gz', 'tar.gz'))) {
        // For tar files, we can check if they're readable
        $handle = fopen($file_path, 'rb');
        if ($handle) {
            $header = fread($handle, 512);
            fclose($handle);
            // Basic check for tar magic number
            return strpos($header, 'ustar') !== false;
        }
    }

    return false;
}

Download Management

Download History

function log_backup_download($backup_id, $user_id, $download_method = 'direct') {
    global $wpdb;

    $log_data = array(
        'backup_id' => $backup_id,
        'user_id' => $user_id,
        'download_method' => $download_method,
        'download_time' => current_time('mysql'),
        'ip_address' => $_SERVER['REMOTE_ADDR'],
        'user_agent' => $_SERVER['HTTP_USER_AGENT']
    );

    $wpdb->insert('backup_download_logs', $log_data);

    // Update download count for backup
    $backup_info = get_backup_info($backup_id);
    if ($backup_info) {
        update_backup_download_count($backup_id);
    }
}

function update_backup_download_count($backup_id) {
    $current_count = get_backup_download_count($backup_id);
    update_option('backup_download_count_' . $backup_id, $current_count + 1);
}

function get_backup_download_count($backup_id) {
    return get_option('backup_download_count_' . $backup_id, 0);
}

Download Queue Management

class BackupDownloadQueue {
    private $queue_option = 'backup_download_queue';

    public function add_to_queue($backup_ids, $user_id) {
        $queue = get_option($this->queue_option, array());

        $queue_item = array(
            'backup_ids' => $backup_ids,
            'user_id' => $user_id,
            'queued_time' => time(),
            'status' => 'queued'
        );

        $queue[] = $queue_item;
        update_option($this->queue_option, $queue);

        return count($queue) - 1; // Return queue position
    }

    public function process_queue() {
        $queue = get_option($this->queue_option, array());

        if (empty($queue)) {
            return;
        }

        // Process first item in queue
        $queue_item = array_shift($queue);

        if ($queue_item['status'] === 'queued') {
            $this->process_download_request($queue_item);
        }

        update_option($this->queue_option, $queue);
    }

    private function process_download_request($queue_item) {
        // Process the download request
        $result = create_batch_download($queue_item['backup_ids']);

        if (!is_wp_error($result)) {
            // Send download link to user
            send_download_link_email($queue_item['user_id'], $result);
        }
    }
}

User Interface Enhancements

Download Dashboard Widget

function add_backup_download_widget() {
    wp_add_dashboard_widget(
        'backup_download_widget',
        'Recent Backup Downloads',
        'display_backup_download_widget'
    );
}

function display_backup_download_widget() {
    global $wpdb;

    // Get recent downloads for current user
    $recent_downloads = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM backup_download_logs
         WHERE user_id = %d
         ORDER BY download_time DESC
         LIMIT 5",
        get_current_user_id()
    ));

    if (empty($recent_downloads)) {
        echo '<p>No recent backup downloads.</p>';
        return;
    }

    echo '<table class="widefat striped">';
    echo '<thead><tr><th>Backup</th><th>Download Time</th><th>Method</th></tr></thead>';
    echo '<tbody>';

    foreach ($recent_downloads as $download) {
        $backup_info = get_backup_info($download->backup_id);
        $backup_name = $backup_info ? basename($backup_info['path']) : 'Unknown';

        echo '<tr>';
        echo '<td>' . esc_html($backup_name) . '</td>';
        echo '<td>' . esc_html(date('M j, Y H:i', strtotime($download->download_time))) . '</td>';
        echo '<td>' . esc_html(ucfirst($download->download_method)) . '</td>';
        echo '</tr>';
    }

    echo '</tbody></table>';
}
add_action('wp_dashboard_setup', 'add_backup_download_widget');

Download Progress Indicator

function display_download_progress($download_id) {
    $progress = get_download_progress($download_id);

    if (!$progress) {
        return '<p>Download not found or expired.</p>';
    }

    $progress_bar_width = $progress['percentage'] . '%';

    ob_start();
    ?>
    <div class="download-progress-container">
        <div class="download-progress-bar">
            <div class="download-progress-fill" style="width: <?php echo esc_attr($progress_bar_width); ?>">
                <span class="download-progress-text">
                    <?php echo esc_html($progress['percentage']); ?>%
                </span>
            </div>
        </div>
        <div class="download-progress-info">
            <span><?php echo size_format($progress['bytes_sent']); ?> of <?php echo size_format($progress['total_bytes']); ?></span>
            <span><?php echo esc_html($progress['speed_bps']); ?> B/s</span>
        </div>
    </div>

    <script>
        // Auto-refresh progress every 2 seconds
        setInterval(function() {
            location.reload();
        }, 2000);
    </script>
    <?php

    return ob_get_clean();
}

Best Practices

Security Best Practices

  1. Secure Tokens: Use cryptographically secure tokens for download links
  2. Expiration: Set reasonable expiration times for download links
  3. IP Validation: Validate downloads come from authorized IP addresses
  4. Rate Limiting: Implement download rate limiting to prevent abuse
  5. Audit Logging: Log all download activities for security monitoring

Performance Best Practices

  1. Chunked Downloads: Handle large files in chunks to prevent memory issues
  2. Resume Support: Allow downloads to resume if interrupted
  3. Progress Tracking: Provide real-time download progress feedback
  4. Queue Management: Process multiple downloads efficiently
  5. Bandwidth Control: Implement bandwidth throttling if needed

User Experience Best Practices

  1. Clear Instructions: Provide clear download instructions
  2. Progress Feedback: Show download progress and estimated time
  3. Error Handling: Handle download errors gracefully
  4. File Organization: Organize downloads by date and type
  5. Help Resources: Provide help for common download issues

Secure and efficient backup download management for WordPress.

On this page