array( "Number" => array("Number", "get", "int"), ), "wordlets" => array("download"), "user_fields" => "", "regonly" => 0, "admin_only" => 0, "admin_or_mod" => 0, ); } function page_download_run() { global $userob, $user, $in, $ubbt_lang, $config, $forumvisit, $visit, $dbh, $html; // Safely extract GET parameters extract($in, EXTR_OVERWRITE | EXTR_REFS); // Validate numeric file ID $Number = isset($Number) ? (int)$Number : 0; if ($Number <= 0) { header('HTTP/1.1 400 Bad Request'); exit('Invalid file ID'); } // ------------------------------------------------ // Fetch file metadata (forum, name, type, original name) $query = " SELECT t1.FORUM_ID, t3.FILE_NAME, t3.FILE_TYPE, t3.FILE_ORIGINAL_NAME FROM {$config['TABLE_PREFIX']}TOPICS AS t1, {$config['TABLE_PREFIX']}POSTS AS t2, {$config['TABLE_PREFIX']}FILES AS t3 WHERE t2.POST_ID = t3.POST_ID AND t1.TOPIC_ID = t2.TOPIC_ID AND t3.FILE_ID = ? "; $sth = $dbh->do_placeholder_query($query, array($Number), __LINE__, __FILE__); list($board, $file, $file_extension, $file_orig) = $dbh->fetch_array($sth); if (!$file) { header('HTTP/1.1 404 Not Found'); exit('File not found'); } // -------------------------------------------------------- // Verify download permission if (!$userob->check_access("forum", "CAN_DOWNLOAD", $board)) { $html->not_right($ubbt_lang['NO_DOWNLOAD']); } // -------------------------------------------------------- // Increment download counter $query = " UPDATE {$config['TABLE_PREFIX']}FILES SET FILE_DOWNLOADS = FILE_DOWNLOADS + 1 WHERE FILE_ID = ? "; $dbh->do_placeholder_query($query, array($Number), __LINE__, __FILE__); // -------------------------------------------------------- // PATCH: Separate filesystem filename and URL filename // - $file_fs: real filename in filesystem (may contain spaces/UTF-8) // - $file_url: URL-encoded version for redirects // -------------------------------------------------------- $file_fs = (string)$file; $file_url = rawurlencode((string)$file); $path_fs = rtrim($config['ATTACHMENTS_PATH'], '/')."/".$file_fs; // -------------------------------------------------------- // PATCH: Prevent path traversal attacks // -------------------------------------------------------- if (basename($file_fs) !== $file_fs) { header('HTTP/1.1 400 Bad Request'); exit('Invalid filename'); } // -------------------------------------------------------- // PATCH: Ensure file exists; attempt encoding fallback // (UTF-8 ↔ ISO-8859-1 transliteration) // -------------------------------------------------------- if (!is_file($path_fs)) { // Try UTF-8 → Latin-1 $file_fs_latin1 = @iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $file_fs); if ($file_fs_latin1 && $file_fs_latin1 !== $file_fs) { $cand = rtrim($config['ATTACHMENTS_PATH'], '/')."/".$file_fs_latin1; if (is_file($cand)) { $file_fs = $file_fs_latin1; $path_fs = $cand; } } // Try Latin-1 → UTF-8 (if still not found) if (!is_file($path_fs)) { $file_fs_utf8 = @iconv('ISO-8859-1', 'UTF-8//TRANSLIT', $file_fs); if ($file_fs_utf8 && $file_fs_utf8 !== $file_fs) { $cand = rtrim($config['ATTACHMENTS_PATH'], '/')."/".$file_fs_utf8; if (is_file($cand)) { $file_fs = $file_fs_utf8; $path_fs = $cand; } } } // Still not found → 404 if (!is_file($path_fs)) { header('HTTP/1.1 404 Not Found'); exit('File not found'); } } // -------------------------------------------------------- // PATCH: Set caching headers and handle 304 Not Modified // -------------------------------------------------------- $mtime = @filemtime($path_fs); if ($mtime === false) { $mtime = time(); } // Check client cache validation header $ifMod = null; if (function_exists('apache_request_headers')) { $hdrs = apache_request_headers(); if (is_array($hdrs)) { $hdrs = array_change_key_case($hdrs, CASE_LOWER); if (isset($hdrs['if-modified-since'])) { $ifMod = $hdrs['if-modified-since']; } } } if (!$ifMod && isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $ifMod = $_SERVER['HTTP_IF_MODIFIED_SINCE']; } $lastModStr = gmdate('D, d M Y H:i:s', $mtime).' GMT'; if ($ifMod && (strtotime($ifMod) === $mtime)) { header('Last-Modified: '.$lastModStr, true, 304); exit; } header('Last-Modified: '.$lastModStr); // -------------------------------------------------------- // PATCH: Build RFC 6266-compliant filename header // - ASCII fallback for legacy browsers // - UTF-8 version via filename* // -------------------------------------------------------- $file_orig = (string)$file_orig; $file_orig_display = preg_replace("/ +/", "-", $file_orig); $file_orig_utf8 = $file_orig_display; $file_orig_ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $file_orig_utf8); if ($file_orig_ascii === false || $file_orig_ascii === '') { $file_orig_ascii = 'download'; } $disposition_param = "filename=\"{$file_orig_ascii}\"; filename*=UTF-8''".rawurlencode($file_orig_utf8); // -------------------------------------------------------- // Determine content type // -------------------------------------------------------- $ext = strtolower((string)$file_extension); $isImage = in_array($ext, array('gif','jpg','jpeg','png'), true); // -------------------------------------------------------- // TXT files: redirect to static attachment URL // -------------------------------------------------------- if ($ext === 'txt') { header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private", false); header("Location: ".rtrim($config['ATTACHMENTS_URL'], '/')."/".$file_url); exit; } // -------------------------------------------------------- // Images: display inline // -------------------------------------------------------- if ($isImage) { $ctype = ($ext === 'jpg') ? 'image/jpeg' : 'image/'.$ext; header("Content-Type: {$ctype}"); header("Content-Disposition: inline; {$disposition_param}"); $len = @filesize($path_fs); if ($len !== false) { header("Content-Length: ".$len); } readfile($path_fs); exit; } // -------------------------------------------------------- // All other files: force download // -------------------------------------------------------- header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; {$disposition_param}"); $len = @filesize($path_fs); if ($len !== false) { header("Content-Length: ".$len); } readfile($path_fs); exit(); }