package.php 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. <?php
  2. declare(strict_types=1);
  3. $baseDir = __DIR__;
  4. $manifestFile = $baseDir . "/manifest.json";
  5. function updateServerPackageError(int $status, string $message): void
  6. {
  7. http_response_code($status);
  8. header("Content-Type: text/plain; charset=utf-8");
  9. echo $message;
  10. exit;
  11. }
  12. function updateServerPackageValidVersion(string $version): bool
  13. {
  14. return preg_match('/^v\d+\.\d+\.\d+$/', $version) === 1;
  15. }
  16. $version = trim((string) ($_GET["version"] ?? ""));
  17. if (!updateServerPackageValidVersion($version)) {
  18. updateServerPackageError(400, "Invalid version.");
  19. }
  20. if (!is_file($manifestFile)) {
  21. updateServerPackageError(500, "Manifest file is missing.");
  22. }
  23. $decoded = json_decode((string) file_get_contents($manifestFile), true);
  24. if (!is_array($decoded)) {
  25. updateServerPackageError(500, "Manifest file is not valid JSON.");
  26. }
  27. $releases = isset($decoded["releases"]) && is_array($decoded["releases"])
  28. ? $decoded["releases"]
  29. : [];
  30. if (!isset($releases[$version]) || !is_array($releases[$version])) {
  31. updateServerPackageError(404, "Release is not configured.");
  32. }
  33. $release = $releases[$version];
  34. $releaseVersion = trim((string) ($release["version"] ?? $version));
  35. $sha256 = strtolower(trim((string) ($release["sha256"] ?? "")));
  36. $package = trim((string) ($release["package"] ?? ""));
  37. if ($releaseVersion !== $version || !updateServerPackageValidVersion($releaseVersion)) {
  38. updateServerPackageError(500, "Release version is invalid.");
  39. }
  40. if (!preg_match('/^[a-f0-9]{64}$/', $sha256)) {
  41. updateServerPackageError(500, "Release checksum is invalid.");
  42. }
  43. if ($package === "" || str_contains($package, "\0") || str_starts_with($package, "/")) {
  44. updateServerPackageError(500, "Release package path is invalid.");
  45. }
  46. $packagePath = realpath($baseDir . "/" . $package);
  47. $packagesDir = realpath($baseDir . "/packages");
  48. if (
  49. $packagePath === false ||
  50. $packagesDir === false ||
  51. !str_starts_with($packagePath, $packagesDir . DIRECTORY_SEPARATOR) ||
  52. !is_file($packagePath)
  53. ) {
  54. updateServerPackageError(404, "Release package is missing.");
  55. }
  56. if (strtolower(pathinfo($packagePath, PATHINFO_EXTENSION)) !== "zip") {
  57. updateServerPackageError(500, "Release package is not a ZIP file.");
  58. }
  59. $fileName = basename($packagePath);
  60. $fileSize = filesize($packagePath);
  61. $handle = fopen($packagePath, "rb");
  62. if ($fileSize === false || $handle === false) {
  63. updateServerPackageError(500, "Release package cannot be opened.");
  64. }
  65. header("Content-Type: application/zip");
  66. header("Content-Disposition: attachment; filename=\"" . addcslashes($fileName, "\"\\") . "\"");
  67. header("Content-Length: " . (string) $fileSize);
  68. header("Cache-Control: public, max-age=300");
  69. header("X-Content-Type-Options: nosniff");
  70. fpassthru($handle);
  71. fclose($handle);