$v) { if (strpos($k, 'HTTP_') === 0) { $name = str_replace(' ', '-', ucwords(strtolower( str_replace('_', ' ', substr($k, 5))))); $h[$name] = $v; } } return $h; } } $out = str_repeat('=', 72) . "\n"; $out .= sprintf("TIME: %s\n", date('c')); $out .= sprintf("FROM: %s:%s\n", $_SERVER['REMOTE_ADDR'] ?? '?', $_SERVER['REMOTE_PORT'] ?? '?'); $out .= sprintf("METHOD: %s\n", $_SERVER['REQUEST_METHOD'] ?? '?'); $out .= sprintf("URI: %s\n", $_SERVER['REQUEST_URI'] ?? '?'); $headers = getallheaders(); ksort($headers); $out .= "--- headers ---\n"; foreach ($headers as $name => $value) { $out .= sprintf(" %s: %s\n", $name, $value); } // Signature check. CloudStack sends the hex HMAC-SHA256 of the body, // keyed with the webhook secret, only when a secret is configured. $sigHeader = $headers['X-Cs-Signature'] ?? $headers['X-CS-Signature'] ?? null; if ($sigHeader !== null) { if ($secret === '') { $out .= "--- signature ---\n present, but no local secret set to verify against\n"; } else { $expected = hash_hmac('sha256', $raw, $secret); $ok = hash_equals($expected, trim($sigHeader)); $out .= "--- signature ---\n"; $out .= sprintf(" received: %s\n expected: %s\n result: %s\n", trim($sigHeader), $expected, $ok ? 'VALID' : 'MISMATCH'); } } if (!empty($_GET)) { $out .= "--- query parameters ---\n"; foreach ($_GET as $k => $v) { $out .= sprintf(" %s = %s\n", $k, is_array($v) ? json_encode($v) : $v); } } if (!empty($_POST)) { $out .= "--- post parameters ---\n"; foreach ($_POST as $k => $v) { $out .= sprintf(" %s = %s\n", $k, is_array($v) ? json_encode($v) : $v); } } if (!empty($_FILES)) { $out .= "--- uploaded files ---\n"; foreach ($_FILES as $field => $f) { $out .= sprintf(" %s: %s (%s, %d bytes)\n", $field, $f['name'] ?? '?', $f['type'] ?? '?', $f['size'] ?? 0); } } if ($raw !== '') { $out .= sprintf("--- body (%d bytes, %s) ---\n", strlen($raw), $_SERVER['CONTENT_TYPE'] ?? 'no content-type'); $decoded = json_decode($raw, true); if (json_last_error() === JSON_ERROR_NONE) { $out .= json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; } else { $out .= $raw . "\n"; } } $out .= "\n"; @mkdir(dirname($logFile), 0775, true); file_put_contents($logFile, $out, FILE_APPEND | LOCK_EX); header('Content-Type: application/json'); http_response_code(200); echo json_encode(['status' => 'received', 'bytes' => strlen($raw)]);