<?php

if (!defined('ABSPATH')) {
   fwduvp_downloader_maybe_bootstrap_wordpress();
}

if (!defined('ABSPATH')) {
   exit;
}

if (!function_exists('WP_Filesystem') || !function_exists('wp_unslash')) {
   fwduvp_downloader_maybe_bootstrap_wordpress();
}

$fwduvp_raw_nonce = filter_input(INPUT_GET, 'fwduvp_nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE);
$fwduvp_raw_nonce = $fwduvp_raw_nonce === null || $fwduvp_raw_nonce === false ? '' : (string) $fwduvp_raw_nonce;
$fwduvp_raw_nonce = function_exists('wp_unslash') ? wp_unslash($fwduvp_raw_nonce) : $fwduvp_raw_nonce;
$fwduvp_nonce = function_exists('sanitize_text_field') ? sanitize_text_field($fwduvp_raw_nonce) : trim($fwduvp_raw_nonce);
$fwduvp_nonce_is_valid = function_exists('wp_verify_nonce') && $fwduvp_nonce !== '' && wp_verify_nonce($fwduvp_nonce, 'fwduvp_download_video');

if (!$fwduvp_nonce_is_valid) {
   http_response_code(403);
   exit;
}

if (function_exists('is_user_logged_in') && is_user_logged_in() && function_exists('current_user_can') && !current_user_can('read')) {
   http_response_code(403);
   exit;
}

$fwduvp_raw_name = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE);
$fwduvp_raw_path = filter_input(INPUT_GET, 'path', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE);

if ($fwduvp_raw_name === null || $fwduvp_raw_name === false || $fwduvp_raw_path === null || $fwduvp_raw_path === false) {
   http_response_code(400);
   exit;
}

$fwduvp_name = function_exists('wp_unslash') ? wp_unslash((string) $fwduvp_raw_name) : (string) $fwduvp_raw_name;
$fwduvp_path = function_exists('wp_unslash') ? wp_unslash((string) $fwduvp_raw_path) : (string) $fwduvp_raw_path;

$fwduvp_name = trim(str_replace("\0", '', $fwduvp_name));
$fwduvp_path = trim(str_replace("\0", '', $fwduvp_path));

if ($fwduvp_name === '' || $fwduvp_path === '') {
   http_response_code(400);
   exit;
}

$fwduvp_name = function_exists('sanitize_file_name') ? sanitize_file_name($fwduvp_name) : basename($fwduvp_name);
if ($fwduvp_name === '') {
   $fwduvp_name = 'download.bin';
}

$fwduvp_path = fwduvp_downloader_prepare_path($fwduvp_path);

if (fwduvp_downloader_is_http_url($fwduvp_path)) {
   if (!fwduvp_downloader_is_allowed_remote_url($fwduvp_path)) {
      http_response_code(403);
      exit;
   }
} elseif (!fwduvp_downloader_is_allowed_local_path($fwduvp_path)) {
   http_response_code(403);
   exit;
}

$fwduvp_stream_file_path = fwduvp_downloader_get_stream_file_path($fwduvp_path, $fwduvp_name);
if ($fwduvp_stream_file_path === false) {
   http_response_code(404);
   exit;
}

$fwduvp_is_temp_stream_file = fwduvp_downloader_is_http_url($fwduvp_path);
$fwduvp_content_length = @filesize($fwduvp_stream_file_path);

header('Content-Type: application/octet-stream');
header('X-Content-Type-Options: nosniff');
header('Content-Disposition: attachment; filename="' . $fwduvp_name . '"');
if ($fwduvp_content_length !== false) {
   header('Content-Length: ' . (string) $fwduvp_content_length);
}

if (!fwduvp_downloader_output_file_stream($fwduvp_stream_file_path)) {
   if ($fwduvp_is_temp_stream_file && is_file($fwduvp_stream_file_path)) {
      fwduvp_downloader_delete_file($fwduvp_stream_file_path);
   }
   http_response_code(404);
   exit;
}

if ($fwduvp_is_temp_stream_file && is_file($fwduvp_stream_file_path)) {
   fwduvp_downloader_delete_file($fwduvp_stream_file_path);
}

exit;

function fwduvp_downloader_prepare_path($fwduvp_path)
{
   $fwduvp_path_fragments = explode('/', $fwduvp_path);
   $fwduvp_first = '';
   $fwduvp_end = end($fwduvp_path_fragments);

   for ($fwduvp_i = 0; $fwduvp_i < count($fwduvp_path_fragments) - 1; $fwduvp_i++) {
      $fwduvp_first .= $fwduvp_path_fragments[$fwduvp_i] . '/';
   }

   $fwduvp_first = rawurlencode($fwduvp_first);
   $fwduvp_first = str_replace('%2F', '/', $fwduvp_first);
   $fwduvp_first = str_replace('%3A', ':', $fwduvp_first);

   return $fwduvp_first . rawurlencode((string) $fwduvp_end);
}

function fwduvp_downloader_maybe_bootstrap_wordpress()
{
   $fwduvp_search_dir = __DIR__;

   for ($fwduvp_depth = 0; $fwduvp_depth < 8; $fwduvp_depth++) {
      $fwduvp_candidate = $fwduvp_search_dir . DIRECTORY_SEPARATOR . 'wp-load.php';
      if (is_readable($fwduvp_candidate)) {
         require_once $fwduvp_candidate;
         return;
      }

      $fwduvp_parent_dir = dirname($fwduvp_search_dir);
      if ($fwduvp_parent_dir === $fwduvp_search_dir) {
         break;
      }

      $fwduvp_search_dir = $fwduvp_parent_dir;
   }
}

