diff --git a/Agent/Services/AgentSocket.cs b/Agent/Services/AgentSocket.cs index 8aead83a..58953415 100644 --- a/Agent/Services/AgentSocket.cs +++ b/Agent/Services/AgentSocket.cs @@ -25,8 +25,9 @@ namespace Remotely.Agent.Services Uninstaller uninstaller, CommandExecutor commandExecutor, ScriptRunner scriptRunner, + ChatClientService chatService, IAppLauncher appLauncher, - ChatClientService chatService) + Updater updater) { ConfigService = configService; Uninstaller = uninstaller; @@ -34,10 +35,12 @@ namespace Remotely.Agent.Services ScriptRunner = scriptRunner; AppLauncher = appLauncher; ChatService = chatService; + Updater = updater; } public bool IsConnected => HubConnection?.State == HubConnectionState.Connected; private IAppLauncher AppLauncher { get; } private ChatClientService ChatService { get; } + private Updater Updater { get; } private CommandExecutor CommandExecutor { get; } private ConfigService ConfigService { get; } private ConnectionInfo ConnectionInfo { get; set; } @@ -267,6 +270,11 @@ namespace Remotely.Agent.Services await ScriptRunner.RunScript(mode, fileID, commandResultID, requesterID, HubConnection); }); + HubConnection.On("ReinstallAgent", async () => + { + await Updater.InstallLatestVersion(); + }); + HubConnection.On("UninstallAgent", () => { Uninstaller.UninstallAgent(); diff --git a/Agent/Services/Updater.cs b/Agent/Services/Updater.cs index 7b24849c..2903d1e2 100644 --- a/Agent/Services/Updater.cs +++ b/Agent/Services/Updater.cs @@ -17,7 +17,8 @@ namespace Remotely.Agent.Services } private ConfigService ConfigService { get; } - private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1); + private SemaphoreSlim CheckForUpdatesLock { get; } = new SemaphoreSlim(1, 1); + private SemaphoreSlim InstallLatestVersionLock { get; } = new SemaphoreSlim(1, 1); private System.Timers.Timer UpdateTimer { get; } = new System.Timers.Timer(TimeSpan.FromHours(6).TotalMilliseconds); @@ -33,11 +34,6 @@ namespace Remotely.Agent.Services UpdateTimer.Start(); } - private async void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) - { - await CheckForUpdates(); - } - public async Task CheckForUpdates() { try @@ -47,7 +43,7 @@ namespace Remotely.Agent.Services return; } - await UpdateLock.WaitAsync(); + await CheckForUpdatesLock.WaitAsync(); var connectionInfo = ConfigService.GetConnectionInfo(); var serverUrl = ConfigService.GetConnectionInfo().Host; @@ -104,15 +100,16 @@ namespace Remotely.Agent.Services } finally { - UpdateLock.Release(); + CheckForUpdatesLock.Release(); } } - public async Task InstallLatestVersion() { try { + await InstallLatestVersionLock.WaitAsync(); + var connectionInfo = ConfigService.GetConnectionInfo(); var serverUrl = connectionInfo.Host; @@ -191,9 +188,16 @@ namespace Remotely.Agent.Services { Logger.Write(ex); } + finally + { + InstallLatestVersionLock.Release(); + } } - + private async void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + await CheckForUpdates(); + } private class WebClientEx : WebClient { private readonly int _requestTimeout; diff --git a/Desktop.Win/Program.cs b/Desktop.Win/Program.cs index 93f0f3ef..db4290bd 100644 --- a/Desktop.Win/Program.cs +++ b/Desktop.Win/Program.cs @@ -185,8 +185,6 @@ namespace Remotely.Desktop.Win Logger.Write("Failed to set initial desktop."); } - Win32Interop.SetMonitorState(User32.MonitorState.MonitorStateOn); - await SendReadyNotificationToViewers(); Services.GetRequiredService().Start(); CursorIconWatcher.OnChange += CursorIconWatcher_OnChange; diff --git a/Desktop.Win/Services/ClipboardServiceWin.cs b/Desktop.Win/Services/ClipboardServiceWin.cs index de31b1b1..1b4680fe 100644 --- a/Desktop.Win/Services/ClipboardServiceWin.cs +++ b/Desktop.Win/Services/ClipboardServiceWin.cs @@ -10,7 +10,6 @@ namespace Remotely.Desktop.Win.Services { public class ClipboardServiceWin : IClipboardService { - private CancellationTokenSource _cancelTokenSource; public event EventHandler ClipboardTextChanged; @@ -19,15 +18,17 @@ namespace Remotely.Desktop.Win.Services public void BeginWatching() { - StopWatching(); - _cancelTokenSource = new CancellationTokenSource(); - App.Current.Dispatcher.Invoke(() => { App.Current.Exit -= App_Exit; App.Current.Exit += App_Exit; }); + StopWatching(); + + _cancelTokenSource = new CancellationTokenSource(); + + WatchClipboard(_cancelTokenSource.Token); } diff --git a/Desktop.Win/Services/KeyboardMouseInputWin.cs b/Desktop.Win/Services/KeyboardMouseInputWin.cs index 25e2c358..3839493f 100644 --- a/Desktop.Win/Services/KeyboardMouseInputWin.cs +++ b/Desktop.Win/Services/KeyboardMouseInputWin.cs @@ -6,6 +6,7 @@ using Remotely.Shared.Win32; using System; using System.Collections.Concurrent; using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; using static Remotely.Shared.Win32.User32; @@ -35,6 +36,12 @@ namespace Remotely.Desktop.Win.Services public void Init() { + App.Current.Dispatcher.Invoke(() => + { + App.Current.Exit -= App_Exit; + App.Current.Exit += App_Exit; + }); + StartInputProcessingThread(); } @@ -297,6 +304,7 @@ namespace Remotely.Desktop.Win.Services CancelTokenSource?.Cancel(); CancelTokenSource?.Dispose(); + // After BlockInput is enabled, only simulated input coming from the same thread // will work. So we have to start a new thread that runs continuously and // processes a queue of input events. @@ -305,12 +313,6 @@ namespace Remotely.Desktop.Win.Services Logger.Write($"New input processing thread started on thread {Thread.CurrentThread.ManagedThreadId}."); CancelTokenSource = new CancellationTokenSource(); - App.Current.Dispatcher.Invoke(() => - { - App.Current.Exit -= App_Exit; - App.Current.Exit += App_Exit; - }); - if (inputBlocked) { ToggleBlockInput(true); diff --git a/Desktop.Win/Services/SessionIndicatorWin.cs b/Desktop.Win/Services/SessionIndicatorWin.cs index a76557a2..2be84aee 100644 --- a/Desktop.Win/Services/SessionIndicatorWin.cs +++ b/Desktop.Win/Services/SessionIndicatorWin.cs @@ -19,10 +19,9 @@ namespace Remotely.Desktop.Win.Services public SessionIndicatorWin(Form backgroundForm) { BackgroundForm = backgroundForm; - Application.ApplicationExit += Application_ApplicationExit; } - private void Application_ApplicationExit(object sender, EventArgs e) + private void App_Exit(object sender, EventArgs e) { notifyIcon?.Dispose(); } @@ -38,6 +37,8 @@ namespace Remotely.Desktop.Win.Services return; } + App.Current.Exit += App_Exit; + BackgroundForm.Invoke(new Action(() => { container = new Container(); diff --git a/Server/Hubs/BrowserHub.cs b/Server/Hubs/BrowserHub.cs index 336cbf64..ec0dfe5f 100644 --- a/Server/Hubs/BrowserHub.cs +++ b/Server/Hubs/BrowserHub.cs @@ -29,9 +29,9 @@ namespace Remotely.Server.Hubs } public static ConcurrentDictionary ConnectionIdToUserLookup { get; } = new ConcurrentDictionary(); + private IHubContext AgentHubContext { get; } private IApplicationConfig AppConfig { get; } private IDataService DataService { get; } - private IHubContext AgentHubContext { get; } private RemotelyUser RemotelyUser { get @@ -133,6 +133,18 @@ namespace Remotely.Server.Hubs await base.OnDisconnectedAsync(exception); } + public Task ReinstallAgents(string[] deviceIDs) + { + deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser); + var connections = GetActiveClientConnections(deviceIDs); + foreach (var connection in connections) + { + AgentHubContext.Clients.Client(connection.Key).SendAsync("ReinstallAgent"); + } + DataService.RemoveDevices(deviceIDs); + return Clients.Caller.SendAsync("RefreshDeviceList"); + } + public async Task RemoteControl(string deviceID) { var targetDevice = AgentHub.ServiceConnections.FirstOrDefault(x => x.Value.ID == deviceID); @@ -168,6 +180,32 @@ namespace Remotely.Server.Hubs } + public Task UninstallAgents(string[] deviceIDs) + { + deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser); + var connections = GetActiveClientConnections(deviceIDs); + foreach (var connection in connections) + { + AgentHubContext.Clients.Client(connection.Key).SendAsync("UninstallAgent"); + } + DataService.RemoveDevices(deviceIDs); + return Clients.Caller.SendAsync("RefreshDeviceList"); + } + + public Task UpdateTags(string deviceID, string tags) + { + if (DataService.DoesUserHaveAccessToDevice(deviceID, RemotelyUser)) + { + if (tags.Length > 200) + { + return Clients.Caller.SendAsync("DisplayMessage", $"Tag must be 200 characters or less. Supplied length is {tags.Length}.", "Tag must be under 200 characters."); + } + DataService.UpdateTags(deviceID, tags); + return Clients.Caller.SendAsync("DisplayMessage", "Device updated successfully.", "Device updated."); + } + return Task.CompletedTask; + } + public Task UploadFiles(List fileIDs, string transferID, string[] deviceIDs) { DataService.WriteEvent(new EventLog() @@ -185,31 +223,6 @@ namespace Remotely.Server.Hubs } return Task.CompletedTask; } - public Task UninstallAgents(string[] deviceIDs) - { - deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser); - var connections = GetActiveClientConnections(deviceIDs); - foreach (var connection in connections) - { - AgentHubContext.Clients.Client(connection.Key).SendAsync("UninstallAgent"); - } - DataService.RemoveDevices(deviceIDs); - return Clients.Caller.SendAsync("RefreshDeviceList"); - } - public Task UpdateTags(string deviceID, string tags) - { - if (DataService.DoesUserHaveAccessToDevice(deviceID, RemotelyUser)) - { - if (tags.Length > 200) - { - return Clients.Caller.SendAsync("DisplayMessage", $"Tag must be 200 characters or less. Supplied length is {tags.Length}.", "Tag must be under 200 characters."); - } - DataService.UpdateTags(deviceID, tags); - return Clients.Caller.SendAsync("DisplayMessage", "Device updated successfully.", "Device updated."); - } - return Task.CompletedTask; - } - private IEnumerable> GetActiveClientConnections(string[] deviceIDs) { return AgentHub.ServiceConnections.Where(x => diff --git a/Server/wwwroot/src/Main/Commands/WebCommands.ts b/Server/wwwroot/src/Main/Commands/WebCommands.ts index 7d10842e..c9b5bbe0 100644 --- a/Server/wwwroot/src/Main/Commands/WebCommands.ts +++ b/Server/wwwroot/src/Main/Commands/WebCommands.ts @@ -262,6 +262,16 @@ var commands: Array = [ AddConsoleOutput(output); } ), + new ConsoleCommand("Reinstall", + [], + "Reinstalls the Remotely agent on the selected devices.", + "reinstall", + "", + (parameters, parameterDict) => { + var selectedDevices = DataGrid.GetSelectedDevices(); + BrowserHubConnection.Connection.invoke("ReinstallAgents", selectedDevices.map(x => x.ID)); + } + ), new ConsoleCommand( "Remove", [ @@ -457,7 +467,7 @@ var commands: Array = [ ), new ConsoleCommand("Uninstall", [], - "Uninstalls the Remotely client from the selected devices. Warning: This can't be undone from the web portal. You would need to redeploy the client.", + "Uninstalls the Remotely agent from the selected devices. Warning: This can't be undone from the web portal. You would need to redeploy the agent.", "uninstall", "", (parameters, parameterDict) => {