using System.Security.Cryptography; using System.Text.Json; using Microsoft.EntityFrameworkCore; using RadicadeServer.DB; using RadicadeServer.Extensions; namespace RadicadeServer.API.Rooms { internal static class RoomEconomyPayload { private const string RawKey = "RoomEconomyPayload.Raw"; public static async Task ReadBodyAsync(HttpRequest request) { if (request.HasFormContentType) { try { var form = await request.ReadFormAsync(); if (form.Count > 0) { var fields = new Dictionary(); foreach (var field in form) fields[field.Key] = field.Value.ToString(); foreach (var file in form.Files) fields[file.Name] = file.FileName; return JsonSerializer.SerializeToElement(fields); } } catch (Exception ex) { request.HttpContext.Items[RawKey] = $"
"; } } string raw; using (var reader = new StreamReader(request.Body)) raw = await reader.ReadToEndAsync(); var text = raw.TrimStart('\uFEFF', ' ', '\t', '\r', '\n'); if (text.Length == 0) return null; if (text[0] is '{' or '[') { try { using var doc = JsonDocument.Parse(text); return doc.RootElement.Clone(); } catch (JsonException) { } } var pairs = ParsePairs(raw); if (pairs.Count > 0) { request.HttpContext.Items[RawKey] = raw; return JsonSerializer.SerializeToElement(pairs); } request.HttpContext.Items[RawKey] = raw; return null; } public static string RawBody(HttpRequest request) => request.HttpContext.Items.TryGetValue(RawKey, out var raw) ? raw as string ?? "" : ""; public static JsonElement? Value(JsonElement? body, params string[] names) { if (body == null) return null; var stack = new Stack(); stack.Push(body.Value); var visited = 0; while (stack.Count > 0 && visited++ < 2000) { var element = stack.Pop(); if (element.ValueKind == JsonValueKind.Array) { foreach (var item in element.EnumerateArray()) stack.Push(item); continue; } if (element.ValueKind != JsonValueKind.Object) continue; foreach (var property in element.EnumerateObject()) { if (property.Value.ValueKind is JsonValueKind.Object or JsonValueKind.Array) { stack.Push(property.Value); continue; } if (Matches(property.Name, names)) return property.Value; } } return null; } public static string? String(JsonElement? body, params string[] names) { var value = Value(body, names); if (value == null) return null; if (value.Value.ValueKind == JsonValueKind.String) return value.Value.GetString(); if (value.Value.ValueKind is JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False) return value.Value.ToString(); return null; } public static long? Long(JsonElement? body, params string[] names) { var value = Value(body, names); if (value == null) return null; if (value.Value.ValueKind == JsonValueKind.Number) { if (value.Value.TryGetInt64(out var number)) return number; if (value.Value.TryGetDouble(out var real)) return (long)real; } if (value.Value.ValueKind == JsonValueKind.String && long.TryParse(value.Value.GetString(), out var parsed)) return parsed; return null; } public static bool? Bool(JsonElement? body, params string[] names) { var value = Value(body, names); if (value == null) return null; if (value.Value.ValueKind == JsonValueKind.True) return true; if (value.Value.ValueKind == JsonValueKind.False) return false; return null; } public static long? LongFromRequest(HttpRequest request, params string[] names) { foreach (var pair in request.Query) { if (!Matches(pair.Key, names)) continue; if (long.TryParse(pair.Value.FirstOrDefault(), out var fromQuery)) return fromQuery; } foreach (var pair in request.RouteValues) { if (!Matches(pair.Key, names)) continue; if (long.TryParse(pair.Value?.ToString(), out var fromRoute)) return fromRoute; } return null; } public static Dictionary Fields(JsonElement? body) { var fields = new Dictionary(StringComparer.OrdinalIgnoreCase); if (body == null || body.Value.ValueKind != JsonValueKind.Object) return fields; foreach (var property in body.Value.EnumerateObject()) { fields[property.Name] = property.Value.ValueKind switch { JsonValueKind.String => property.Value.GetString(), JsonValueKind.Number => property.Value.TryGetInt64(out var number) ? number : property.Value.GetDouble(), JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Null => null, _ => property.Value.Clone() }; } return fields; } public static async Task RoomIdAsync(RecDBContext db, HttpRequest request, JsonElement? body, params string[] names) { var roomId = Long(body, names) ?? LongFromRequest(request, names); if (roomId is > 0) return roomId; var accountId = request.HttpContext.User.GetUserId(); if (accountId is not > 0) return null; var presence = await db.Presences.AsNoTracking() .FirstOrDefaultAsync(item => item.playerId == accountId.Value); if (presence?.roomInstance is not > 0) return null; return await db.RoomInstances.AsNoTracking() .Where(instance => instance.roomInstanceId == presence.roomInstance.Value) .Select(instance => (long?)instance.roomId) .FirstOrDefaultAsync(); } public static string NewId() { var bytes = RandomNumberGenerator.GetBytes(16); return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_').TrimEnd('='); } private static Dictionary ParsePairs(string raw) { var pairs = new Dictionary(); foreach (var pair in raw.Split('&', StringSplitOptions.RemoveEmptyEntries)) { var at = pair.IndexOf('='); var key = at < 0 ? pair : pair[..at]; var value = at < 0 ? "" : pair[(at + 1)..]; key = key.Trim(); if (key.Length == 0) continue; try { pairs[Uri.UnescapeDataString(key.Replace('+', ' '))] = Uri.UnescapeDataString(value.Replace('+', ' ')); } catch (UriFormatException) { pairs[key] = value; } } return pairs; } private static bool Matches(string name, string[] names) { foreach (var wanted in names) { if (string.Equals(Normalize(name), Normalize(wanted), StringComparison.Ordinal)) return true; } return false; } private static string Normalize(string name) => string.Concat(name.Where(char.IsLetterOrDigit)).ToLowerInvariant(); } }