Remotely/Shared/Services/EmbeddedServerDataSearcher.cs
2023-01-01 18:55:19 -08:00

132 lines
4.5 KiB
C#

#nullable enable
using Microsoft.Extensions.Logging;
using Remotely.Shared.Models;
using Remotely.Shared.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Remotely.Shared.Services
{
public interface IEmbeddedServerDataSearcher
{
Task<Result<RewritableStream>> GetRewrittenStream(string filePath, EmbeddedServerData serverData);
Task<Result<EmbeddedServerData>> TryGetEmbeddedData(string filePath);
}
public class EmbeddedServerDataSearcher : IEmbeddedServerDataSearcher
{
public static EmbeddedServerDataSearcher Instance { get; } = new();
public Task<Result<EmbeddedServerData>> TryGetEmbeddedData(string filePath)
{
try
{
if (!File.Exists(filePath))
{
return Task.FromResult(Result.Fail<EmbeddedServerData>($"File path does not exist: {filePath}"));
}
using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var result = SearchBuffer(fs, AppConstants.EmbeddedImmySignature);
if (result == -1)
{
return Task.FromResult(Result.Fail<EmbeddedServerData>("Signature not found in file buffer."));
}
fs.Seek(result + AppConstants.EmbeddedImmySignature.Length, SeekOrigin.Begin);
using var reader = new BinaryReader(fs, Encoding.UTF8, true);
var serializedData = reader.ReadString();
var embeddedData = JsonSerializer.Deserialize<EmbeddedServerData>(serializedData);
if (embeddedData is null)
{
return Task.FromResult(Result.Fail<EmbeddedServerData>("Embedded data is empty."));
}
return Task.FromResult(Result.Ok(embeddedData));
}
catch (Exception ex)
{
return Task.FromResult(Result.Fail<EmbeddedServerData>(ex));
}
}
public async Task<Result<RewritableStream>> GetRewrittenStream(string filePath, EmbeddedServerData serverData)
{
try
{
using var dataStream = new MemoryStream();
using var writer = new BinaryWriter(dataStream, Encoding.UTF8, true);
var serializedData = JsonSerializer.Serialize(serverData);
writer.Write(serializedData);
var dataBytes = dataStream.ToArray();
if (dataBytes.Length > AppConstants.EmbeddedDataBlockLength)
{
throw new Exception($"Embedded data size exceeds the maximum of {AppConstants.EmbeddedDataBlockLength}");
}
var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var result = SearchBuffer(fs, AppConstants.EmbeddedImmySignature);
if (result == -1)
{
await fs.DisposeAsync();
return Result.Fail<RewritableStream>("Signature not found in file buffer.");
}
var rewriteMap = new Dictionary<long, byte>();
var rewriteIndex = result + AppConstants.EmbeddedImmySignature.Length;
for (var i = 0; i < dataBytes.Length; i++)
{
rewriteMap.TryAdd(rewriteIndex++, dataBytes[i]);
}
fs.Seek(0, SeekOrigin.Begin);
var rewriteStream = new RewritableStream(fs, rewriteMap);
return Result.Ok(rewriteStream);
}
catch (Exception ex)
{
return Result.Fail<RewritableStream>(ex);
}
}
private long SearchBuffer(FileStream fileStream, byte[] matchPattern)
{
var matchSize = matchPattern.Length;
var limit = fileStream.Length - matchSize;
for (var i = 0; i <= limit; i++)
{
var k = 0;
for (; k < matchSize; k++)
{
if (matchPattern[k] != fileStream.ReadByte())
{
break;
}
}
if (k == matchSize)
{
return fileStream.Position - matchSize;
}
}
return -1;
}
}
}