Encode the URL in the file name of the attended support client.

This commit is contained in:
Jared Goodwin 2024-06-03 16:40:28 -07:00
parent d06c8d7333
commit f46567fec8
7 changed files with 205 additions and 106 deletions

View File

@ -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;

View File

@ -85,7 +85,7 @@ public class BrandingProvider : IBrandingProvider
return Result.Fail<BrandingInfo>("Failed to retrieve executing file name.");
}
var result = await _embeddedDataSearcher.TryGetEmbeddedData(filePath);
var result = _embeddedDataSearcher.TryGetEmbeddedData(filePath);
if (!result.IsSuccess)
{

View File

@ -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;

View File

@ -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<IActionResult> GetInstallFile(string organizationId, string platformID)

View File

@ -32,6 +32,20 @@
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="downloads">
<span class="oi oi-cloud-download" aria-hidden="true"></span> Downloads
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="/RemoteControl/Viewer" target="_blank">
<span class="oi oi-monitor" aria-hidden="true"></span> Remote Control
</NavLink>
</li>
<AuthorizeView>
<Authorized>
<li class="nav-item px-3">
@ -58,17 +72,6 @@
</Authorized>
</AuthorizeView>
<li class="nav-item px-3">
<NavLink class="nav-link" href="/RemoteControl/Viewer" target="_blank">
<span class="oi oi-monitor" aria-hidden="true"></span> Remote Control
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="downloads">
<span class="oi oi-cloud-download" aria-hidden="true"></span> Downloads
</NavLink>
</li>
@if (_user?.IsAdministrator == true)
{

View File

@ -9,9 +9,13 @@
@inject NavigationManager NavManager
@inject ILogger<Downloads> Logger
<div class="alert alert-info">
Looking for background agents? Head to the <a href="/deploy">Deploy</a> page!
</div>
@if (_isAuthenticated)
{
<div class="alert alert-info">
Check out the <a href="/deploy">Deploy</a> page for a copy-and-paste deploy script!
</div>
}
<div class="row mb-3">
<h4>Portable Instant Support Clients</h4>
@ -37,57 +41,163 @@
</p>
</div>
@*<div class="col-sm-6 mb-3">
<strong>macOS x64 (10.12 - 10.15)</strong>
<p>
<a target="_blank" href="/api/ClientDownloads/Desktop/MacOS-x64/@_organizationId">macOS x64 Executable</a>
</p>
<strong>macOS arm64 (11.01)</strong>
<p>
<a target="_blank" href="/api/ClientDownloads/Desktop/MacOS-arm64/@_organizationId">macOS arm64 Executable</a>
</p>
<strong>macOS x64 (10.12 - 10.15)</strong>
<p>
<a target="_blank" href="/api/ClientDownloads/Desktop/MacOS-x64/@_organizationId">macOS x64 Executable</a>
</p>
<strong>macOS arm64 (11.01)</strong>
<p>
<a target="_blank" href="/api/ClientDownloads/Desktop/MacOS-arm64/@_organizationId">macOS arm64 Executable</a>
</p>
</div>*@
</div>
<div class="row">
<h4>Binaries Only</h4>
<h4>Resident Agents</h4>
<p class="text-info col-sm-12 mb-2">
Archives containing the agent binaries only.
Installable background agents that provide unattended access and remote scripting.
</p>
<div class="col-sm-6 mb-3">
<strong>Windows 10 / 11 (64-Bit and 32-Bit)</strong>
<p>
<a target="_blank" href="/Content/Remotely-Win-x64.zip">Windows x64 Files Only</a>
<br />
<a target="_blank" href="/Content/Remotely-Win-x86.zip">Windows x86 Files Only</a>
</p>
</div>
@if (!_isAuthenticated)
{
<div class="col-sm-6 mb-3">
<h6>Must be logged in to download.</h6>
</div>
<div class="col-sm-6 mb-3">
<strong>Linux 64-Bit</strong>
<p>
<a target="_blank" href="/API/ClientDownloads/ManjaroInstaller-x64/@_organizationId">Manjaro x64 Bash Installer</a>
<br />
<a target="_blank" href="/Content/Remotely-Linux.zip">Linux x64 Files Only</a>
</p>
</div>
}
else
{
<div class="col-sm-6 mb-3">
<strong>Windows 10 / 11 (64-Bit and 32-Bit)</strong>
<p>
<a target="_blank" href="/API/ClientDownloads/WindowsInstaller/@_organizationId">Windows PowerShell Installer</a>
<br />
<a target="_blank" href="/Content/Remotely-Win-x64.zip">Windows x64 Files Only</a>
<br />
<a target="_blank" href="/Content/Remotely-Win-x86.zip">Windows x86 Files Only</a>
</p>
@* There is currently no bandwidth for Mac development. The community will need to pick it up. *@
@*<div class="col-sm-6 mb-3">
<strong>macOS x64 (10.12 - 10.15)</strong>
<p>
<a target="_blank" href="/Content/Remotely-MacOS-x64.zip">macOS x64 Files Only</a>
</p>
</div>
<p>
<div class="small">Example Quiet Install:</div>
<div class="col-sm-6 mb-3">
<strong>macOS arm64</strong>
<p>
<a target="_blank" href="/Content/Remotely-MacOS-arm64.zip">macOS arm64 Files Only</a>
</p>
</div> *@
<code class="label label-default small">
powershell.exe -ExecutionPolicy Bypass -F {path}\Install-Remotely.ps1
-install
-quiet
-organizationid "0b3d706b-9c5d-41e6-8ae9-5720d16324e6"
-serverurl "https://remotely.mytechshop.com"
</code>
</p>
<p>
<div class="small">Example Quiet Uninstall:</div>
<code class="label label-default small">powershell.exe -ExecutionPolicy Bypass -F Install-Remotely.ps1 -uninstall -quiet</code>
</p>
<p>
<div class="small">Example Local Install:</div>
<code class="label label-default small">
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"
</code>
</p>
<p>
<div class="small">All Override Options:</div>
<code class="label label-default small">
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"
</code>
</p>
</div>
<div class="col-sm-6 mb-3">
<strong>Linux 64-Bit</strong>
<p>
<a target="_blank" href="/API/ClientDownloads/UbuntuInstaller-x64/@_organizationId">Ubuntu x64 Bash Installer</a>
<br />
<a target="_blank" href="/API/ClientDownloads/ManjaroInstaller-x64/@_organizationId">Manjaro x64 Bash Installer</a>
<br />
<a target="_blank" href="/Content/Remotely-Linux.zip">Linux x64 Files Only</a>
</p>
<p>
<div class="small">Example Install:</div>
<code class="label label-default small">sudo [path]/Install-Ubuntu-x64.sh</code>
</p>
<p>
<div class="small">Example Local Install:</div>
<code class="label label-default small">sudo [path]/Install-Ubuntu-x64.sh --path [path]/Remotely-Linux.zip</code>
</p>
<p>
<div class="small">Uninstall:</div>
<code class="label label-default small">sudo [path]/Install-Ubuntu-x64.sh --uninstall</code>
</p>
</div>
<div class="col-sm-6 mb-3">
<strong>macOS x64 (10.12 - 10.15)</strong>
<p>
<a target="_blank" href="/API/ClientDownloads/MacOSInstaller-x64/@_organizationId">macOS x64 Bash Installer</a>
<br />
<a target="_blank" href="/Content/Remotely-MacOS-x64.zip">macOS x64 Files Only</a>
</p>
<p>
<div class="small">Example Install:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-x64.sh</code>
</p>
<p>
<div class="small">Example Local Install:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-x64.sh --path [path]/Remotely-MacOS-x64.zip</code>
</p>
<p>
<div class="small">Example Uninstall:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-x64.sh --uninstall</code>
</p>
</div>
<div class="col-sm-6 mb-3">
<strong>macOS arm64</strong>
<p>
<a target="_blank" href="/API/ClientDownloads/MacOSInstaller-arm64/@_organizationId">macOS arm64 Bash Installer</a>
<br />
<a target="_blank" href="/Content/Remotely-MacOS-arm64.zip">macOS arm64 Files Only</a>
</p>
<p>
<div class="small">Example Install:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-arm64.sh</code>
</p>
<p>
<div class="small">Example Local Install:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-arm64.sh --path [path]/Remotely-MacOS-arm64.zip</code>
</p>
<p>
<div class="small">Example Uninstall:</div>
<code class="label label-default small">sudo [path]/Install-MacOS-arm64.sh --uninstall</code>
</p>
</div>
}
</div>

View File

@ -19,42 +19,40 @@ namespace Remotely.Shared.Services;
public interface IEmbeddedServerDataSearcher
{
Task<Result<AppendableStream>> GetAppendedStream(string filePath, EmbeddedServerData serverData);
Task<Result<EmbeddedServerData>> TryGetEmbeddedData(string filePath);
string GetEncodedFileName(string filePath, EmbeddedServerData serverData);
Result<EmbeddedServerData> TryGetEmbeddedData(string filePath);
}
public class EmbeddedServerDataSearcher() : IEmbeddedServerDataSearcher
{
public static EmbeddedServerDataSearcher Instance { get; } = new();
public async Task<Result<EmbeddedServerData>> 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<EmbeddedServerData> TryGetEmbeddedData(string filePath)
{
try
{
if (!File.Exists(filePath))
{
return Result.Fail<EmbeddedServerData>($"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<EmbeddedServerData>(json);
if (embeddedData is null)
{
return Result.Fail<EmbeddedServerData>("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<EmbeddedServerData>(Convert.FromBase64String(base64));
return Result.Ok(embeddedData);
}
catch (Exception ex)
@ -62,21 +60,4 @@ public class EmbeddedServerDataSearcher() : IEmbeddedServerDataSearcher
return Result.Fail<EmbeddedServerData>(ex);
}
}
public Task<Result<AppendableStream>> 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<AppendableStream>(ex));
}
}
}