Files
PowerToys/src/modules/Hosts/HostsUILib/Helpers/HostsService.cs

322 lines
11 KiB
C#
Raw Normal View History

2022-10-13 13:05:43 +02:00
// 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;
2022-10-13 13:05:43 +02:00
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;
2022-10-13 13:05:43 +02:00
namespace HostsUILib.Helpers
2022-10-13 13:05:43 +02:00
{
public partial class HostsService : IHostsService, IDisposable
2022-10-13 13:05:43 +02:00
{
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
private const int DefaultBufferSize = 4096; // From System.IO.File source code
2022-10-13 13:05:43 +02:00
private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
private readonly IFileSystem _fileSystem;
private readonly IUserSettings _userSettings;
private readonly IElevationHelper _elevationHelper;
private readonly IFileSystemWatcher _fileSystemWatcher;
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
private readonly IBackupManager _backupManager;
2022-10-13 13:05:43 +02:00
private readonly string _hostsFilePath;
private bool _disposed;
public string HostsFilePath => _hostsFilePath;
public event EventHandler FileChanged;
public Encoding Encoding => _userSettings.Encoding == HostsEncoding.Utf8 ? new UTF8Encoding(false) : new UTF8Encoding(true);
2022-10-13 13:05:43 +02:00
public HostsService(
IFileSystem fileSystem,
IUserSettings userSettings,
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
IElevationHelper elevationHelper,
IBackupManager backupManager)
2022-10-13 13:05:43 +02:00
{
_fileSystem = fileSystem;
_userSettings = userSettings;
_elevationHelper = elevationHelper;
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
_backupManager = backupManager;
2022-10-13 13:05:43 +02:00
_hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\drivers\etc\hosts");
_fileSystemWatcher = _fileSystem.FileSystemWatcher.New();
2022-10-13 13:05:43 +02:00
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(HostsFilePath);
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(HostsFilePath);
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
public async Task<HostsData> ReadAsync()
2022-10-13 13:05:43 +02:00
{
var entries = new List<Entry>();
var unparsedBuilder = new StringBuilder();
var splittedEntries = false;
2022-10-13 13:05:43 +02:00
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
if (!_fileSystem.File.Exists(HostsFilePath))
2022-10-13 13:05:43 +02:00
{
return new HostsData(entries, unparsedBuilder.ToString(), false);
2022-10-13 13:05:43 +02:00
}
var lines = await _fileSystem.File.ReadAllLinesAsync(HostsFilePath, Encoding);
2022-10-13 13:05:43 +02:00
var id = 0;
2022-10-13 13:05:43 +02:00
for (var i = 0; i < lines.Length; i++)
{
var line = lines[i];
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var entry = new Entry(id, line);
2022-10-13 13:05:43 +02:00
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;
2022-10-13 13:05:43 +02:00
}
else
{
if (unparsedBuilder.Length > 0)
{
unparsedBuilder.Append(Environment.NewLine);
}
unparsedBuilder.Append(line);
}
}
return new HostsData(entries, unparsedBuilder.ToString(), splittedEntries);
2022-10-13 13:05:43 +02:00
}
public async Task WriteAsync(string additionalLines, IEnumerable<Entry> entries)
2022-10-13 13:05:43 +02:00
{
if (!_elevationHelper.IsElevated)
{
throw new NotRunningElevatedException();
2022-10-13 13:05:43 +02:00
}
if (_fileSystem.FileInfo.New(HostsFilePath).IsReadOnly)
{
throw new ReadOnlyHostsException();
}
2022-10-13 13:05:43 +02:00
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);
2022-10-13 13:05:43 +02:00
}
else
{
if (!e.Active)
{
lineBuilder.Append('#').Append(' ');
}
Hosts: add “No leading spaces” option and honor it when saving (#41206) ## Summary of the Pull Request Adds a new Hosts File Editor setting “No leading spaces” that prevents prepending spaces to active lines when saving the hosts file (when any entry is disabled). Default is Off to preserve current behavior. ## PR Checklist - [x] Closes: #36386   - [ ] Communication: N/A (small, scoped option) - [x] Tests: Added/updated and all pass - [x] Localization: New en-US strings added; other locales handled by loc pipeline - [ ] Dev docs: N/A - [x] New binaries: None - [x] Documentation updated: N/A ## Detailed Description of the Pull Request / Additional comments - Settings surface:   - `src/settings-ui/Settings.UI.Library/HostsProperties.cs`: add `NoLeadingSpaces`   - `src/modules/Hosts/HostsUILib/Settings/IUserSettings.cs`: add `NoLeadingSpaces`   - `src/modules/Hosts/Hosts/Settings/UserSettings.cs`: load/save value from settings.json   - `src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs`: expose `NoLeadingSpaces`   - `src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml`: new SettingsCard toggle   - `src/settings-ui/Settings.UI/Strings/en-us/Resources.resw`: add `Hosts_NoLeadingSpaces.Header/Description` - Writer change:   - `src/modules/Hosts/HostsUILib/Helpers/HostsService.cs`: gate indent with `anyDisabled && !_userSettings.NoLeadingSpaces` - Tests:   - `src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs`: `NoLeadingSpaces_Disabled_RemovesIndent` Backward compatibility: default Off, current formatting unchanged unless the user enables the option. ## Validation Steps Performed - Automated: `HostsEditor.UnitTests` including `NoLeadingSpaces_Disabled_RemovesIndent` passing. - Manual:   1. Run PowerToys (runner) as Admin.   2. Settings → Hosts File Editor → enable “No leading spaces”.   3. In editor, add active `127.0.0.10 example1` and disabled `127.0.0.11 example2`; Save.   4. Open `C:\Windows\System32\drivers\etc\hosts` in Notepad.      - ON: active line starts at column 0; disabled is `# 127...`.      - OFF: active line begins with two spaces when a disabled entry exists.
2025-08-19 00:58:10 -05:00
else if (anyDisabled && !_userSettings.NoLeadingSpaces)
2022-10-13 13:05:43 +02:00
{
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)
2022-10-13 13:05:43 +02:00
{
lines.Insert(0, additionalLines);
}
else if (_userSettings.AdditionalLinesPosition == HostsAdditionalLinesPosition.Bottom)
2022-10-13 13:05:43 +02:00
{
lines.Add(additionalLines);
}
}
try
{
await _asyncLock.WaitAsync();
_fileSystemWatcher.EnableRaisingEvents = false;
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
_backupManager.Create(HostsFilePath);
2022-10-13 13:05:43 +02:00
// FileMode.OpenOrCreate is necessary to prevent UnauthorizedAccessException when the hosts file is hidden
[Hosts] Backup Settings (#37778) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Add backup settings for the Hosts File Editor to allow users to customize the existing hardcoded logic. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #37666 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <img width="707" alt="image" src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa" /> <img width="707" alt="image" src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed - Backup on: verified that backup isn't executed - Backups off: Verified that only one backup is executed - Verified that backup is located in the expected path - Auto delete is set to "never": verified that no backups are deleted - Auto delete is set to "based on count": verified that backups are deleted according to count value - Auto delete is set to "based on age and count": verified that backups are deleted according to days and count values - Verified that files without the backup pattern aren't deleted - There is also adequate test coverage for these scenarios 🚀 --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 09:42:31 +01:00
using var stream = _fileSystem.FileStream.New(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous);
using var writer = new StreamWriter(stream, Encoding);
foreach (var line in lines)
{
await writer.WriteLineAsync(line.AsMemory());
}
stream.SetLength(stream.Position);
await writer.FlushAsync();
2022-10-13 13:05:43 +02:00
}
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 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 RemoveReadOnlyAttribute()
{
var fileInfo = _fileSystem.FileInfo.New(HostsFilePath);
if (fileInfo.IsReadOnly)
{
fileInfo.IsReadOnly = false;
}
}
2022-10-13 13:05:43 +02:00
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;
}
}
}
}
}