Remotely/Desktop.Win/ViewModels/MainWindowViewModel.cs
2021-07-29 07:58:18 -07:00

360 lines
12 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using Remotely.Desktop.Core;
using Remotely.Desktop.Core.Interfaces;
using Remotely.Desktop.Core.Services;
using Remotely.Desktop.Win.Services;
using Remotely.Desktop.Win.Views;
using Remotely.Shared.Models;
using Remotely.Shared.Utilities;
using Remotely.Shared.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Remotely.Desktop.Win.ViewModels
{
public class MainWindowViewModel : BrandedViewModelBase
{
private readonly ICasterSocket _casterSocket;
private readonly Conductor _conductor;
private readonly IConfigService _configService;
private readonly ICursorIconWatcher _cursorIconWatcher;
private string _host;
private string _sessionID;
public MainWindowViewModel()
{
Current = this;
if (Services is null)
{
return;
}
Application.Current.Exit += Application_Exit;
_configService = Services.GetRequiredService<IConfigService>();
_cursorIconWatcher = Services.GetRequiredService<ICursorIconWatcher>();
_cursorIconWatcher.OnChange += CursorIconWatcher_OnChange;
_conductor = Services.GetRequiredService<Conductor>();
_casterSocket = Services.GetRequiredService<ICasterSocket>();
Services.GetRequiredService<IClipboardService>().BeginWatching();
Services.GetRequiredService<IKeyboardMouseInput>().Init();
_conductor.SessionIDChanged += SessionIDChanged;
_conductor.ViewerRemoved += ViewerRemoved;
_conductor.ViewerAdded += ViewerAdded;
_conductor.ScreenCastRequested += ScreenCastRequested;
}
public static MainWindowViewModel Current { get; private set; }
public static IServiceProvider Services => ServiceContainer.Instance;
public ICommand ChangeServerCommand
{
get
{
return new Executor(async (param) =>
{
PromptForHostName();
await Init();
});
}
}
public ICommand ElevateToAdminCommand
{
get
{
return new Executor((param) =>
{
try
{
//var filePath = Process.GetCurrentProcess().MainModule.FileName;
var commandLine = Win32Interop.GetCommandLine().Replace(" -elevate", "");
var sections = commandLine.Split('"', StringSplitOptions.RemoveEmptyEntries);
var filePath = sections.First();
var arguments = string.Join('"', sections.Skip(1));
var psi = new ProcessStartInfo(filePath, arguments)
{
Verb = "RunAs",
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(psi);
Environment.Exit(0);
}
// Exception can be thrown if UAC is dialog is cancelled.
catch { }
}, (param) =>
{
return !IsAdministrator;
});
}
}
public ICommand ElevateToServiceCommand
{
get
{
return new Executor((param) =>
{
try
{
var psi = new ProcessStartInfo("cmd.exe")
{
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true
};
//var filePath = Process.GetCurrentProcess().MainModule.FileName;
var commandLine = Win32Interop.GetCommandLine().Replace(" -elevate", "");
var sections = commandLine.Split('"', StringSplitOptions.RemoveEmptyEntries);
var filePath = sections.First();
var arguments = string.Join('"', sections.Skip(1));
Logger.Write($"Creating temporary service with file path {filePath} and arguments {arguments}.");
psi.Arguments = $"/c sc create Remotely_Temp binPath=\"{filePath} {arguments} -elevate\"";
Process.Start(psi).WaitForExit();
psi.Arguments = "/c sc start Remotely_Temp";
Process.Start(psi).WaitForExit();
psi.Arguments = "/c sc delete Remotely_Temp";
Process.Start(psi).WaitForExit();
App.Current.Shutdown();
}
catch { }
}, (param) =>
{
return IsAdministrator && !WindowsIdentity.GetCurrent().IsSystem;
});
}
}
public string Host
{
get => _host;
set
{
_host = value;
FirePropertyChanged();
}
}
public bool IsAdministrator => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
public ICommand RemoveViewersCommand
{
get
{
return new Executor(async (param) =>
{
foreach (Viewer viewer in (param as IList<object>).ToArray())
{
ViewerRemoved(this, viewer.ViewerConnectionID);
await _casterSocket.DisconnectViewer(viewer, true);
}
},
(param) =>
{
return (param as IList<object>)?.Count > 0;
});
}
}
public string SessionID
{
get => _sessionID;
set
{
_sessionID = value;
FirePropertyChanged();
}
}
public ObservableCollection<Viewer> Viewers { get; } = new ObservableCollection<Viewer>();
public void CopyLink()
{
Clipboard.SetText($"{Host}/RemoteControl?sessionID={SessionID?.Replace(" ", "")}");
}
public async Task GetSessionID()
{
await _casterSocket.SendDeviceInfo(_conductor.ServiceID, Environment.MachineName, _conductor.DeviceID);
await _casterSocket.GetSessionID();
}
public async Task Init()
{
SessionID = "Retrieving...";
Host = _configService.GetConfig().Host;
while (string.IsNullOrWhiteSpace(Host))
{
Host = "https://";
PromptForHostName();
}
_conductor.ProcessArgs(new string[] { "-mode", "Normal", "-host", Host });
try
{
var result = await _casterSocket.Connect(_conductor.Host);
if (result)
{
_casterSocket.Connection.Closed += (ex) =>
{
App.Current?.Dispatcher?.Invoke(() =>
{
Viewers.Clear();
SessionID = "Disconnected";
});
return Task.CompletedTask;
};
_casterSocket.Connection.Reconnecting += (ex) =>
{
App.Current?.Dispatcher?.Invoke(() =>
{
Viewers.Clear();
SessionID = "Reconnecting";
});
return Task.CompletedTask;
};
_casterSocket.Connection.Reconnected += async (arg) =>
{
await GetSessionID();
};
await DeviceInitService.GetInitParams();
ApplyBranding();
await GetSessionID();
return;
}
}
catch (Exception ex)
{
Logger.Write(ex);
}
// If we got here, something went wrong.
SessionID = "Failed";
MessageBox.Show(Application.Current.MainWindow, "Failed to connect to server.", "Connection Failed", MessageBoxButton.OK, MessageBoxImage.Warning);
}
public void PromptForHostName()
{
var prompt = new HostNamePrompt();
if (!string.IsNullOrWhiteSpace(Host))
{
prompt.ViewModel.Host = Host;
}
prompt.Owner = App.Current?.MainWindow;
prompt.ShowDialog();
var result = prompt.ViewModel.Host?.Trim()?.TrimEnd('/');
if (!Uri.TryCreate(result, UriKind.Absolute, out var serverUri) ||
(serverUri.Scheme != Uri.UriSchemeHttp && serverUri.Scheme != Uri.UriSchemeHttps))
{
Logger.Write("Server URL is not valid.");
MessageBox.Show("Server URL must be a valid Uri (e.g. https://app.remotely.one).", "Invalid Server URL", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
Host = result;
var config = _configService.GetConfig();
config.Host = Host;
_configService.Save(config);
}
public void ShutdownApp()
{
Services.GetRequiredService<IShutdownService>().Shutdown();
}
private void Application_Exit(object sender, ExitEventArgs e)
{
App.Current.Dispatcher.Invoke(() =>
{
Viewers.Clear();
});
}
private async void CursorIconWatcher_OnChange(object sender, CursorInfo cursor)
{
if (_conductor?.Viewers?.Count > 0)
{
foreach (var viewer in _conductor.Viewers.Values)
{
await viewer.SendCursorChange(cursor);
}
}
}
private void ScreenCastRequested(object sender, ScreenCastRequest screenCastRequest)
{
App.Current.Dispatcher.Invoke(() =>
{
App.Current.MainWindow.Activate();
var result = MessageBox.Show(Application.Current.MainWindow, $"You've received a connection request from {screenCastRequest.RequesterName}. Accept?", "Connection Request", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Task.Run(() =>
{
Services.GetRequiredService<IScreenCaster>().BeginScreenCasting(screenCastRequest);
});
}
else
{
// Run on another thread so it doesn't tie up the UI thread.
Task.Run(async () =>
{
await _casterSocket.SendConnectionRequestDenied(screenCastRequest.ViewerID);
});
}
});
}
private void SessionIDChanged(object sender, string sessionID)
{
var formattedSessionID = "";
for (var i = 0; i < sessionID.Length; i += 3)
{
formattedSessionID += sessionID.Substring(i, 3) + " ";
}
SessionID = formattedSessionID.Trim();
}
private void ViewerAdded(object sender, Viewer viewer)
{
App.Current.Dispatcher.Invoke(() =>
{
Viewers.Add(viewer);
});
}
private void ViewerRemoved(object sender, string viewerID)
{
App.Current.Dispatcher.Invoke(() =>
{
var viewer = Viewers.FirstOrDefault(x => x.ViewerConnectionID == viewerID);
if (viewer != null)
{
Viewers.Remove(viewer);
}
});
}
}
}