diff --git a/Desktop.Linux/Program.cs b/Desktop.Linux/Program.cs index 10972d16..00db19f0 100644 --- a/Desktop.Linux/Program.cs +++ b/Desktop.Linux/Program.cs @@ -33,7 +33,7 @@ public class Program var logger = new FileLogger("Remotely_Desktop", version, "Program.cs"); var filePath = Environment.ProcessPath ?? Environment.GetCommandLineArgs().First(); var serverUrl = Debugger.IsAttached ? "http://localhost:5000" : string.Empty; - var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath); + var getEmbeddedResult = EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath); if (getEmbeddedResult.IsSuccess) { serverUrl = getEmbeddedResult.Value.ServerUrl.AbsoluteUri; diff --git a/Desktop.Shared/Services/BrandingProvider.cs b/Desktop.Shared/Services/BrandingProvider.cs index 1e128a96..b57bbbd3 100644 --- a/Desktop.Shared/Services/BrandingProvider.cs +++ b/Desktop.Shared/Services/BrandingProvider.cs @@ -85,7 +85,7 @@ public class BrandingProvider : IBrandingProvider return Result.Fail("Failed to retrieve executing file name."); } - var result = await _embeddedDataSearcher.TryGetEmbeddedData(filePath); + var result = _embeddedDataSearcher.TryGetEmbeddedData(filePath); if (!result.IsSuccess) { diff --git a/Desktop.Win/Program.cs b/Desktop.Win/Program.cs index 1b365318..c677c90f 100644 --- a/Desktop.Win/Program.cs +++ b/Desktop.Win/Program.cs @@ -35,7 +35,7 @@ public class Program var logger = new FileLogger("Remotely_Desktop", version, "Program.cs"); var filePath = Environment.ProcessPath ?? Environment.GetCommandLineArgs().First(); var serverUrl = Debugger.IsAttached ? "https://localhost:5001" : string.Empty; - var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath); + var getEmbeddedResult = EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath); if (getEmbeddedResult.IsSuccess) { serverUrl = getEmbeddedResult.Value.ServerUrl.AbsoluteUri; diff --git a/Server/API/ClientDownloadsController.cs b/Server/API/ClientDownloadsController.cs index 8638492e..1cd1aa95 100644 --- a/Server/API/ClientDownloadsController.cs +++ b/Server/API/ClientDownloadsController.cs @@ -144,15 +144,20 @@ public class ClientDownloadsController : ControllerBase var effectiveScheme = settings.ForceClientHttps ? "https" : Request.Scheme; var serverUrl = $"{effectiveScheme}://{Request.Host}"; - var embeddedData = new EmbeddedServerData(new Uri(serverUrl), organizationId); - var result = await _embeddedDataSearcher.GetAppendedStream(filePath, embeddedData); - if (!result.IsSuccess) + var defaultOrg = await _dataService.GetDefaultOrganization(); + + // The default org will be used if unspecified, so might as well save the + // space in the file name. + if (defaultOrg.IsSuccess && + defaultOrg.Value.ID.Equals(organizationId, StringComparison.OrdinalIgnoreCase)) { - throw result.Exception ?? new Exception(result.Reason); + organizationId = null; } - - return File(result.Value, "application/octet-stream", Path.GetFileName(filePath)); + var embeddedData = new EmbeddedServerData(new Uri(serverUrl), organizationId); + var fileName = _embeddedDataSearcher.GetEncodedFileName(filePath, embeddedData); + var fileInfo = _hostEnv.WebRootFileProvider.GetFileInfo(filePath.Replace(_hostEnv.WebRootPath, string.Empty)); + return File(fileInfo.CreateReadStream(), "application/octet-stream", fileName); } private async Task GetInstallFile(string organizationId, string platformID) diff --git a/Server/Components/Layout/NavMenu.razor b/Server/Components/Layout/NavMenu.razor index b3284078..585ab135 100644 --- a/Server/Components/Layout/NavMenu.razor +++ b/Server/Components/Layout/NavMenu.razor @@ -32,6 +32,20 @@ + + + + + - @if (_user?.IsAdministrator == true) { diff --git a/Server/Components/Pages/Downloads.razor b/Server/Components/Pages/Downloads.razor index 208d43e3..48668e81 100644 --- a/Server/Components/Pages/Downloads.razor +++ b/Server/Components/Pages/Downloads.razor @@ -9,9 +9,13 @@ @inject NavigationManager NavManager @inject ILogger Logger -
- Looking for background agents? Head to the Deploy page! -
+@if (_isAuthenticated) +{ +
+ Check out the Deploy page for a copy-and-paste deploy script! +
+} +

Portable Instant Support Clients

@@ -37,57 +41,163 @@

@*
- macOS x64 (10.12 - 10.15) -

- macOS x64 Executable -

- macOS arm64 (11.01) -

- macOS arm64 Executable -

+ macOS x64 (10.12 - 10.15) +

+ macOS x64 Executable +

+ macOS arm64 (11.01) +

+ macOS arm64 Executable +

*@ -
-

Binaries Only

+

Resident Agents

+

- Archives containing the agent binaries only. + Installable background agents that provide unattended access and remote scripting.

-
- Windows 10 / 11 (64-Bit and 32-Bit) -

- Windows x64 Files Only -
- Windows x86 Files Only -

-
+ @if (!_isAuthenticated) + { +
+
Must be logged in to download.
+
- + } + else + { +
+ Windows 10 / 11 (64-Bit and 32-Bit) +

+ Windows PowerShell Installer +
+ Windows x64 Files Only +
+ Windows x86 Files Only +

- @* There is currently no bandwidth for Mac development. The community will need to pick it up. *@ - @*
- macOS x64 (10.12 - 10.15) -

- macOS x64 Files Only -

-
+

+

Example Quiet Install:
-
- macOS arm64 -

- macOS arm64 Files Only -

-
*@ + + powershell.exe -ExecutionPolicy Bypass -F {path}\Install-Remotely.ps1 + -install + -quiet + -organizationid "0b3d706b-9c5d-41e6-8ae9-5720d16324e6" + -serverurl "https://remotely.mytechshop.com" + +

+

+

Example Quiet Uninstall:
+ + powershell.exe -ExecutionPolicy Bypass -F Install-Remotely.ps1 -uninstall -quiet +

+

+

Example Local Install:
+ + + powershell.exe -ExecutionPolicy Bypass -F {path}\Install-Remotely.ps1 + -install + -quiet + -organizationid "0b3d706b-9c5d-41e6-8ae9-5720d16324e6" + -serverurl "https://remotely.mytechshop.com" + -path "[path]\Remotely-Win-x64.zip" + +

+

+

All Override Options:
+ + + powershell.exe -ExecutionPolicy Bypass -F {path}\Install-Remotely.ps1 + -install -quiet -supportshortcut + -organizationid "0b3d706b-9c5d-41e6-8ae9-5720d16324e6" + -serverurl "https://remotely.mytechshop.com" + -devicegroup "Customer Group 1" + -devicealias "Front Desk" + -deviceuuid "eebb4d87-5459-4976-989e-a7876dffc69c" + +

+
+ +
+ Linux 64-Bit +

+ Ubuntu x64 Bash Installer +
+ Manjaro x64 Bash Installer +
+ Linux x64 Files Only +

+

+

Example Install:
+ + sudo [path]/Install-Ubuntu-x64.sh +

+

+

Example Local Install:
+ + sudo [path]/Install-Ubuntu-x64.sh --path [path]/Remotely-Linux.zip +

+

+

Uninstall:
+ + sudo [path]/Install-Ubuntu-x64.sh --uninstall +

+
+ +
+ macOS x64 (10.12 - 10.15) +

+ macOS x64 Bash Installer +
+ macOS x64 Files Only +

+ +

+

Example Install:
+ + sudo [path]/Install-MacOS-x64.sh +

+

+

Example Local Install:
+ + sudo [path]/Install-MacOS-x64.sh --path [path]/Remotely-MacOS-x64.zip +

+

+

Example Uninstall:
+ + sudo [path]/Install-MacOS-x64.sh --uninstall +

+
+ +
+ macOS arm64 +

+ macOS arm64 Bash Installer +
+ macOS arm64 Files Only +

+ +

+

Example Install:
+ + sudo [path]/Install-MacOS-arm64.sh +

+

+

Example Local Install:
+ + sudo [path]/Install-MacOS-arm64.sh --path [path]/Remotely-MacOS-arm64.zip +

+

+

Example Uninstall:
+ + sudo [path]/Install-MacOS-arm64.sh --uninstall +

+
+ }
diff --git a/Shared/Services/EmbeddedServerDataSearcher.cs b/Shared/Services/EmbeddedServerDataSearcher.cs index 72b948b7..297e3493 100644 --- a/Shared/Services/EmbeddedServerDataSearcher.cs +++ b/Shared/Services/EmbeddedServerDataSearcher.cs @@ -19,42 +19,40 @@ namespace Remotely.Shared.Services; public interface IEmbeddedServerDataSearcher { - Task> GetAppendedStream(string filePath, EmbeddedServerData serverData); - Task> TryGetEmbeddedData(string filePath); + string GetEncodedFileName(string filePath, EmbeddedServerData serverData); + Result TryGetEmbeddedData(string filePath); } public class EmbeddedServerDataSearcher() : IEmbeddedServerDataSearcher { public static EmbeddedServerDataSearcher Instance { get; } = new(); - public async Task> TryGetEmbeddedData(string filePath) + public string GetEncodedFileName(string filePath, EmbeddedServerData serverData) + { + var fileName = Path.GetFileNameWithoutExtension(filePath); + var ext = Path.GetExtension(filePath); + + // Make the base64 string safe file paths and URIs. + var encodedData = Convert + .ToBase64String(MessagePackSerializer.Serialize(serverData)) + .Replace("/", "_") + .Replace("+", "-"); + + return $"{fileName}[{encodedData}]{ext}"; + } + + public Result TryGetEmbeddedData(string filePath) { try { - if (!File.Exists(filePath)) - { - return Result.Fail($"File path does not exist: {filePath}"); - } - - using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - using var br = new BinaryReader(fs); - using var sr = new StreamReader(fs); - - fs.Seek(-4, SeekOrigin.End); - var dataSize = br.ReadInt32(); - fs.Seek(-dataSize - 4, SeekOrigin.End); - - var buffer = new byte[dataSize]; - await fs.ReadExactlyAsync(buffer); - var json = Encoding.UTF8.GetString(buffer); - - var embeddedData = JsonSerializer.Deserialize(json); - - if (embeddedData is null) - { - return Result.Fail("Embedded data is empty."); - } + var fileName = Path.GetFileNameWithoutExtension(filePath); + var start = fileName.LastIndexOf('[') + 1; + var end = fileName.LastIndexOf(']'); + var base64 = fileName[start..end] + .Replace("_", "/") + .Replace("-", "+"); + var embeddedData = MessagePackSerializer.Deserialize(Convert.FromBase64String(base64)); return Result.Ok(embeddedData); } catch (Exception ex) @@ -62,21 +60,4 @@ public class EmbeddedServerDataSearcher() : IEmbeddedServerDataSearcher return Result.Fail(ex); } } - - public Task> GetAppendedStream(string filePath, EmbeddedServerData serverData) - { - try - { - var json = JsonSerializer.Serialize(serverData); - var jsonBytes = Encoding.UTF8.GetBytes(json); - byte[] appendPayload = [.. jsonBytes, .. BitConverter.GetBytes(jsonBytes.Length)]; - var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var appendableStream = new AppendableStream(fs, appendPayload); - return Task.FromResult(Result.Ok(appendableStream)); - } - catch (Exception ex) - { - return Task.FromResult(Result.Fail(ex)); - } - } }