true, CURLOPT_FAILONERROR => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, CURLOPT_TIMEOUT => FETCH_TIMEOUT_SECONDS, ]); $content = curl_exec($curl); return is_string($content) ? $content : false; } function escapeForDoubleQuotedShellString(string $value): string { return str_replace(['\\', '"', '$', '`'], ['\\\\', '\\"', '\\$', '\\`'], $value); } function resolveHomePath(string $path): string { if ($path === '~' || str_starts_with($path, '~/')) { return '$HOME' . escapeForDoubleQuotedShellString(substr($path, 1)); } return escapeForDoubleQuotedShellString($path); } function renderScriptTemplate(string $templateName, array $placeholders): string|false { $path = TEMPLATES_DIRECTORY . '/' . $templateName . '.sh.tpl'; if (!is_file($path)) { return false; } $script = file_get_contents($path); foreach ($placeholders as $key => $value) { $script = str_replace('{{' . $key . '}}', $value, $script); } return $script; } $repoRawBaseUrl = getenv('REPO_RAW_BASE_URL'); if ($repoRawBaseUrl === false || $repoRawBaseUrl === '') { sendPlainTextError(500, 'REPO_RAW_BASE_URL is not configured'); } define('REPO_RAW_BASE_URL', $repoRawBaseUrl); $publicBaseUrl = getenv('PUBLIC_BASE_URL'); if ($publicBaseUrl === false || $publicBaseUrl === '') { sendPlainTextError(500, 'PUBLIC_BASE_URL is not configured'); } define('PUBLIC_BASE_URL', $publicBaseUrl); $requestPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?? '/'; $name = trim(rawurldecode($requestPath), '/'); if ($name === '') { sendPlainTextError(400, 'Missing name of the installable item'); } $isRawRequest = str_starts_with($name, 'raw/'); if ($isRawRequest) { $name = substr($name, strlen('raw/')); } $repoHost = parse_url(REPO_RAW_BASE_URL, PHP_URL_HOST); $manifestYaml = fetchRaw(MANIFEST_PATH); if ($manifestYaml === false) { sendPlainTextError(502, "Failed to fetch the manifest from $repoHost"); } $manifest = yaml_parse($manifestYaml); if ($manifest === false) { sendPlainTextError(502, "Failed to parse the manifest from $repoHost"); } $entry = $manifest[$name] ?? null; if ($entry === null) { sendPlainTextError(404, "Unknown item '$name'"); } if (!is_string($entry['source'] ?? null) || !is_string($entry['target'] ?? null) || !is_string($entry['template'] ?? null)) { sendPlainTextError(500, "Malformed manifest entry for item '$name'"); } if ($isRawRequest) { $content = fetchRaw($entry['source']); if ($content === false) { sendPlainTextError(502, "Failed to fetch the file from $repoHost"); } header('Content-Type: application/octet-stream'); echo $content; exit; } $variables = [ 'SOURCE_URL' => rtrim(PUBLIC_BASE_URL, '/') . '/raw/' . rawurlencode($name), 'TARGET_PATH' => resolveHomePath($entry['target']), 'POST_INSTALL' => $entry['post_install'] ?? '', ]; $script = renderScriptTemplate($entry['template'], $variables); if ($script === false) { sendPlainTextError(500, 'Template for this item is missing'); } header('Content-Type: text/x-shellscript; charset=utf-8'); echo $script;