mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
* Convert server to new single-file startup model. * Add remote control implementations. * Implement IViewerAuthorizer. * Update hub endpoints. * Implement HubEventHandler. * Implement ViewerHubDataProvider. * Implement page data provider. * Implement RCL and refactor. * Update submodule. * Replace submodule with NuGet. * Update copy URL. * Update NuGet. * Remove deprecated WebRTC. * Remove deprecated WebRTC. * Update Immense.RemoteControl * Building out desktop projects. * Bring more services into submodule. * Update submodule. * Update submodule. * Refactoring for module. * Update submodule. * Update submodule * Got Windows desktop app running. * Refactor for submodule changes. * FIx unattended session start. * Switch desktop app out of console mode. * Fix tests. * Update publishing. * Remove ClickOnce middleware. * Remove ClickOnce remnants. * Update submodule * Add some logging. * Update Linux path. * Update submodule. * Add cleanup service for unattended sessions that failed to start. * Update submodule. * Fix chat. * Add ValidateExecutableReferencesMatchSelfContained property. * Add other submodule projects. Align checkbox. * Update submodule. Reduce deserialization in the browser, resulting in faster renders. * Update submodule. * Update submodule. * Update submodule. * Update submodule. * Add orgId back for branding. * Get branding loading in desktop apps. * Update submodule. * Create log dir. * Refactor version check on config page. * Update submodule. * Update submodule. * Change submodule URL. * Correct namespace. * Update submodule. * Checkout submodules recursively.
133 lines
5.0 KiB
C#
133 lines
5.0 KiB
C#
using Immense.RemoteControl.Shared.Models;
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
using Remotely.Agent.Interfaces;
|
|
using Remotely.Agent.Models;
|
|
using Remotely.Shared.Models;
|
|
using Remotely.Shared.Utilities;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.IO.Pipes;
|
|
using System.Runtime.Caching;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Remotely.Agent.Services
|
|
{
|
|
public class ChatClientService
|
|
{
|
|
private readonly IAppLauncher _appLauncher;
|
|
private readonly MemoryCache _chatClients = new("ChatClients");
|
|
private readonly SemaphoreSlim _messageLock = new(1,1);
|
|
|
|
public ChatClientService(IAppLauncher appLauncher)
|
|
{
|
|
_appLauncher = appLauncher;
|
|
}
|
|
|
|
private CacheItemPolicy CacheItemPolicy { get; } = new()
|
|
{
|
|
SlidingExpiration = TimeSpan.FromMinutes(10),
|
|
RemovedCallback = new CacheEntryRemovedCallback(args =>
|
|
{
|
|
var chatSession = (args.CacheItem.Value as ChatSession);
|
|
chatSession.PipeStream.Dispose();
|
|
Process.GetProcessById(chatSession.ProcessID)?.Kill();
|
|
})
|
|
};
|
|
|
|
|
|
public async Task SendMessage(
|
|
string senderName,
|
|
string message,
|
|
string orgName,
|
|
string orgId,
|
|
bool disconnected,
|
|
string senderConnectionID,
|
|
HubConnection hubConnection)
|
|
{
|
|
if (!await _messageLock.WaitAsync(30000))
|
|
{
|
|
Logger.Write("Timed out waiting for chat message lock.", Shared.Enums.EventType.Warning);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ChatSession chatSession;
|
|
if (!_chatClients.Contains(senderConnectionID))
|
|
{
|
|
if (disconnected)
|
|
{
|
|
// Don't start a new session just to show a disconnected message.
|
|
return;
|
|
}
|
|
|
|
var pipeName = Guid.NewGuid().ToString();
|
|
var procID = await _appLauncher.LaunchChatService(pipeName, senderConnectionID, senderName, orgName, orgId, hubConnection);
|
|
|
|
if (procID > 0)
|
|
{
|
|
Logger.Write($"Chat app started. Process ID: {procID}");
|
|
}
|
|
else
|
|
{
|
|
Logger.Write($"Chat app did not start successfully.");
|
|
return;
|
|
}
|
|
|
|
var clientPipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
|
|
clientPipe.Connect(15000);
|
|
if (!clientPipe.IsConnected)
|
|
{
|
|
Logger.Write("Failed to connect to chat host.");
|
|
return;
|
|
}
|
|
chatSession = new ChatSession() { PipeStream = clientPipe, ProcessID = procID };
|
|
_ = Task.Run(async () => { await ReadFromStream(chatSession.PipeStream, senderConnectionID, hubConnection); });
|
|
_chatClients.Add(senderConnectionID, chatSession, CacheItemPolicy);
|
|
}
|
|
|
|
chatSession = (ChatSession)_chatClients.Get(senderConnectionID);
|
|
|
|
if (!chatSession.PipeStream.IsConnected)
|
|
{
|
|
_chatClients.Remove(senderConnectionID);
|
|
await hubConnection.SendAsync("DisplayMessage", "Chat disconnected. Please try again.", "Chat disconnected.", "bg-warning", senderConnectionID);
|
|
return;
|
|
}
|
|
|
|
using var sw = new StreamWriter(chatSession.PipeStream, leaveOpen: true);
|
|
var chatMessage = new ChatMessage(senderName, message, disconnected);
|
|
await sw.WriteLineAsync(JsonSerializer.Serialize(chatMessage));
|
|
await sw.FlushAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Write(ex);
|
|
}
|
|
finally
|
|
{
|
|
_messageLock.Release();
|
|
}
|
|
}
|
|
|
|
private async Task ReadFromStream(NamedPipeClientStream clientPipe, string senderConnectionID, HubConnection hubConnection)
|
|
{
|
|
using var sr = new StreamReader(clientPipe, leaveOpen: true);
|
|
while (clientPipe.IsConnected)
|
|
{
|
|
var messageJson = await sr.ReadLineAsync();
|
|
if (!string.IsNullOrWhiteSpace(messageJson))
|
|
{
|
|
var chatMessage = JsonSerializer.Deserialize<ChatMessage>(messageJson);
|
|
await hubConnection.SendAsync("Chat", chatMessage.Message, false, senderConnectionID);
|
|
}
|
|
}
|
|
await hubConnection.SendAsync("Chat", string.Empty, true, senderConnectionID);
|
|
_chatClients.Remove(senderConnectionID);
|
|
}
|
|
}
|
|
}
|