mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 11:46:30 +02:00
[EnvVar][Hosts][RegPrev]Decouple and refactor to make it "packable" as nuget package (#32604)
* WIP Hosts - remove deps * Add consumer app * Move App and MainWindow to Consumer app. Make Hosts dll * Try consume it * Fix errors * Make it work with custom build targets * Dependency injection Refactor Explicit page creation Wire missing dependencies * Fix installer * Remove unneeded stuff * Fix build again * Extract UI and logic from MainWindow to RegistryPreviewMainPage * Convert to lib Change namespace to RegistryPreviewUILib Remove PT deps * Add exe app and move App.xaml and MainWindow.xaml * Consume the lib * Update Hosts package creation * Fix RegistryPreview package creation * Rename RegistryPreviewUI back to RegistryPreview * Back to consuming lib * Ship icons and assets in nuget packages * Rename to EnvironmentVariablesUILib and convert to lib * Add app and consume * Telemetry * GPO * nuget * Rename HostsPackageConsumer to Hosts and Hosts lib to HostsUILib * Assets cleanup * nuget struct * v0 * assets * [Hosts] Re-add AppList to Lib Assets, [RegPrev] Copy lib assets to out dir * Sign UI dlls * Revert WinUIEx bump * Cleanup * Align deps * version exception dll * Fix RegistryPreview crashes * XAML format * XAML format 2 * Pack .pri files in lib/ dir --------- Co-authored-by: Darshak Bhatti <dabhatti@microsoft.com>
This commit is contained in:
20
src/modules/Hosts/HostsUILib/Helpers/ElevationHelper.cs
Normal file
20
src/modules/Hosts/HostsUILib/Helpers/ElevationHelper.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public class ElevationHelper : IElevationHelper
|
||||
{
|
||||
private readonly bool _isElevated;
|
||||
|
||||
public bool IsElevated => _isElevated;
|
||||
|
||||
public ElevationHelper()
|
||||
{
|
||||
_isElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/modules/Hosts/HostsUILib/Helpers/ExpressionExtensions.cs
Normal file
43
src/modules/Hosts/HostsUILib/Helpers/ExpressionExtensions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
// https://stackoverflow.com/a/22569086
|
||||
public static class ExpressionExtensions
|
||||
{
|
||||
public static Expression<Func<T, bool>> And<T>(
|
||||
this Expression<Func<T, bool>> expr1,
|
||||
Expression<Func<T, bool>> expr2)
|
||||
{
|
||||
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
|
||||
}
|
||||
|
||||
public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx)
|
||||
{
|
||||
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
|
||||
}
|
||||
|
||||
internal sealed class ReplaceVisitor : ExpressionVisitor
|
||||
{
|
||||
private readonly Expression _from;
|
||||
private readonly Expression _to;
|
||||
|
||||
public ReplaceVisitor(Expression from, Expression to)
|
||||
{
|
||||
_from = from;
|
||||
_to = to;
|
||||
}
|
||||
|
||||
public override Expression Visit(Expression node)
|
||||
{
|
||||
return node == _from ? _to : base.Visit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
329
src/modules/Hosts/HostsUILib/Helpers/HostsService.cs
Normal file
329
src/modules/Hosts/HostsUILib/Helpers/HostsService.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using HostsUILib.Exceptions;
|
||||
using HostsUILib.Models;
|
||||
using HostsUILib.Settings;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public class HostsService : IHostsService, IDisposable
|
||||
{
|
||||
private const string _backupSuffix = $"_PowerToysBackup_";
|
||||
|
||||
private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IUserSettings _userSettings;
|
||||
private readonly IElevationHelper _elevationHelper;
|
||||
private readonly IFileSystemWatcher _fileSystemWatcher;
|
||||
private readonly string _hostsFilePath;
|
||||
private bool _backupDone;
|
||||
private bool _disposed;
|
||||
|
||||
public string HostsFilePath => _hostsFilePath;
|
||||
|
||||
public event EventHandler FileChanged;
|
||||
|
||||
public Encoding Encoding => _userSettings.Encoding == HostsEncoding.Utf8 ? new UTF8Encoding(false) : new UTF8Encoding(true);
|
||||
|
||||
public HostsService(
|
||||
IFileSystem fileSystem,
|
||||
IUserSettings userSettings,
|
||||
IElevationHelper elevationHelper)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_userSettings = userSettings;
|
||||
_elevationHelper = elevationHelper;
|
||||
|
||||
_hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\drivers\etc\hosts");
|
||||
|
||||
_fileSystemWatcher = _fileSystem.FileSystemWatcher.CreateNew();
|
||||
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(HostsFilePath);
|
||||
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(HostsFilePath);
|
||||
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
|
||||
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
public bool Exists()
|
||||
{
|
||||
return _fileSystem.File.Exists(HostsFilePath);
|
||||
}
|
||||
|
||||
public async Task<HostsData> ReadAsync()
|
||||
{
|
||||
var entries = new List<Entry>();
|
||||
var unparsedBuilder = new StringBuilder();
|
||||
var splittedEntries = false;
|
||||
|
||||
if (!Exists())
|
||||
{
|
||||
return new HostsData(entries, unparsedBuilder.ToString(), false);
|
||||
}
|
||||
|
||||
var lines = await _fileSystem.File.ReadAllLinesAsync(HostsFilePath, Encoding);
|
||||
|
||||
var id = 0;
|
||||
|
||||
for (var i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entry = new Entry(id, line);
|
||||
|
||||
if (entry.Valid)
|
||||
{
|
||||
entries.Add(entry);
|
||||
id++;
|
||||
}
|
||||
else if (entry.Validate(false))
|
||||
{
|
||||
foreach (var hostsChunk in entry.SplittedHosts.Chunk(Consts.MaxHostsCount))
|
||||
{
|
||||
var clonedEntry = entry.Clone();
|
||||
clonedEntry.Id = id;
|
||||
clonedEntry.Hosts = string.Join(' ', hostsChunk);
|
||||
entries.Add(clonedEntry);
|
||||
id++;
|
||||
}
|
||||
|
||||
splittedEntries = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unparsedBuilder.Length > 0)
|
||||
{
|
||||
unparsedBuilder.Append(Environment.NewLine);
|
||||
}
|
||||
|
||||
unparsedBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
|
||||
return new HostsData(entries, unparsedBuilder.ToString(), splittedEntries);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string additionalLines, IEnumerable<Entry> entries)
|
||||
{
|
||||
if (!_elevationHelper.IsElevated)
|
||||
{
|
||||
throw new NotRunningElevatedException();
|
||||
}
|
||||
|
||||
if (_fileSystem.FileInfo.FromFileName(HostsFilePath).IsReadOnly)
|
||||
{
|
||||
throw new ReadOnlyHostsException();
|
||||
}
|
||||
|
||||
var lines = new List<string>();
|
||||
|
||||
if (entries.Any())
|
||||
{
|
||||
var addressPadding = entries.Max(e => e.Address.Length) + 1;
|
||||
var hostsPadding = entries.Max(e => e.Hosts.Length) + 1;
|
||||
var anyDisabled = entries.Any(e => !e.Active);
|
||||
|
||||
foreach (var e in entries)
|
||||
{
|
||||
var lineBuilder = new StringBuilder();
|
||||
|
||||
if (!e.Valid)
|
||||
{
|
||||
lineBuilder.Append(e.Line);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!e.Active)
|
||||
{
|
||||
lineBuilder.Append('#').Append(' ');
|
||||
}
|
||||
else if (anyDisabled)
|
||||
{
|
||||
lineBuilder.Append(' ').Append(' ');
|
||||
}
|
||||
|
||||
lineBuilder.Append(e.Address.PadRight(addressPadding));
|
||||
lineBuilder.Append(string.Join(' ', e.Hosts).PadRight(hostsPadding));
|
||||
|
||||
if (e.Comment != string.Empty)
|
||||
{
|
||||
lineBuilder.Append('#').Append(' ');
|
||||
lineBuilder.Append(e.Comment);
|
||||
}
|
||||
|
||||
lines.Add(lineBuilder.ToString().TrimEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(additionalLines))
|
||||
{
|
||||
if (_userSettings.AdditionalLinesPosition == HostsAdditionalLinesPosition.Top)
|
||||
{
|
||||
lines.Insert(0, additionalLines);
|
||||
}
|
||||
else if (_userSettings.AdditionalLinesPosition == HostsAdditionalLinesPosition.Bottom)
|
||||
{
|
||||
lines.Add(additionalLines);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _asyncLock.WaitAsync();
|
||||
_fileSystemWatcher.EnableRaisingEvents = false;
|
||||
|
||||
if (!_backupDone && Exists())
|
||||
{
|
||||
_fileSystem.File.Copy(HostsFilePath, HostsFilePath + _backupSuffix + DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture));
|
||||
_backupDone = true;
|
||||
}
|
||||
|
||||
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines, Encoding);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||
_asyncLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PingAsync(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var ping = new Ping();
|
||||
var reply = await ping.SendPingAsync(address, 4000); // 4000 is the default ping timeout for ping.exe
|
||||
return reply.Status == IPStatus.Success;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanupBackup()
|
||||
{
|
||||
Directory.GetFiles(Path.GetDirectoryName(HostsFilePath), $"*{_backupSuffix}*")
|
||||
.Select(f => new FileInfo(f))
|
||||
.Where(f => f.CreationTime < DateTime.Now.AddDays(-15))
|
||||
.ToList()
|
||||
.ForEach(f => f.Delete());
|
||||
}
|
||||
|
||||
public void OpenHostsFile()
|
||||
{
|
||||
var notepadFallback = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to open in default editor
|
||||
var key = Registry.ClassesRoot.OpenSubKey("SystemFileAssociations\\text\\shell\\edit\\command");
|
||||
if (key != null)
|
||||
{
|
||||
var commandPattern = key.GetValue(string.Empty).ToString(); // Default value
|
||||
var file = null as string;
|
||||
var args = null as string;
|
||||
|
||||
if (commandPattern.StartsWith('\"'))
|
||||
{
|
||||
var endQuoteIndex = commandPattern.IndexOf('\"', 1);
|
||||
if (endQuoteIndex != -1)
|
||||
{
|
||||
file = commandPattern[1..endQuoteIndex];
|
||||
args = commandPattern[(endQuoteIndex + 1)..].Trim();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var spaceIndex = commandPattern.IndexOf(' ');
|
||||
if (spaceIndex != -1)
|
||||
{
|
||||
file = commandPattern[..spaceIndex];
|
||||
args = commandPattern[(spaceIndex + 1)..].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (file != null && args != null)
|
||||
{
|
||||
args = args.Replace("%1", HostsFilePath);
|
||||
Process.Start(new ProcessStartInfo(file, args));
|
||||
}
|
||||
else
|
||||
{
|
||||
notepadFallback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerInstance.Logger.LogError("Failed to open default editor", ex);
|
||||
notepadFallback = true;
|
||||
}
|
||||
|
||||
if (notepadFallback)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("notepad.exe", HostsFilePath));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerInstance.Logger.LogError("Failed to open notepad", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveReadOnly()
|
||||
{
|
||||
var fileInfo = _fileSystem.FileInfo.FromFileName(HostsFilePath);
|
||||
if (fileInfo.IsReadOnly)
|
||||
{
|
||||
fileInfo.IsReadOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
_fileSystemWatcher.EnableRaisingEvents = false;
|
||||
FileChanged?.Invoke(this, EventArgs.Empty);
|
||||
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_asyncLock.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/modules/Hosts/HostsUILib/Helpers/IElevationHelper.cs
Normal file
11
src/modules/Hosts/HostsUILib/Helpers/IElevationHelper.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public interface IElevationHelper
|
||||
{
|
||||
bool IsElevated { get; }
|
||||
}
|
||||
}
|
||||
30
src/modules/Hosts/HostsUILib/Helpers/IHostsService.cs
Normal file
30
src/modules/Hosts/HostsUILib/Helpers/IHostsService.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using HostsUILib.Models;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public interface IHostsService : IDisposable
|
||||
{
|
||||
string HostsFilePath { get; }
|
||||
|
||||
event EventHandler FileChanged;
|
||||
|
||||
Task<HostsData> ReadAsync();
|
||||
|
||||
Task WriteAsync(string additionalLines, IEnumerable<Entry> entries);
|
||||
|
||||
Task<bool> PingAsync(string address);
|
||||
|
||||
void CleanupBackup();
|
||||
|
||||
void OpenHostsFile();
|
||||
|
||||
void RemoveReadOnly();
|
||||
}
|
||||
}
|
||||
23
src/modules/Hosts/HostsUILib/Helpers/ILogger.cs
Normal file
23
src/modules/Hosts/HostsUILib/Helpers/ILogger.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
public void LogError(string message);
|
||||
|
||||
public void LogError(string message, Exception ex);
|
||||
|
||||
public void LogWarning(string message);
|
||||
|
||||
public void LogInfo(string message);
|
||||
|
||||
public void LogDebug(string message);
|
||||
|
||||
public void LogTrace();
|
||||
}
|
||||
}
|
||||
10
src/modules/Hosts/HostsUILib/Helpers/LoggerInstance.cs
Normal file
10
src/modules/Hosts/HostsUILib/Helpers/LoggerInstance.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public static class LoggerInstance
|
||||
{
|
||||
public static ILogger Logger { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
internal static class ResourceLoaderInstance
|
||||
{
|
||||
internal static ResourceLoader ResourceLoader { get; private set; }
|
||||
|
||||
static ResourceLoaderInstance()
|
||||
{
|
||||
ResourceLoader = new Microsoft.Windows.ApplicationModel.Resources.ResourceLoader("PowerToys.HostsUILib.pri", "PowerToys.HostsUILib/Resources");
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/modules/Hosts/HostsUILib/Helpers/StringHelper.cs
Normal file
14
src/modules/Hosts/HostsUILib/Helpers/StringHelper.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public static class StringHelper
|
||||
{
|
||||
public static string GetHostsWithComment(string hosts, string comment)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(comment) ? hosts : string.Concat(hosts, " - ", comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/modules/Hosts/HostsUILib/Helpers/ValidationHelper.cs
Normal file
68
src/modules/Hosts/HostsUILib/Helpers/ValidationHelper.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public static class ValidationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the address is a valid IPv4
|
||||
/// </summary>
|
||||
public static bool ValidIPv4(string address)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var regex = new Regex("^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
|
||||
return regex.IsMatch(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the address is a valid IPv6
|
||||
/// </summary>
|
||||
public static bool ValidIPv6(string address)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var regex = new Regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$");
|
||||
return regex.IsMatch(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the hosts are valid
|
||||
/// </summary>
|
||||
public static bool ValidHosts(string hosts, bool validateHostsLength)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(hosts))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var splittedHosts = hosts.Split(' ');
|
||||
|
||||
if (validateHostsLength && splittedHosts.Length > Consts.MaxHostsCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var host in splittedHosts)
|
||||
{
|
||||
if (Uri.CheckHostName(host) == UriHostNameType.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/modules/Hosts/HostsUILib/Helpers/VisualTreeUtils.cs
Normal file
67
src/modules/Hosts/HostsUILib/Helpers/VisualTreeUtils.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
// Taken from https://github.com/microsoft/microsoft-ui-xaml/blob/main/test/MUXControlsTestApp/Utilities/VisualTreeUtils.cs
|
||||
// Original copyright header:
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
public static class VisualTreeUtils
|
||||
{
|
||||
public static T FindVisualChildByType<T>(this DependencyObject element)
|
||||
where T : DependencyObject
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (element is T elementAsT)
|
||||
{
|
||||
return elementAsT;
|
||||
}
|
||||
|
||||
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var result = VisualTreeHelper.GetChild(element, i).FindVisualChildByType<T>();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FrameworkElement FindVisualChildByName(this DependencyObject element, string name)
|
||||
{
|
||||
if (element == null || string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (element is FrameworkElement elementAsFE && elementAsFE.Name == name)
|
||||
{
|
||||
return elementAsFE;
|
||||
}
|
||||
|
||||
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var result = VisualTreeHelper.GetChild(element, i).FindVisualChildByName(name);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user