manifest.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. declare(strict_types=1);
  3. header("Content-Type: application/json; charset=utf-8");
  4. header("Cache-Control: no-store");
  5. $baseDir = __DIR__;
  6. $manifestFile = $baseDir . "/manifest.json";
  7. function updateServerRespond(int $status, array $payload): void
  8. {
  9. http_response_code($status);
  10. echo json_encode(
  11. $payload,
  12. JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
  13. );
  14. exit;
  15. }
  16. function updateServerValidateVersion(string $version): bool
  17. {
  18. return preg_match('/^v\d+\.\d+\.\d+$/', $version) === 1;
  19. }
  20. function updateServerNormalizePackageUrl(string $version): string
  21. {
  22. $scheme = "http";
  23. if (
  24. (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off") ||
  25. (isset($_SERVER["SERVER_PORT"]) && (int) $_SERVER["SERVER_PORT"] === 443) ||
  26. strtolower((string) ($_SERVER["HTTP_X_FORWARDED_PROTO"] ?? "")) === "https"
  27. ) {
  28. $scheme = "https";
  29. }
  30. $host = $_SERVER["HTTP_HOST"] ?? "localhost";
  31. $scriptDir = rtrim(str_replace("\\", "/", dirname($_SERVER["SCRIPT_NAME"] ?? "")), "/");
  32. return $scheme .
  33. "://" .
  34. $host .
  35. ($scriptDir === "" ? "" : $scriptDir) .
  36. "/package.php?version=" .
  37. rawurlencode($version);
  38. }
  39. if (!is_file($manifestFile)) {
  40. updateServerRespond(500, ["error" => "Manifest file is missing."]);
  41. }
  42. $decoded = json_decode((string) file_get_contents($manifestFile), true);
  43. if (!is_array($decoded)) {
  44. updateServerRespond(500, ["error" => "Manifest file is not valid JSON."]);
  45. }
  46. $latest = trim((string) ($decoded["latest"] ?? ""));
  47. $releases = isset($decoded["releases"]) && is_array($decoded["releases"])
  48. ? $decoded["releases"]
  49. : [];
  50. if ($latest === "" || !updateServerValidateVersion($latest)) {
  51. updateServerRespond(404, ["error" => "No valid latest release is configured."]);
  52. }
  53. if (!isset($releases[$latest]) || !is_array($releases[$latest])) {
  54. updateServerRespond(404, ["error" => "Latest release entry is missing."]);
  55. }
  56. $release = $releases[$latest];
  57. $version = trim((string) ($release["version"] ?? $latest));
  58. $package = trim((string) ($release["package"] ?? ""));
  59. $sha256 = strtolower(trim((string) ($release["sha256"] ?? "")));
  60. $publishedAt = trim((string) ($release["published_at"] ?? ""));
  61. $size = isset($release["size"]) ? (int) $release["size"] : 0;
  62. if ($version !== $latest || !updateServerValidateVersion($version)) {
  63. updateServerRespond(500, ["error" => "Latest release version is invalid."]);
  64. }
  65. if ($package === "" || str_contains($package, "\0") || str_starts_with($package, "/")) {
  66. updateServerRespond(500, ["error" => "Latest release package path is invalid."]);
  67. }
  68. if (!preg_match('/^[a-f0-9]{64}$/', $sha256)) {
  69. updateServerRespond(500, ["error" => "Latest release checksum is invalid."]);
  70. }
  71. $packagePath = realpath($baseDir . "/" . $package);
  72. $packagesDir = realpath($baseDir . "/packages");
  73. if (
  74. $packagePath === false ||
  75. $packagesDir === false ||
  76. !str_starts_with($packagePath, $packagesDir . DIRECTORY_SEPARATOR) ||
  77. !is_file($packagePath)
  78. ) {
  79. updateServerRespond(404, ["error" => "Latest release package is missing."]);
  80. }
  81. if ($size <= 0) {
  82. $size = filesize($packagePath) ?: 0;
  83. }
  84. updateServerRespond(200, [
  85. "latest" => $latest,
  86. "version" => $version,
  87. "package_url" => updateServerNormalizePackageUrl($version),
  88. "sha256" => $sha256,
  89. "size" => $size,
  90. "published_at" => $publishedAt,
  91. ]);