function fwduvp_downloader_get_stream_file_path($fwduvp_path, $fwduvp_name)
{
   if (fwduvp_downloader_is_http_url($fwduvp_path) && function_exists('wp_remote_get')) {
      $fwduvp_temp_file = function_exists('wp_tempnam') ? wp_tempnam($fwduvp_name) : tempnam(sys_get_temp_dir(), 'fwduvp_');
      if (!is_string($fwduvp_temp_file) || $fwduvp_temp_file === '') {
         return false;
      }

      $fwduvp_response = wp_remote_get(
         $fwduvp_path,
         array(
            'timeout' => 20,
            'redirection' => 3,
            'reject_unsafe_urls' => true,
            'stream' => true,
            'filename' => $fwduvp_temp_file,
         )
      );

      if (is_wp_error($fwduvp_response) || (int) wp_remote_retrieve_response_code($fwduvp_response) !== 200) {
         if (is_file($fwduvp_temp_file)) {
            fwduvp_downloader_delete_file($fwduvp_temp_file);
         }
         return false;
      }

      if (is_readable($fwduvp_temp_file)) {
         return $fwduvp_temp_file;
      }

      if (is_file($fwduvp_temp_file)) {
         fwduvp_downloader_delete_file($fwduvp_temp_file);
      }
      return false;
   }

   if (!fwduvp_downloader_is_http_url($fwduvp_path) && is_readable($fwduvp_path)) {
      return $fwduvp_path;
   }

   return false;
}

function fwduvp_downloader_output_file_stream($fwduvp_file_path)
{
	// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Streaming download output avoids loading large files into memory.
   $fwduvp_read_result = @readfile($fwduvp_file_path);
   if ($fwduvp_read_result === false) {
      return false;
   }
   return true;
}

function fwduvp_downloader_delete_file($fwduvp_file_path)
{
   if (!is_file($fwduvp_file_path)) {
      return;
   }

   if (function_exists('wp_delete_file')) {
      wp_delete_file($fwduvp_file_path);
   }
}

function fwduvp_downloader_is_http_url($fwduvp_path)
{
   $fwduvp_scheme = (string) wp_parse_url($fwduvp_path, PHP_URL_SCHEME);
   return $fwduvp_scheme === 'http' || $fwduvp_scheme === 'https';
}

function fwduvp_downloader_is_allowed_remote_url($fwduvp_url)
{
   if (!function_exists('wp_http_validate_url') || !wp_http_validate_url($fwduvp_url)) {
      return false;
   }

   if (fwduvp_downloader_is_same_site_url($fwduvp_url)) {
      return true;
   }

   $fwduvp_host = (string) wp_parse_url($fwduvp_url, PHP_URL_HOST);
   if ($fwduvp_host === '') {
      return false;
   }

   $fwduvp_port = wp_parse_url($fwduvp_url, PHP_URL_PORT);
   if ($fwduvp_port !== null && $fwduvp_port !== false && !in_array((int) $fwduvp_port, array(80, 443), true)) {
      return false;
   }

   $fwduvp_host = strtolower($fwduvp_host);
   if (in_array($fwduvp_host, array('localhost', '127.0.0.1', '::1'), true)) {
      return false;
   }

   if (filter_var($fwduvp_host, FILTER_VALIDATE_IP)) {
      return !fwduvp_downloader_is_private_ip($fwduvp_host);
   }

   $fwduvp_ips = function_exists('gethostbynamel') ? gethostbynamel($fwduvp_host) : false;
   if (is_array($fwduvp_ips)) {
      foreach ($fwduvp_ips as $fwduvp_ip) {
         if (fwduvp_downloader_is_private_ip($fwduvp_ip)) {
            return false;
         }
      }
   }

   return true;
}

function fwduvp_downloader_is_private_ip($fwduvp_ip)
{
   return filter_var($fwduvp_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
}

function fwduvp_downloader_is_same_site_url($fwduvp_url)
{
   $fwduvp_url_host = strtolower((string) wp_parse_url($fwduvp_url, PHP_URL_HOST));
   if ($fwduvp_url_host === '') {
      return false;
   }

   $fwduvp_site_host = strtolower((string) wp_parse_url(home_url('/'), PHP_URL_HOST));
   if ($fwduvp_site_host !== '' && $fwduvp_url_host === $fwduvp_site_host) {
      return true;
   }

   $fwduvp_http_host = filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE);
   if (is_string($fwduvp_http_host) && $fwduvp_http_host !== '') {
      $fwduvp_http_host = sanitize_text_field(wp_unslash($fwduvp_http_host));
      $fwduvp_current_parts = wp_parse_url('http://' . $fwduvp_http_host);
      $fwduvp_current_host = isset($fwduvp_current_parts['host']) ? strtolower((string) $fwduvp_current_parts['host']) : '';
      if ($fwduvp_current_host !== '' && $fwduvp_url_host === $fwduvp_current_host) {
         return true;
      }
   }

   return false;
}

function fwduvp_downloader_is_allowed_local_path($fwduvp_path)
{
   $fwduvp_real_path = realpath($fwduvp_path);
   if ($fwduvp_real_path === false) {
      return false;
   }

   if (!function_exists('wp_upload_dir')) {
      return false;
   }

   $fwduvp_uploads = wp_upload_dir(null, false);
   if (empty($fwduvp_uploads['basedir'])) {
      return false;
   }

   $fwduvp_uploads_base = realpath($fwduvp_uploads['basedir']);
   if ($fwduvp_uploads_base === false) {
      return false;
   }

   $fwduvp_real_norm = wp_normalize_path($fwduvp_real_path);
   $fwduvp_base_norm = rtrim(wp_normalize_path($fwduvp_uploads_base), '/');

   return $fwduvp_real_norm === $fwduvp_base_norm || strpos($fwduvp_real_norm, $fwduvp_base_norm . '/') === 0;
}
