about summary refs log tree commit diff
diff options
context:
space:
mode:
m---------ArcaneLibs0
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs12
-rw-r--r--LibMatrix/Services/WellKnownResolverService.cs159
-rw-r--r--LibMatrix/StateEvent.cs37
4 files changed, 169 insertions, 39 deletions
diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject 9a3faf1e7088a3ea0d0b6997efdf229745630a7
+Subproject 174c07bba99f268bafcb15153e26ad63980d729
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index b68a891..0863284 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -139,11 +139,13 @@ public class GenericRoom {
         }
     }
 
-    public async Task<MessagesResponse> GetMessagesAsync(string from = "", int? limit = null, string dir = "b", string filter = "") {
+    public async Task<MessagesResponse> GetMessagesAsync(string from = "", int? limit = null, string dir = "b", string? filter = "", SyncFilter.EventFilter? filterObject = null) {
         var url = $"/_matrix/client/v3/rooms/{RoomId}/messages?dir={dir}";
         if (!string.IsNullOrWhiteSpace(from)) url += $"&from={from}";
         if (limit is not null) url += $"&limit={limit}";
-        if (!string.IsNullOrWhiteSpace(filter)) url += $"&filter={filter}";
+        if(filterObject is not null) url += $"&filter={filterObject.ToJson()}";
+        else if (!string.IsNullOrWhiteSpace(filter)) url += $"&filter={filter}";
+
         var res = await Homeserver.ClientHttpClient.GetFromJsonAsync<MessagesResponse>(url);
         return res;
     }
@@ -151,12 +153,12 @@ public class GenericRoom {
     /// <summary>
     /// Same as <see cref="GetMessagesAsync"/>, except keeps fetching more responses until the beginning of the room is found, or the target message limit is reached
     /// </summary>
-    public async IAsyncEnumerable<MessagesResponse> GetManyMessagesAsync(string from = "", int limit = 100, string dir = "b", string filter = "", bool includeState = true,
-        bool fixForward = false, int chunkSize = 100) {
+    public async IAsyncEnumerable<MessagesResponse> GetManyMessagesAsync(string from = "", int limit = int.MaxValue, string dir = "b", string? filter = "",
+        SyncFilter.EventFilter? filterObject = null, bool includeState = true, bool fixForward = false, int chunkSize = 100) {
         if (dir == "f" && fixForward) {
             var concat = new List<MessagesResponse>();
             while (true) {
-                var resp = await GetMessagesAsync(from, int.MaxValue, "b", filter);
+                var resp = await GetMessagesAsync(from, int.MaxValue, "b", filter, filterObject);
                 concat.Add(resp);
                 if (!includeState)
                     resp.State.Clear();
diff --git a/LibMatrix/Services/WellKnownResolverService.cs b/LibMatrix/Services/WellKnownResolverService.cs
new file mode 100644
index 0000000..0ba79b6
--- /dev/null
+++ b/LibMatrix/Services/WellKnownResolverService.cs
@@ -0,0 +1,159 @@
+using System.Diagnostics;
+using System.Text.Json.Serialization;
+using ArcaneLibs.Collections;
+using ArcaneLibs.Extensions;
+using LibMatrix.Extensions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace LibMatrix.Services;
+
+public class WellKnownResolverService {
+    private readonly MatrixHttpClient _httpClient = new();
+
+    private static readonly SemaphoreCache<ClientWellKnown> ClientWellKnownCache = new();
+    private static readonly SemaphoreCache<ServerWellKnown> ServerWellKnownCache = new();
+    // private static readonly SemaphoreCache<support> SupportWellKnownCache = new();
+    // private static readonly SemaphoreCache<WellKnownRecords> WellKnownCache = new();
+
+    private readonly ILogger<WellKnownResolverService> _logger;
+
+    public WellKnownResolverService(ILogger<WellKnownResolverService> logger) {
+        _logger = logger;
+        if (logger is NullLogger<WellKnownResolverService>) {
+            var stackFrame = new StackTrace(true).GetFrame(1);
+            Console.WriteLine(
+                $"WARN | Null logger provided to WellKnownResolverService!\n{stackFrame?.GetMethod()?.DeclaringType?.ToString() ?? "null"} at {stackFrame?.GetFileName() ?? "null"}:{stackFrame?.GetFileLineNumber().ToString() ?? "null"}");
+        }
+    }
+
+    public async Task<WellKnownRecords> TryResolveWellKnownRecords(string homeserver) {
+        WellKnownRecords records = new();
+        records.ClientWellKnown = await ClientWellKnownCache.TryGetOrAdd(homeserver, async () => {
+            _logger.LogTrace($"Resolving client well-known: {homeserver}");
+            ClientWellKnown? clientWellKnown = null;
+            // check if homeserver has a client well-known
+            try {
+
+                var wk = await _httpClient.TryGetFromJsonAsync<ClientWellKnown>($"{homeserver}/.well-known/matrix/client");
+            }
+            catch { }
+
+            // return clientWellKnown;
+
+            _logger.LogInformation("No client well-known for {server}...", homeserver);
+            return null;
+        });
+        return records;
+    } 
+
+    // public async Task<WellKnownUris> ResolveHomeserverFromWellKnown(string homeserver, bool enableClient = true, bool enableServer = true) {
+    //     ArgumentNullException.ThrowIfNull(homeserver);
+    //
+    //     return await WellKnownCache.GetOrAdd(homeserver, async () => {
+    //         _logger.LogTrace($"Resolving homeserver well-knowns: {homeserver}");
+    //         var client = enableClient ? _tryResolveClientEndpoint(homeserver) : null;
+    //         var server = enableServer ? _tryResolveServerEndpoint(homeserver) : null;
+    //
+    //         var res = new WellKnownUris();
+    //
+    //         if (client != null)
+    //             res.Client = (await client)?.TrimEnd('/') ?? throw new Exception($"Could not resolve client URL for {homeserver}.");
+    //
+    //         if (server != null)
+    //             res.Server = (await server)?.TrimEnd('/') ?? throw new Exception($"Could not resolve server URL for {homeserver}.");
+    //
+    //         _logger.LogInformation("Resolved well-knowns for {hs}: {json}", homeserver, res.ToJson(indent: false));
+    //         return res;
+    //     });
+    // }
+    //
+    // private async Task<string?> _tryResolveClientEndpoint(string homeserver) {
+    //     ArgumentNullException.ThrowIfNull(homeserver);
+    //     _logger.LogTrace("Resolving client well-known: {homeserver}", homeserver);
+    //     ClientWellKnown? clientWellKnown = null;
+    //     // check if homeserver has a client well-known
+    //     if (homeserver.StartsWith("https://")) {
+    //         clientWellKnown = await _httpClient.TryGetFromJsonAsync<ClientWellKnown>($"{homeserver}/.well-known/matrix/client");
+    //     }
+    //     else if (homeserver.StartsWith("http://")) {
+    //         clientWellKnown = await _httpClient.TryGetFromJsonAsync<ClientWellKnown>($"{homeserver}/.well-known/matrix/client");
+    //     }
+    //     else {
+    //         clientWellKnown ??= await _httpClient.TryGetFromJsonAsync<ClientWellKnown>($"https://{homeserver}/.well-known/matrix/client");
+    //         clientWellKnown ??= await _httpClient.TryGetFromJsonAsync<ClientWellKnown>($"http://{homeserver}/.well-known/matrix/client");
+    //
+    //         if (clientWellKnown is null) {
+    //             if (await _httpClient.CheckSuccessStatus($"https://{homeserver}/_matrix/client/versions"))
+    //                 return $"https://{homeserver}";
+    //             if (await _httpClient.CheckSuccessStatus($"http://{homeserver}/_matrix/client/versions"))
+    //                 return $"http://{homeserver}";
+    //         }
+    //     }
+    //
+    //     if (!string.IsNullOrWhiteSpace(clientWellKnown?.Homeserver.BaseUrl))
+    //         return clientWellKnown.Homeserver.BaseUrl;
+    //
+    //     _logger.LogInformation("No client well-known for {server}...", homeserver);
+    //     return null;
+    // }
+    //
+    // private async Task<string?> _tryResolveServerEndpoint(string homeserver) {
+    //     // TODO: implement SRV delegation via DoH: https://developers.google.com/speed/public-dns/docs/doh/json
+    //     ArgumentNullException.ThrowIfNull(homeserver);
+    //     _logger.LogTrace($"Resolving server well-known: {homeserver}");
+    //     ServerWellKnown? serverWellKnown = null;
+    //     // check if homeserver has a server well-known
+    //     if (homeserver.StartsWith("https://")) {
+    //         serverWellKnown = await _httpClient.TryGetFromJsonAsync<ServerWellKnown>($"{homeserver}/.well-known/matrix/server");
+    //     }
+    //     else if (homeserver.StartsWith("http://")) {
+    //         serverWellKnown = await _httpClient.TryGetFromJsonAsync<ServerWellKnown>($"{homeserver}/.well-known/matrix/server");
+    //     }
+    //     else {
+    //         serverWellKnown ??= await _httpClient.TryGetFromJsonAsync<ServerWellKnown>($"https://{homeserver}/.well-known/matrix/server");
+    //         serverWellKnown ??= await _httpClient.TryGetFromJsonAsync<ServerWellKnown>($"http://{homeserver}/.well-known/matrix/server");
+    //     }
+    //
+    //     _logger.LogInformation("Server well-known for {hs}: {json}", homeserver, serverWellKnown?.ToJson() ?? "null");
+    //
+    //     if (!string.IsNullOrWhiteSpace(serverWellKnown?.Homeserver)) {
+    //         var resolved = serverWellKnown.Homeserver;
+    //         if (resolved.StartsWith("https://") || resolved.StartsWith("http://"))
+    //             return resolved;
+    //         if (await _httpClient.CheckSuccessStatus($"https://{resolved}/_matrix/federation/v1/version"))
+    //             return $"https://{resolved}";
+    //         if (await _httpClient.CheckSuccessStatus($"http://{resolved}/_matrix/federation/v1/version"))
+    //             return $"http://{resolved}";
+    //         _logger.LogWarning("Server well-known points to invalid server: {resolved}", resolved);
+    //     }
+    //
+    //     // fallback: most servers host C2S and S2S on the same domain
+    //     var clientUrl = await _tryResolveClientEndpoint(homeserver);
+    //     if (clientUrl is not null && await _httpClient.CheckSuccessStatus($"{clientUrl}/_matrix/federation/v1/version"))
+    //         return clientUrl;
+    //
+    //     _logger.LogInformation("No server well-known for {server}...", homeserver);
+    //     return null;
+    // }
+
+    public class ClientWellKnown {
+        [JsonPropertyName("m.homeserver")]
+        public WellKnownHomeserver Homeserver { get; set; }
+
+        public class WellKnownHomeserver {
+            [JsonPropertyName("base_url")]
+            public string BaseUrl { get; set; }
+        }
+    }
+
+    public class ServerWellKnown {
+        [JsonPropertyName("m.server")]
+        public string Homeserver { get; set; }
+    }
+    
+    public class WellKnownRecords {
+        public ClientWellKnown? ClientWellKnown { get; set; }
+        public ServerWellKnown? ServerWellKnown { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index cc870e4..6d8f195 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -107,37 +107,6 @@ public class StateEvent {
         get => _rawContent;
         set => _rawContent = value;
     }
-    //
-    // [JsonIgnore]
-    // public new Type GetType {
-    //     get {
-    //         var type = GetStateEventType(Type);
-    //
-    //         //special handling for some types
-    //         // if (type == typeof(RoomEmotesEventContent)) {
-    //         //     RawContent["emote"] = RawContent["emote"]?.AsObject() ?? new JsonObject();
-    //         // }
-    //         //
-    //         // if (this is StateEventResponse stateEventResponse) {
-    //         //     if (type == null || type == typeof(object)) {
-    //         //         Console.WriteLine($"Warning: unknown event type '{Type}'!");
-    //         //         Console.WriteLine(RawContent.ToJson());
-    //         //         Directory.CreateDirectory($"unknown_state_events/{Type}");
-    //         //         File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json",
-    //         //             RawContent.ToJson());
-    //         //         Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json");
-    //         //     }
-    //         //     else if (RawContent is not null && RawContent.FindExtraJsonObjectFields(type)) {
-    //         //         Directory.CreateDirectory($"unknown_state_events/{Type}");
-    //         //         File.WriteAllText($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json",
-    //         //             RawContent.ToJson());
-    //         //         Console.WriteLine($"Saved to unknown_state_events/{Type}/{stateEventResponse.EventId}.json");
-    //         //     }
-    //         // }
-    //
-    //         return type;
-    //     }
-    // }
 
     //debug
     [JsonIgnore]
@@ -264,8 +233,6 @@ public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoR
 }
 */
 
-#endregion
-
 /*
 public class ForgivingObjectConverter<T> : JsonConverter<T> where T : new() {
     public override T? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) {
@@ -283,4 +250,6 @@ public class ForgivingObjectConverter<T> : JsonConverter<T> where T : new() {
 
     public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
         => JsonSerializer.Serialize<T>(writer, value, options);
-}*/
\ No newline at end of file
+}*/
+
+#endregion
\ No newline at end of file