public static function handle_download_request(): void { $track_id = (int) get_query_var('mcs_dl_track'); $type = (string) get_query_var('mcs_dl_type'); $att_id = (int) get_query_var('mcs_dl_att'); if ($track_id <= 0 || $type === '') return; $type = sanitize_key($type); // Whitelist supported types $allowed_types = ['preview', 'hq', 'stems', 'clearance']; if (!in_array($type, $allowed_types, true)) { self::deny(404, 'Invalid download type'); } // Attachment id is only allowed for clearance if ($att_id > 0 && $type !== 'clearance') { self::deny(404, 'Invalid download request'); } // Validate track $track = get_post($track_id); $tracks_cpt = defined('MCS_CPT_TRACKS') ? MCS_CPT_TRACKS : 'tracks'; if (!$track || $track->post_type !== $tracks_cpt) { self::deny(404, 'Track not found'); } // Require login (all downloads protected) if (!is_user_logged_in()) { self::deny(403, 'Login required', 'not_logged_in', ['track_id'=>$track_id,'type'=>$type]); } $user_id = get_current_user_id(); // 0) Membership gate applies to EVERYONE (including owners/delegates) if (!self::user_has_membership($user_id)) { self::deny(403, 'Active membership required', 'no_membership', [ 'track_id' => $track_id, 'type' => $type, ]); } // 1) Explicit PMPro revocation flag (applies to everyone) if (class_exists('MCS_Module_PMPro_Revocation') && MCS_Module_PMPro_Revocation::is_revoked($user_id)) { self::deny(403, 'Download access revoked due to membership status.', 'pmpro_revoked', [ 'track_id' => $track_id, 'type' => $type, ]); } // 2) Support path (owner/delegate): membership already enforced, purchase NOT required if (!$allow_owner_delegate) { // Customer path: must have purchase entitlement self::assert_purchase_entitlement($user_id, $track_id, $type); } // Stems requires stems license if ($type === 'stems' && !MCS_Module_Woo_Entitlements::user_has_track_entitlement($user_id, $track_id, 'stems')) { self::deny(403, 'Stems license required', 'stems_required', ['track_id'=>$track_id]); } // Clearance currently = any purchase required (same gate as above) // If you later add clearance-only SKUs, add a 'clearance' entitlement type here. } // ---- Rate limits (abuse protection; not a "download limit") ---- $policy = self::policy_for_type($type); if (class_exists('MCS_Module_Rate_Limit')) { if (!MCS_Module_Rate_Limit::allow($user_id, 'dl_' . $type, $policy['user_max'], $policy['window'])) { self::deny(429, 'Rate limit exceeded (user)', 'rate_limited_user', ['track_id'=>$track_id,'type'=>$type]); } if (!MCS_Module_Rate_Limit::allow_ip('dl_' . $type, $policy['ip_max'], $policy['window'])) { self::deny(429, 'Rate limit exceeded (ip)', 'rate_limited_ip', ['track_id'=>$track_id,'type'=>$type]); } } // Resolve file $file = self::resolve_file($track_id, $type, $att_id); if (empty($file['path']) || !file_exists($file['path'])) { self::deny(404, 'File not found'); } $ent_row = null; if (!$allow_owner_delegate) { $ent_row = self::get_active_entitlement_row($user_id, $track_id); } if (class_exists('MCS_Logger')) { MCS_Logger::info('download_served', [ 'user_id' => $user_id, 'track_id' => $track_id, 'type' => $type, 'att_id' => ($att_id ?: null), 'file' => basename($file['path']), 'bypass' => $allow_owner_delegate ? 1 : 0, 'entitlement_id' => $ent_row ? (int)$ent_row['id'] : null, 'order_id' => $ent_row ? (int)$ent_row['order_id'] : null, 'license_type' => $ent_row ? (string)$ent_row['license_type'] : null, 'includes_stems' => $ent_row ? (int)$ent_row['includes_stems'] : null, 'includes_samples'=> $ent_row ? (int)$ent_row['includes_samples'] : null, ]); } self::force_download($file['path'], $file['download_name']); }