1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-12 14:23:26 +00:00
Files
mobile/src/Core/Utilities/WebAuthenticatorMAUI/WebUtils.cs
Federico Maccaroni 19f238d9bb [PM-6539] Fix Autofill Extension TDE without MP flow (#3049)
* PM-6539 Fix Autofill Extension TDE without MP updating PromptSSO to work in MAUI and also Generator view. WebAuthenticator copied with UIWindow gotten as it was in Xamarin forms to work. Also fix one NRE on state migration.

* PM-6539 Remove unnecessary using
2024-03-05 18:09:20 -03:00

126 lines
4.2 KiB
C#

// This is copied from MAUI repo to be used from WebAuthenticator
// https://github.com/dotnet/maui/blob/main/src/Essentials/src/Types/Shared/WebUtils.shared.cs
#if IOS
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Bit.Core.Utilities.MAUI
{
static class WebUtils
{
internal static IDictionary<string, string> ParseQueryString(Uri uri)
{
var parameters = new Dictionary<string, string>(StringComparer.Ordinal);
if (uri == null)
return parameters;
// Note: Uri.Query starts with a '?'
if (!string.IsNullOrEmpty(uri.Query))
UnpackParameters(uri.Query.AsSpan(1), parameters);
// Note: Uri.Fragment starts with a '#'
if (!string.IsNullOrEmpty(uri.Fragment))
UnpackParameters(uri.Fragment.AsSpan(1), parameters);
return parameters;
}
// The following method is a port of the logic found in https://source.dot.net/#Microsoft.AspNetCore.WebUtilities/src/Shared/QueryStringEnumerable.cs
// but refactored such that it:
//
// 1. avoids the IEnumerable overhead that isn't needed (the ASP.NET logic was clearly designed that way to offer a public API whereas we don't need that)
// 2. avoids the use of unsafe code
static void UnpackParameters(ReadOnlySpan<char> query, Dictionary<string, string> parameters)
{
while (!query.IsEmpty)
{
int delimeterIndex = query.IndexOf('&');
ReadOnlySpan<char> segment;
if (delimeterIndex >= 0)
{
segment = query.Slice(0, delimeterIndex);
query = query.Slice(delimeterIndex + 1);
}
else
{
segment = query;
query = default;
}
// If it's nonempty, emit it
if (!segment.IsEmpty)
{
var equalIndex = segment.IndexOf('=');
string name, value;
if (equalIndex >= 0)
{
name = segment.Slice(0, equalIndex).ToString();
var span = segment.Slice(equalIndex + 1);
var chars = new char[span.Length];
for (int i = 0; i < span.Length; i++)
chars[i] = span[i] == '+' ? ' ' : span[i];
value = new string(chars);
}
else
{
name = segment.ToString();
value = string.Empty;
}
name = Uri.UnescapeDataString(name);
parameters[name] = Uri.UnescapeDataString(value);
}
}
}
internal static Uri EscapeUri(Uri uri)
{
if (uri == null)
throw new ArgumentNullException(nameof(uri));
var idn = new global::System.Globalization.IdnMapping();
return new Uri(uri.Scheme + "://" + idn.GetAscii(uri.Authority) + uri.PathAndQuery + uri.Fragment);
}
internal static bool CanHandleCallback(Uri expectedUrl, Uri callbackUrl)
{
if (!callbackUrl.Scheme.Equals(expectedUrl.Scheme, StringComparison.OrdinalIgnoreCase))
return false;
if (!string.IsNullOrEmpty(expectedUrl.Host))
{
if (!callbackUrl.Host.Equals(expectedUrl.Host, StringComparison.OrdinalIgnoreCase))
return false;
}
return true;
}
#if __IOS__ || __TVOS__ || __MACOS__
internal static Foundation.NSUrl GetNativeUrl(Uri uri)
{
try
{
return new Foundation.NSUrl(uri.OriginalString);
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to create NSUrl from Original string, trying Absolute URI: {ex.Message}");
return new Foundation.NSUrl(uri.AbsoluteUri);
}
}
#endif
}
}
#endif