about summary refs log tree commit diff
path: root/LibMatrix/Responses/Federation
diff options
context:
space:
mode:
Diffstat (limited to 'LibMatrix/Responses/Federation')
-rw-r--r--LibMatrix/Responses/Federation/ServerKeysResponse.cs55
-rw-r--r--LibMatrix/Responses/Federation/ServerVersionResponse.cs16
-rw-r--r--LibMatrix/Responses/Federation/SignedObject.cs68
3 files changed, 139 insertions, 0 deletions
diff --git a/LibMatrix/Responses/Federation/ServerKeysResponse.cs b/LibMatrix/Responses/Federation/ServerKeysResponse.cs
new file mode 100644

index 0000000..cb62e34 --- /dev/null +++ b/LibMatrix/Responses/Federation/ServerKeysResponse.cs
@@ -0,0 +1,55 @@ +using System.Diagnostics; +using System.Text.Json.Serialization; +using LibMatrix.Abstractions; + +namespace LibMatrix.Responses.Federation; + +public class ServerKeysResponse { + [JsonPropertyName("server_name")] + public string ServerName { get; set; } + + [JsonPropertyName("valid_until_ts")] + public ulong ValidUntilTs { get; set; } + + [JsonIgnore] + public DateTime ValidUntil { + get => DateTimeOffset.FromUnixTimeMilliseconds((long)ValidUntilTs).DateTime; + set => ValidUntilTs = (ulong)new DateTimeOffset(value).ToUnixTimeMilliseconds(); + } + + [JsonPropertyName("verify_keys")] + public Dictionary<string, CurrentVerifyKey> VerifyKeys { get; set; } = new(); + + [JsonIgnore] + public Dictionary<VersionedKeyId, CurrentVerifyKey> VerifyKeysById { + get => VerifyKeys.ToDictionary(key => (VersionedKeyId)key.Key, key => key.Value); + set => VerifyKeys = value.ToDictionary(key => (string)key.Key, key => key.Value); + } + + [JsonPropertyName("old_verify_keys")] + public Dictionary<string, ExpiredVerifyKey> OldVerifyKeys { get; set; } = new(); + + [JsonIgnore] + public Dictionary<VersionedKeyId, ExpiredVerifyKey> OldVerifyKeysById { + get => OldVerifyKeys.ToDictionary(key => (VersionedKeyId)key.Key, key => key.Value); + set => OldVerifyKeys = value.ToDictionary(key => (string)key.Key, key => key.Value); + } + + [DebuggerDisplay("{Key}")] + public class CurrentVerifyKey { + [JsonPropertyName("key")] + public string Key { get; set; } + } + + [DebuggerDisplay("{Key} (expired {Expired})")] + public class ExpiredVerifyKey : CurrentVerifyKey { + [JsonPropertyName("expired_ts")] + public ulong ExpiredTs { get; set; } + + [JsonIgnore] + public DateTime Expired { + get => DateTimeOffset.FromUnixTimeMilliseconds((long)ExpiredTs).DateTime; + set => ExpiredTs = (ulong)new DateTimeOffset(value).ToUnixTimeMilliseconds(); + } + } +} diff --git a/LibMatrix/Responses/Federation/ServerVersionResponse.cs b/LibMatrix/Responses/Federation/ServerVersionResponse.cs new file mode 100644
index 0000000..b09bdd0 --- /dev/null +++ b/LibMatrix/Responses/Federation/ServerVersionResponse.cs
@@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace LibMatrix.Responses.Federation; + +public class ServerVersionResponse { + [JsonPropertyName("server")] + public required ServerInfo Server { get; set; } + + public class ServerInfo { + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("version")] + public string Version { get; set; } + } +} \ No newline at end of file diff --git a/LibMatrix/Responses/Federation/SignedObject.cs b/LibMatrix/Responses/Federation/SignedObject.cs new file mode 100644
index 0000000..3f6ffd6 --- /dev/null +++ b/LibMatrix/Responses/Federation/SignedObject.cs
@@ -0,0 +1,68 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; +using LibMatrix.Abstractions; +using LibMatrix.Homeservers; + +namespace LibMatrix.Responses.Federation; + +[JsonConverter(typeof(SignedObjectConverterFactory))] +public class SignedObject<T> { + [JsonPropertyName("signatures")] + public Dictionary<string, Dictionary<string, string>> Signatures { get; set; } = new(); + + [JsonIgnore] + public Dictionary<string, Dictionary<VersionedKeyId, string>> SignaturesById { + get => Signatures.ToDictionary(server => server.Key, server => server.Value.ToDictionary(key => (VersionedKeyId)key.Key, key => key.Value)); + set => Signatures = value.ToDictionary(server => server.Key, server => server.Value.ToDictionary(key => (string)key.Key, key => key.Value)); + } + + [JsonExtensionData] + public required JsonObject Content { get; set; } + + [JsonIgnore] + public T TypedContent { + get => Content.Deserialize<T>() ?? throw new JsonException("Failed to deserialize TypedContent from Content."); + set => Content = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(value)) ?? new JsonObject(); + } +} + +public class SignedObjectConverter<T> : JsonConverter<SignedObject<T>> { + public override SignedObject<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var jsonObject = JsonSerializer.Deserialize<JsonObject>(ref reader, options); + if (jsonObject == null) { + throw new JsonException("Failed to deserialize SignedObject, JSON object is null."); + } + + var signatures = jsonObject["signatures"] ?? throw new JsonException("Failed to find 'signatures' property in JSON object."); + jsonObject.Remove("signatures"); + + var signedObject = new SignedObject<T> { + Content = jsonObject, + Signatures = signatures.Deserialize<Dictionary<string, Dictionary<string, string>>>() + ?? throw new JsonException("Failed to deserialize 'signatures' property into Dictionary<string, Dictionary<string, string>>.") + }; + + return signedObject; + } + + public override void Write(Utf8JsonWriter writer, SignedObject<T> value, JsonSerializerOptions options) { + var targetObj = value.Content.DeepClone(); + targetObj["signatures"] = value.Signatures.ToJsonNode(); + JsonSerializer.Serialize(writer, targetObj, options); + } +} + +internal class SignedObjectConverterFactory : JsonConverterFactory { + public override bool CanConvert(Type typeToConvert) { + if (!typeToConvert.IsGenericType) return false; + return typeToConvert.GetGenericTypeDefinition() == typeof(SignedObject<>); + } + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { + var wrappedType = typeToConvert.GetGenericArguments()[0]; + var converter = (JsonConverter)Activator.CreateInstance(typeof(SignedObjectConverter<>).MakeGenericType(wrappedType))!; + return converter; + } +} \ No newline at end of file