From 8673cb236f427ba6af6382e3b5702a134f1afe2e Mon Sep 17 00:00:00 2001 From: Rory& Date: Tue, 18 Feb 2025 07:40:41 +0100 Subject: MxcUri class, other various work --- .../Homeservers/AuthenticatedHomeserverGeneric.cs | 61 +++++++++++++--------- LibMatrix/Homeservers/FederationClient.cs | 4 +- LibMatrix/Homeservers/RemoteHomeServer.cs | 14 ++--- LibMatrix/Homeservers/UserInteractiveAuthClient.cs | 12 ++--- 4 files changed, 51 insertions(+), 40 deletions(-) (limited to 'LibMatrix/Homeservers') diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs index f47ab64..8c95bc3 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs @@ -39,11 +39,12 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { public string UserId => WhoAmI.UserId; public string UserLocalpart => UserId.Split(":")[0][1..]; public string ServerName => UserId.Split(":", 2)[1]; + public string BaseUrl => ClientHttpClient.BaseAddress!.ToString().TrimEnd('/'); [JsonIgnore] public string AccessToken { get; set; } - public HsNamedCaches NamedCaches { get; set; } = null!; + public HsNamedCaches NamedCaches { get; set; } public GenericRoom GetRoom(string roomId) { if (roomId is null || !roomId.StartsWith("!")) throw new ArgumentException("Room ID must start with !", nameof(roomId)); @@ -408,22 +409,22 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { #region Authenticated Media // TODO: implement /_matrix/client/v1/media/config when it's actually useful - https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediaconfig + private bool? _serverSupportsAuthMedia; - private (string ServerName, string MediaId) ParseMxcUri(string mxcUri) { - if (!mxcUri.StartsWith("mxc://")) throw new ArgumentException("Matrix Content URIs must start with 'mxc://'", nameof(mxcUri)); - var parts = mxcUri[6..].Split('/'); - if (parts.Length != 2) throw new ArgumentException($"Invalid Matrix Content URI '{mxcUri}' passed! Matrix Content URIs must exist of only 2 parts!", nameof(mxcUri)); - return (parts[0], parts[1]); - } + public async Task GetMediaUrlAsync(MxcUri mxcUri, string? filename = null, int? timeout = null) { + if (_serverSupportsAuthMedia == true) return mxcUri.ToDownloadUri(BaseUrl, filename, timeout); + if (_serverSupportsAuthMedia == false) return mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout); - public async Task GetMediaStreamAsync(string mxcUri, string? filename = null, int? timeout = null) { - var (serverName, mediaId) = ParseMxcUri(mxcUri); try { - var uri = $"/_matrix/client/v1/media/download/{serverName}/{mediaId}"; - if (!string.IsNullOrWhiteSpace(filename)) uri += $"/{HttpUtility.UrlEncode(filename)}"; - if (timeout is not null) uri += $"?timeout_ms={timeout}"; - var res = await ClientHttpClient.GetAsync(uri); - return await res.Content.ReadAsStreamAsync(); + // Console.WriteLine($"Trying authenticated media URL: {uri}"); + var res = await ClientHttpClient.SendAsync(new() { + Method = HttpMethod.Head, + RequestUri = (new Uri(mxcUri.ToDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute)) + }); + if (res.IsSuccessStatusCode) { + _serverSupportsAuthMedia = true; + return mxcUri.ToDownloadUri(BaseUrl, filename, timeout); + } } catch (MatrixException e) { if (e is not { ErrorCode: "M_UNKNOWN" }) throw; @@ -431,11 +432,15 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { //fallback to legacy media try { - var uri = $"/_matrix/media/v3/download/{serverName}/{mediaId}"; - if (!string.IsNullOrWhiteSpace(filename)) uri += $"/{HttpUtility.UrlEncode(filename)}"; - if (timeout is not null) uri += $"?timeout_ms={timeout}"; - var res = await ClientHttpClient.GetAsync(uri); - return await res.Content.ReadAsStreamAsync(); + // Console.WriteLine($"Trying legacy media URL: {uri}"); + var res = await ClientHttpClient.SendAsync(new() { + Method = HttpMethod.Head, + RequestUri = new(mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute) + }); + if (res.IsSuccessStatusCode) { + _serverSupportsAuthMedia = false; + return mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout); + } } catch (MatrixException e) { if (e is not { ErrorCode: "M_UNKNOWN" }) throw; @@ -443,13 +448,19 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { throw new LibMatrixException() { ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED, - Error = "Failed to download media" + Error = "Failed to get media URL" }; - // return default; } - public async Task GetThumbnailStreamAsync(string mxcUri, int width, int height, string? method = null, int? timeout = null) { - var (serverName, mediaId) = ParseMxcUri(mxcUri); + public async Task GetMediaStreamAsync(string mxcUri, string? filename = null, int? timeout = null) { + var uri = await GetMediaUrlAsync(mxcUri, filename, timeout); + var res = await ClientHttpClient.GetAsync(uri); + return await res.Content.ReadAsStreamAsync(); + } + + public async Task GetThumbnailStreamAsync(MxcUri mxcUri, int width, int height, string? method = null, int? timeout = null) { + var (serverName, mediaId) = mxcUri; + try { var uri = new Uri($"/_matrix/client/v1/thumbnail/{serverName}/{mediaId}"); uri = uri.AddQuery("width", width.ToString()); @@ -494,7 +505,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { catch (MatrixException e) { if (e is not { ErrorCode: "M_UNRECOGNIZED" }) throw; } - + //fallback to legacy media try { var res = await ClientHttpClient.GetAsync($"/_matrix/media/v3/preview_url?url={HttpUtility.UrlEncode(url)}"); @@ -503,7 +514,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { catch (MatrixException e) { if (e is not { ErrorCode: "M_UNRECOGNIZED" }) throw; } - + throw new LibMatrixException() { ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED, Error = "Failed to download URL preview" diff --git a/LibMatrix/Homeservers/FederationClient.cs b/LibMatrix/Homeservers/FederationClient.cs index c7f0240..617b737 100644 --- a/LibMatrix/Homeservers/FederationClient.cs +++ b/LibMatrix/Homeservers/FederationClient.cs @@ -13,8 +13,8 @@ public class FederationClient { if (proxy is not null) HttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", federationEndpoint); } - public MatrixHttpClient HttpClient { get; set; } = null!; - public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } = null!; + public MatrixHttpClient HttpClient { get; set; } + public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } public async Task GetServerVersionAsync() => await HttpClient.GetFromJsonAsync("/_matrix/federation/v1/version"); } diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs index 7995f03..7ac54a7 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs @@ -10,24 +10,24 @@ using LibMatrix.Services; namespace LibMatrix.Homeservers; public class RemoteHomeserver { - public RemoteHomeserver(string baseUrl, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { + public RemoteHomeserver(string serverName, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { if (string.IsNullOrWhiteSpace(proxy)) proxy = null; - BaseUrl = baseUrl; + ServerNameOrUrl = serverName; WellKnownUris = wellKnownUris; ClientHttpClient = new MatrixHttpClient { - BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {baseUrl}!")), + BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {serverName}!")), // Timeout = TimeSpan.FromSeconds(300) // TODO: Re-implement this }; - if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", baseUrl); + if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName); if (!string.IsNullOrWhiteSpace(wellKnownUris.Server)) FederationClient = new FederationClient(WellKnownUris.Server!, proxy); Auth = new(this); } private Dictionary _profileCache { get; set; } = new(); - public string BaseUrl { get; } + public string ServerNameOrUrl { get; } [JsonIgnore] public MatrixHttpClient ClientHttpClient { get; set; } @@ -111,8 +111,8 @@ public class RemoteHomeserver { public class AliasResult { [JsonPropertyName("room_id")] - public string RoomId { get; set; } = null!; + public string RoomId { get; set; } [JsonPropertyName("servers")] - public List Servers { get; set; } = null!; + public List Servers { get; set; } } \ No newline at end of file diff --git a/LibMatrix/Homeservers/UserInteractiveAuthClient.cs b/LibMatrix/Homeservers/UserInteractiveAuthClient.cs index bb3889f..8de01d2 100644 --- a/LibMatrix/Homeservers/UserInteractiveAuthClient.cs +++ b/LibMatrix/Homeservers/UserInteractiveAuthClient.cs @@ -49,27 +49,27 @@ public class UserInteractiveAuthClient { internal class RegisterFlowsResponse { [JsonPropertyName("session")] - public string Session { get; set; } = null!; + public string Session { get; set; } [JsonPropertyName("flows")] - public List Flows { get; set; } = null!; + public List Flows { get; set; } [JsonPropertyName("params")] - public JsonObject Params { get; set; } = null!; + public JsonObject Params { get; set; } public class RegisterFlow { [JsonPropertyName("stages")] - public List Stages { get; set; } = null!; + public List Stages { get; set; } } } internal class LoginFlowsResponse { [JsonPropertyName("flows")] - public List Flows { get; set; } = null!; + public List Flows { get; set; } public class LoginFlow { [JsonPropertyName("type")] - public string Type { get; set; } = null!; + public string Type { get; set; } } } -- cgit 1.5.1