diff --git a/Agent.Installer.Win/Agent.Installer.Win.csproj b/Agent.Installer.Win/Agent.Installer.Win.csproj index 0a5a2427..b62d16c4 100644 --- a/Agent.Installer.Win/Agent.Installer.Win.csproj +++ b/Agent.Installer.Win/Agent.Installer.Win.csproj @@ -8,7 +8,7 @@ WinExe Remotely.Agent.Installer.Win Remotely_Installer - v4.6.2 + v4.8 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 diff --git a/Agent.Installer.Win/App.config b/Agent.Installer.Win/App.config index 8d234373..4bfa0056 100644 --- a/Agent.Installer.Win/App.config +++ b/Agent.Installer.Win/App.config @@ -1,6 +1,6 @@ - + diff --git a/Agent/Models/ChatSession.cs b/Agent/Models/ChatSession.cs index e66ba049..bae7ebfc 100644 --- a/Agent/Models/ChatSession.cs +++ b/Agent/Models/ChatSession.cs @@ -5,6 +5,6 @@ namespace Remotely.Agent.Models public class ChatSession { public int ProcessID { get; set; } - public NamedPipeClientStream PipeStream { get; set; } + public NamedPipeServerStream PipeStream { get; set; } } } diff --git a/Agent/Program.cs b/Agent/Program.cs index b70f7f4c..30607949 100644 --- a/Agent/Program.cs +++ b/Agent/Program.cs @@ -44,7 +44,7 @@ namespace Remotely.Agent builder.AddConsole().AddDebug(); }); serviceCollection.AddSingleton(); - serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); diff --git a/Agent/Services/AgentSocket.cs b/Agent/Services/AgentSocket.cs index 99092803..75774256 100644 --- a/Agent/Services/AgentSocket.cs +++ b/Agent/Services/AgentSocket.cs @@ -26,7 +26,7 @@ namespace Remotely.Agent.Services Uninstaller uninstaller, CommandExecutor commandExecutor, ScriptRunner scriptRunner, - ChatClientService chatService, + ChatHostService chatService, IAppLauncher appLauncher, IUpdater updater) { @@ -40,7 +40,7 @@ namespace Remotely.Agent.Services } public bool IsConnected => HubConnection?.State == HubConnectionState.Connected; private IAppLauncher AppLauncher { get; } - private ChatClientService ChatService { get; } + private ChatHostService ChatService { get; } private CommandExecutor CommandExecutor { get; } private ConfigService ConfigService { get; } private ConnectionInfo ConnectionInfo { get; set; } diff --git a/Agent/Services/ChatClientService.cs b/Agent/Services/ChatHostService.cs similarity index 84% rename from Agent/Services/ChatClientService.cs rename to Agent/Services/ChatHostService.cs index 898bfe64..db8d7c92 100644 --- a/Agent/Services/ChatClientService.cs +++ b/Agent/Services/ChatHostService.cs @@ -14,9 +14,9 @@ using System.Threading.Tasks; namespace Remotely.Agent.Services { - public class ChatClientService + public class ChatHostService { - public ChatClientService(IAppLauncher appLauncher) + public ChatHostService(IAppLauncher appLauncher) { AppLauncher = appLauncher; } @@ -71,15 +71,20 @@ namespace Remotely.Agent.Services Logger.Write($"Chat app did not start successfully."); return; } + var serverStream = new NamedPipeServerStream("Remotely_Chat" + senderConnectionID, + PipeDirection.InOut, + NamedPipeServerStream.MaxAllowedServerInstances, + PipeTransmissionMode.Byte, + PipeOptions.Asynchronous); - var clientPipe = new NamedPipeClientStream(".", "Remotely_Chat" + senderConnectionID, PipeDirection.InOut, PipeOptions.Asynchronous); - clientPipe.Connect(15000); - if (!clientPipe.IsConnected) + var cts = new CancellationTokenSource(15000); + await serverStream.WaitForConnectionAsync(cts.Token); + if (!serverStream.IsConnected) { - Logger.Write("Failed to connect to chat host."); + Logger.Write("Failed to connect to chat client."); return; } - chatSession = new ChatSession() { PipeStream = clientPipe, ProcessID = procID }; + chatSession = new ChatSession() { PipeStream = serverStream, ProcessID = procID }; _ = Task.Run(async () => { await ReadFromStream(chatSession.PipeStream, senderConnectionID, hubConnection); }); ChatClients.Add(senderConnectionID, chatSession, CacheItemPolicy); } @@ -108,11 +113,11 @@ namespace Remotely.Agent.Services } } - private async Task ReadFromStream(NamedPipeClientStream clientPipe, string senderConnectionID, HubConnection hubConnection) + private async Task ReadFromStream(NamedPipeServerStream clientPipe, string senderConnectionID, HubConnection hubConnection) { + using var sr = new StreamReader(clientPipe, leaveOpen: true); while (clientPipe.IsConnected) { - using var sr = new StreamReader(clientPipe, leaveOpen: true); var messageJson = await sr.ReadLineAsync(); if (!string.IsNullOrWhiteSpace(messageJson)) { diff --git a/Desktop.Core/Interfaces/IChatHostService.cs b/Desktop.Core/Interfaces/IChatClientService.cs similarity index 80% rename from Desktop.Core/Interfaces/IChatHostService.cs rename to Desktop.Core/Interfaces/IChatClientService.cs index 9d0a2941..c138490b 100644 --- a/Desktop.Core/Interfaces/IChatHostService.cs +++ b/Desktop.Core/Interfaces/IChatClientService.cs @@ -2,7 +2,7 @@ namespace Remotely.Desktop.Core.Interfaces { - public interface IChatHostService + public interface IChatClientService { Task StartChat(string requesterID, string organizationName); } diff --git a/Desktop.Core/Services/ChatHostService.cs b/Desktop.Core/Services/ChatClientService.cs similarity index 79% rename from Desktop.Core/Services/ChatHostService.cs rename to Desktop.Core/Services/ChatClientService.cs index c1306e92..5d5f91ef 100644 --- a/Desktop.Core/Services/ChatHostService.cs +++ b/Desktop.Core/Services/ChatClientService.cs @@ -13,29 +13,29 @@ using System.Threading.Tasks; namespace Remotely.Desktop.Core.Services { - public class ChatHostService : IChatHostService + public class ChatClientService : IChatClientService { private readonly IChatUiService _chatUiService; - public ChatHostService(IChatUiService chatUiService) + public ChatClientService(IChatUiService chatUiService) { _chatUiService = chatUiService; } - private NamedPipeServerStream NamedPipeStream { get; set; } + private NamedPipeClientStream NamedPipeStream { get; set; } private StreamReader Reader { get; set; } private StreamWriter Writer { get; set; } public async Task StartChat(string requesterID, string organizationName) { - NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + NamedPipeStream = new NamedPipeClientStream(".", "Remotely_Chat" + requesterID, PipeDirection.InOut, PipeOptions.Asynchronous); Writer = new StreamWriter(NamedPipeStream); Reader = new StreamReader(NamedPipeStream); - var cts = new CancellationTokenSource(10000); + try { - await NamedPipeStream.WaitForConnectionAsync(cts.Token); + await NamedPipeStream.ConnectAsync(15_000); } catch (TaskCanceledException) { @@ -43,6 +43,12 @@ namespace Remotely.Desktop.Core.Services Environment.Exit(0); } + if (!NamedPipeStream.IsConnected) + { + Logger.Write("Failed to connect to chat host."); + return; + } + _chatUiService.ChatWindowClosed += OnChatWindowClosed; _chatUiService.ShowChatWindow(organizationName, Writer); @@ -54,7 +60,6 @@ namespace Remotely.Desktop.Core.Services { try { - NamedPipeStream?.Disconnect(); NamedPipeStream?.Dispose(); } catch { } diff --git a/Desktop.Linux/Program.cs b/Desktop.Linux/Program.cs index 77afae4b..8fd523fb 100644 --- a/Desktop.Linux/Program.cs +++ b/Desktop.Linux/Program.cs @@ -55,7 +55,7 @@ namespace Remotely.Desktop.Linux if (Conductor.Mode == Core.Enums.AppMode.Chat) { - await Services.GetRequiredService().StartChat(Conductor.RequesterID, Conductor.OrganizationName); + await Services.GetRequiredService().StartChat(Conductor.RequesterID, Conductor.OrganizationName); } else if (Conductor.Mode == Core.Enums.AppMode.Unattended) { @@ -107,7 +107,7 @@ namespace Remotely.Desktop.Linux serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); diff --git a/Desktop.Win/Program.cs b/Desktop.Win/Program.cs index 876489f5..f322f3ec 100644 --- a/Desktop.Win/Program.cs +++ b/Desktop.Win/Program.cs @@ -61,7 +61,7 @@ namespace Remotely.Desktop.Win StartUiThreads(false); _ = Task.Run(async () => { - var chatService = Services.GetRequiredService(); + var chatService = Services.GetRequiredService(); await chatService.StartChat(Conductor.RequesterID, Conductor.OrganizationName); }); } @@ -105,7 +105,7 @@ namespace Remotely.Desktop.Win serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); diff --git a/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml b/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml index 583220fc..ed9cc7b2 100644 --- a/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml +++ b/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml @@ -139,6 +139,13 @@
+
+ +
+ +
+ +

diff --git a/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml.cs b/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml.cs index 70770338..4cbc3109 100644 --- a/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml.cs +++ b/Server/Areas/Identity/Pages/Account/Manage/ServerConfig.cshtml.cs @@ -199,6 +199,8 @@ namespace Remotely.Server.Areas.Identity.Pages.Account.Manage [Display(Name = "Max Organizations")] public int MaxOrganizationCount { get; set; } + [Display(Name = "Message of the Day")] + public string MessageOfTheDay { get; set; } [Display(Name = "Redirect To HTTPS")] public bool RedirectToHttps { get; set; } diff --git a/Server/Pages/Index.cshtml b/Server/Pages/Index.cshtml index f85db172..437dd5db 100644 --- a/Server/Pages/Index.cshtml +++ b/Server/Pages/Index.cshtml @@ -1,7 +1,9 @@ @page @model IndexModel +@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnv @{ ViewData["Title"] = "Home"; + } @if (!User.Identity.IsAuthenticated) @@ -10,6 +12,15 @@ } else { + if (!string.IsNullOrWhiteSpace(Model.Motd)) + { + + + } + } \ No newline at end of file diff --git a/Server/Pages/Index.cshtml.cs b/Server/Pages/Index.cshtml.cs index 522de886..7286b771 100644 --- a/Server/Pages/Index.cshtml.cs +++ b/Server/Pages/Index.cshtml.cs @@ -1,10 +1,13 @@ -using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using Remotely.Server.Services; using Remotely.Shared.Models; +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; @@ -12,53 +15,58 @@ namespace Remotely.Server.Pages { public class IndexModel : PageModel { + private readonly IApplicationConfig _appConfig; + + private readonly IDataService _dataService; + + private readonly SignInManager _signInManager; + public IndexModel(IDataService dataService, SignInManager signInManager, IApplicationConfig appConfig) { - DataService = dataService; - SignInManager = signInManager; - AppConfig = appConfig; + _dataService = dataService; + _signInManager = signInManager; + _appConfig = appConfig; } - public string DefaultPrompt { get; set; } - public List DeviceGroups { get; set; } = new List(); public List Alerts { get; set; } = new List(); - private IApplicationConfig AppConfig { get; } - private IDataService DataService { get; } - private SignInManager SignInManager { get; } + public string DefaultPrompt { get; set; } + public string Motd { get; set; } + public List DeviceGroups { get; set; } = new List(); public async Task OnGet() { if (User?.Identity?.IsAuthenticated == true) { - var user = DataService.GetUserByName(User.Identity.Name); + var user = _dataService.GetUserByName(User.Identity.Name); if (user is null) { - await SignInManager.SignOutAsync(); + await _signInManager.SignOutAsync(); return RedirectToPage(); } - if (AppConfig.Require2FA && !user.TwoFactorEnabled) + if (_appConfig.Require2FA && !user.TwoFactorEnabled) { return RedirectToPage("TwoFactorRequired"); } - DefaultPrompt = DataService.GetDefaultPrompt(User.Identity.Name); - var groups = DataService.GetDeviceGroups(User.Identity.Name); + DefaultPrompt = _dataService.GetDefaultPrompt(User.Identity.Name); + var groups = _dataService.GetDeviceGroups(User.Identity.Name); if (groups?.Any() == true) { DeviceGroups.AddRange(groups.Select(x => new SelectListItem(x.Name, x.ID))); } - var alerts = DataService.GetAlerts(user.Id); + var alerts = _dataService.GetAlerts(user.Id); if (alerts.Any()) { Alerts.AddRange(alerts); } + Motd = _appConfig.MessageOfTheDay; } else { - DefaultPrompt = DataService.GetDefaultPrompt(); + DefaultPrompt = _dataService.GetDefaultPrompt(); } return Page(); diff --git a/Server/Services/ApplicationConfig.cs b/Server/Services/ApplicationConfig.cs index 588f5d08..e26eda1b 100644 --- a/Server/Services/ApplicationConfig.cs +++ b/Server/Services/ApplicationConfig.cs @@ -16,6 +16,7 @@ namespace Remotely.Server.Services string[] KnownProxies { get; } int MaxConcurrentUpdates { get; } int MaxOrganizationCount { get; } + string MessageOfTheDay { get; } bool RedirectToHttps { get; } bool RemoteControlNotifyUser { get; } bool RemoteControlRequiresAuthentication { get; } @@ -58,6 +59,7 @@ namespace Remotely.Server.Services public string[] KnownProxies => Config.GetSection("ApplicationOptions:KnownProxies").Get() ?? new string[0]; public int MaxConcurrentUpdates => int.Parse(Config["ApplicationOptions:MaxConcurrentUpdates"] ?? "10"); public int MaxOrganizationCount => int.Parse(Config["ApplicationOptions:MaxOrganizationCount"] ?? "1"); + public string MessageOfTheDay => Config["ApplicationOptions:MessageOfTheDay"]; public bool RedirectToHttps => bool.Parse(Config["ApplicationOptions:RedirectToHttps"] ?? "false"); public bool RemoteControlNotifyUser => bool.Parse(Config["ApplicationOptions:RemoteControlNotifyUser"] ?? "true"); public bool RemoteControlRequiresAuthentication => bool.Parse(Config["ApplicationOptions:RemoteControlRequiresAuthentication"] ?? "true"); diff --git a/Server/appsettings.json b/Server/appsettings.json index 4bcee7a8..b21db35e 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -32,6 +32,7 @@ "KnownProxies": [], "MaxConcurrentUpdates": 10, "MaxOrganizationCount": 1, + "MessageOfTheDay": "", "RedirectToHttps": false, "RemoteControlNotifyUser": true, "RemoteControlSessionLimit": 3, diff --git a/Server/wwwroot/src/Main/App.ts b/Server/wwwroot/src/Main/App.ts index 96bebe17..0884f32b 100644 --- a/Server/wwwroot/src/Main/App.ts +++ b/Server/wwwroot/src/Main/App.ts @@ -26,7 +26,19 @@ export const MainApp = { HubConnection: BrowserHubConnection, UserSettings: UserSettings, Sound: Sound, + GetMotd() { + if (!UI.MotdContentSpan.innerHTML || localStorage["remotely-motd"] == UI.MotdContentSpan.innerHTML) { + return; + } + + UI.MotdAlert.querySelector("button").addEventListener("click", () => { + localStorage["remotely-motd"] = UI.MotdContentSpan.innerHTML; + }); + + UI.MotdAlert.removeAttribute("hidden"); + }, Init() { + MainApp.GetMotd(); UI.ConsoleTextArea.focus(); ApplyInputEventHandlers(); BrowserHubConnection.Connect(); diff --git a/Server/wwwroot/src/Main/UI.ts b/Server/wwwroot/src/Main/UI.ts index 7e9a4ab7..6305579f 100644 --- a/Server/wwwroot/src/Main/UI.ts +++ b/Server/wwwroot/src/Main/UI.ts @@ -25,4 +25,6 @@ export var RightPaginationButton = document.querySelector("#rightPaginationButto export var TabContentWrapper = document.getElementById("tabContentWrapper") as HTMLDivElement; export var ToastsWrapper = document.getElementById("toastsWrapper") as HTMLDivElement; export var TotalDevicesCount = document.querySelector("#totalDevicesSpan") as HTMLSpanElement; -export var TotalPagesSpan = document.querySelector("#totalPagesSpan") as HTMLSpanElement; \ No newline at end of file +export var TotalPagesSpan = document.querySelector("#totalPagesSpan") as HTMLSpanElement; +export var MotdAlert = document.getElementById("motdAlert") as HTMLDivElement; +export var MotdContentSpan = document.getElementById("motdContentSpan") as HTMLSpanElement; \ No newline at end of file