Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Filee.Engines/Infrastructure/EngineEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// the PC (on the PATH or where its installer puts it), so nobody has to download a second FFmpeg they already have.

using System.Collections.Concurrent;
using System.Runtime.Versioning;
using System.Security;
using Filee.Core.Settings;
using Microsoft.Win32;

namespace Filee.Engines.Infrastructure;

Expand Down Expand Up @@ -219,6 +222,8 @@ private static IEnumerable<string> SystemFolders(string id)
if (id == "calibre")
folders.AddRange(["/Applications/calibre.app", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Applications", "calibre.app"), "/opt/calibre"]);
}
else if (id == "libreoffice")
folders.AddRange(RegisteredFolders(LibreOfficeInstallPathKey));
foreach (var root in (OperatingSystem.IsWindows()
? new[] { Environment.SpecialFolder.ProgramFiles, Environment.SpecialFolder.ProgramFilesX86 }
: [])
Expand Down Expand Up @@ -256,6 +261,37 @@ private static IEnumerable<string> SystemFolders(string id)
.Where(f => !IsWithin(f, DownloadRoot, comparison) && !IsWithin(f, AppContext.BaseDirectory, comparison));
}

/// <summary>Where LibreOffice's Windows installer records its program folder (the key's default value).</summary>
internal const string LibreOfficeInstallPathKey = @"SOFTWARE\LibreOffice\UNO\InstallPath";

/// <summary>
/// The folders recorded as the default value of <paramref name="keyPath"/>, for this user and for all users, in
/// the 64-bit and 32-bit views. LibreOffice installed somewhere else than Program Files (another drive, a folder
/// of its own) is found this way without being on the PATH.
/// </summary>
[SupportedOSPlatform("windows")]
internal static List<string> RegisteredFolders(string keyPath)
{
var found = new List<string>();
foreach (var hive in new[] { RegistryHive.CurrentUser, RegistryHive.LocalMachine })
{
foreach (var view in new[] { RegistryView.Registry64, RegistryView.Registry32 })
{
try
{
using var root = RegistryKey.OpenBaseKey(hive, view);
using var key = root.OpenSubKey(keyPath);
if (key?.GetValue(null) is string folder && folder.Trim().Trim('"') is { Length: > 0 } trimmed)
found.Add(Environment.ExpandEnvironmentVariables(trimmed));
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or SecurityException)
{
}
}
}
return found;
}

private static bool IsWithin(string path, string root, StringComparison comparison) =>
string.Equals(Path.TrimEndingDirectorySeparator(path), Path.TrimEndingDirectorySeparator(root), comparison)
|| path.StartsWith(Path.TrimEndingDirectorySeparator(root) + Path.DirectorySeparatorChar, comparison);
Expand Down
24 changes: 24 additions & 0 deletions tests/Filee.Engines.Tests/OwnCopyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ public void A_tool_on_the_path_is_found_when_filee_has_no_copy()
Assert.Equal(Path.Combine(_folder, EngineEnvironment.ProgramName("ffmpeg.exe")), EngineEnvironment.SystemProgram("ffmpeg", "ffmpeg.exe"));
}

[Fact]
[System.Runtime.Versioning.SupportedOSPlatform("windows")] // skips elsewhere
public void LibreOffice_is_found_in_the_folder_its_installer_recorded()
{
Assert.SkipUnless(OperatingSystem.IsWindows(), "The Windows registry.");
const string key = @"Software\FileeTests\LibreOffice\UNO\InstallPath";
File.WriteAllBytes(Path.Combine(_folder, "soffice.exe"), [0x4D, 0x5A]);
try
{
using (var written = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(key))
written.SetValue(null, $"\"{_folder}\"");

var folders = EngineEnvironment.RegisteredFolders(key);

Assert.Contains(_folder, folders);
Assert.Equal(_folder, EngineEnvironment.FolderWithPrograms("libreoffice", _folder));
Assert.Empty(EngineEnvironment.RegisteredFolders(@"Software\FileeTests\Missing"));
}
finally
{
Microsoft.Win32.Registry.CurrentUser.DeleteSubKeyTree(@"Software\FileeTests", throwOnMissingSubKey: false);
}
}

[Theory]
[InlineData("ffmpeg", "ffmpeg version 7.1.1-full_build-www.gyan.dev Copyright (c) 2000-2025", "7.1")]
[InlineData("ffmpeg", "ffmpeg version n8.0 Copyright (c) 2000-2025", "8.0")]
Expand Down
Loading