Skip to content

Commit

Permalink
Degrade gracefully if registry access unavailable
Browse files Browse the repository at this point in the history
Fixes #2
  • Loading branch information
canton7 committed Feb 25, 2015
1 parent 86e4f15 commit 8753f8e
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 32 deletions.
7 changes: 4 additions & 3 deletions src/SyncTrayzor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x64.ActiveCfg = Debug|x86
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x64.Build.0 = Debug|x86
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x86.ActiveCfg = Debug|x64
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x64.ActiveCfg = Debug|x64
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x64.Build.0 = Debug|x64
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x86.ActiveCfg = Debug|x86
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Debug|x86.Build.0 = Debug|x86
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Release|x64.ActiveCfg = Release|x64
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Release|x64.Build.0 = Release|x64
{D1F89B3D-7967-4DC6-AE45-50A7817FE54F}.Release|x86.ActiveCfg = Release|x64
Expand Down
2 changes: 1 addition & 1 deletion src/SyncTrayzor/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protected override void ConfigureIoC(IStyletIoCBuilder builder)
{
builder.Bind<IApplicationState>().ToInstance(new ApplicationState(this.Application));
builder.Bind<IConfigurationProvider>().To<ConfigurationProvider>().InSingletonScope();
builder.Bind<AutostartProvider>().ToSelf().InSingletonScope();
builder.Bind<IAutostartProvider>().To<AutostartProvider>().InSingletonScope();
builder.Bind<ConfigurationApplicator>().ToSelf().InSingletonScope();
builder.Bind<ISyncThingApiClient>().To<SyncThingApiClient>().InSingletonScope();
builder.Bind<ISyncThingEventWatcher>().To<SyncThingEventWatcher>().InSingletonScope();
Expand Down
21 changes: 15 additions & 6 deletions src/SyncTrayzor/Pages/SettingsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
xmlns:pages="clr-namespace:SyncTrayzor.Pages"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance pages:SettingsViewModel}"
Title="SettingsView" Height="500" Width="300"
Title="SettingsView" Height="500" Width="400"
ResizeMode="NoResize" SizeToContent="Height">
<Window.Resources>
<s:BoolToVisibilityConverter x:Key="NoWatchedFoldersVisibilityConverter" TrueVisibility="Collapsed" FalseVisibility="Visible"/>
<s:BoolToVisibilityConverter x:Key="InverseVisibilityConverter" TrueVisibility="Collapsed" FalseVisibility="Visible"/>
</Window.Resources>
<DockPanel Margin="10">
<DockPanel.Resources>
<Style TargetType="GroupBox">
<Setter Property="Padding" Value="5"/>
<Setter Property="Margin" Value="0,5,0,5"/>
</Style>
</DockPanel.Resources>

Expand All @@ -37,18 +38,26 @@
</DockPanel>
</GroupBox>

<GroupBox DockPanel.Dock="Top" Header="Autostart">
<GroupBox DockPanel.Dock="Top" Header="Start on Login">
<DockPanel>
<CheckBox DockPanel.Dock="Top" IsChecked="{Binding StartOnLogon}">Automatically start on login</CheckBox>
<CheckBox DockPanel.Dock="Top" IsChecked="{Binding StartMinimized}" IsEnabled="{Binding StartOnLogon}">
<TextBlock DockPanel.Dock="Top" Visibility="{Binding CanReadAndWriteAutostart, Converter={StaticResource InverseVisibilityConverter}}"
TextWrapping="WrapWithOverflow" Margin="0,0,0,5">
Permission denied. Please start SyncTrayzor as an Administrator to change these settings.
</TextBlock>
<CheckBox DockPanel.Dock="Top" IsChecked="{Binding StartOnLogon}"
IsEnabled="{Binding CanWriteAutostart}" Visibility="{Binding CanReadOrWriteAutostart, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
Automatically start on login
</CheckBox>
<CheckBox DockPanel.Dock="Top" IsChecked="{Binding StartMinimized}"
IsEnabled="{Binding StartMinimizedEnabled}" Visibility="{Binding CanReadOrWriteAutostart, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
Start Minimized
</CheckBox>
</DockPanel>
</GroupBox>

<GroupBox DockPanel.Dock="Top" Header="Watched Folders">
<DockPanel>
<TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" Visibility="{Binding WatchedFolders, Converter={StaticResource NoWatchedFoldersVisibilityConverter}}">
<TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" Visibility="{Binding WatchedFolders, Converter={StaticResource InverseVisibilityConverter}}">
Folders unavailable. Please start Syncthing
</TextBlock>
<TextBlock DockPanel.Dock="Top" TextWrapping="Wrap" Margin="0,0,0,10" Visibility="{Binding WatchedFolders, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
Expand Down
21 changes: 20 additions & 1 deletion src/SyncTrayzor/Pages/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,27 @@ public class SettingsViewModel : Screen
public bool StartSyncThingAutomatically { get; set; }
public string SyncThingAddress { get; set; }
public string SyncThingApiKey { get; set; }

public bool CanReadAutostart { get; set; }
public bool CanWriteAutostart { get; set; }
public bool CanReadOrWriteAutostart
{
get { return this.CanReadAutostart || this.CanWriteAutostart; }
}
public bool CanReadAndWriteAutostart
{
get { return this.CanReadAutostart && this.CanWriteAutostart; }
}
public bool StartOnLogon { get; set; }
public bool StartMinimized { get; set; }
public bool StartMinimizedEnabled
{
get { return this.CanReadAndWriteAutostart && this.StartOnLogon; }
}

public BindableCollection<WatchedFolder> WatchedFolders { get; set; }

public SettingsViewModel(IConfigurationProvider configurationProvider)
public SettingsViewModel(IConfigurationProvider configurationProvider, IAutostartProvider autostartProvider)
{
this.DisplayName = "Settings";

Expand All @@ -50,6 +66,9 @@ public SettingsViewModel(IConfigurationProvider configurationProvider)
Folder = x.ID,
IsSelected = x.IsWatched
}));

this.CanReadAutostart = autostartProvider.CanRead;
this.CanWriteAutostart = autostartProvider.CanWrite;
}

public void Save()
Expand Down
79 changes: 61 additions & 18 deletions src/SyncTrayzor/Services/AutostartProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,100 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace SyncTrayzor.Services
{
public interface IAutostartProvider
{
bool CanRead { get; }
bool CanWrite { get; }

AutostartConfiguration GetCurrentSetup();
void SetAutoStart(AutostartConfiguration config);
}

public class AutostartConfiguration
{
public bool AutoStart { get; set; }
public bool StartMinimized { get; set; }
}

public class AutostartProvider
public class AutostartProvider : IAutostartProvider
{
private const string applicationName = "SyncTrayzor";

private RegistryKey OpenRegistryKey()
public bool CanRead { get; private set; }
public bool CanWrite { get; private set; }

public AutostartProvider()
{
return Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
// Check our access
try
{
this.OpenRegistryKey(true).Dispose();
this.CanWrite = true;
this.CanRead = true;
return;
}
catch (SecurityException) { }

try
{
this.OpenRegistryKey(false).Dispose();
this.CanRead = true;
}
catch (SecurityException) { }
}

private RegistryKey OpenRegistryKey(bool writable)
{
return Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", writable);
}

public AutostartConfiguration GetCurrentSetup()
{
if (!this.CanRead)
throw new InvalidOperationException("Don't have permission to read the registry");

bool autoStart = false;
bool startMinimized = false;

var registryKey = this.OpenRegistryKey();
var value = registryKey.GetValue(applicationName) as string;
if (value != null)
using (var registryKey = this.OpenRegistryKey(false))
{
autoStart = true;
if (value.Contains(" -minimized"))
startMinimized = true;
var value = registryKey.GetValue(applicationName) as string;
if (value != null)
{
autoStart = true;
if (value.Contains(" -minimized"))
startMinimized = true;
}
}

return new AutostartConfiguration() { AutoStart = autoStart, StartMinimized = startMinimized };
}

public void SetAutoStart(AutostartConfiguration config)
{
var registryKey = this.OpenRegistryKey();
var keyExists = registryKey.GetValue(applicationName) != null;
if (!this.CanWrite)
throw new InvalidOperationException("Don't have permission to write to the registry");

if (config.AutoStart)
{
var path = String.Format("\"{0}\"{1}", Assembly.GetExecutingAssembly().Location, config.StartMinimized ? " -minimized" : "");
registryKey.SetValue(applicationName, path);
}
else if (keyExists)
using (var registryKey = this.OpenRegistryKey(true))
{
registryKey.DeleteValue(applicationName);
var keyExists = registryKey.GetValue(applicationName) != null;

if (config.AutoStart)
{
var path = String.Format("\"{0}\"{1}", Assembly.GetExecutingAssembly().Location, config.StartMinimized ? " -minimized" : "");
registryKey.SetValue(applicationName, path);
}
else if (keyExists)
{
registryKey.DeleteValue(applicationName);
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/SyncTrayzor/Services/ConfigurationApplicator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class ConfigurationApplicator

private readonly INotifyIconManager notifyIconManager;
private readonly ISyncThingManager syncThingManager;
private readonly AutostartProvider autostartProvider;
private readonly IAutostartProvider autostartProvider;
private readonly IWatchedFolderMonitor watchedFolderMonitor;
private readonly IGithubApiClient githubApiClient;
private readonly IUpdateChecker updateChecker;
Expand All @@ -25,7 +25,7 @@ public ConfigurationApplicator(
IConfigurationProvider configurationProvider,
INotifyIconManager notifyIconManager,
ISyncThingManager syncThingManager,
AutostartProvider autostartProvider,
IAutostartProvider autostartProvider,
IWatchedFolderMonitor watchedFolderMonitor,
IGithubApiClient githubApiClient,
IUpdateChecker updateChecker)
Expand Down Expand Up @@ -61,6 +61,10 @@ private void UpdateConfigOnInit()

private void UpdateAutostart()
{
// Don't have permission? Meh
if (!this.autostartProvider.CanRead)
return;

// If the user's manually updated the registry themselves, update our config to match
var config = this.configurationProvider.Load();
var autostartConfig = this.autostartProvider.GetCurrentSetup();
Expand Down Expand Up @@ -90,7 +94,9 @@ private void ApplyNewConfiguration(Configuration configuration)
this.syncThingManager.ExecutablePath = configuration.SyncthingPath;
this.syncThingManager.ApiKey = configuration.SyncthingApiKey;

this.autostartProvider.SetAutoStart(new AutostartConfiguration() { AutoStart = configuration.StartOnLogon, StartMinimized = configuration.StartMinimized });
// Don't have permission? Meh
if (this.autostartProvider.CanWrite)
this.autostartProvider.SetAutoStart(new AutostartConfiguration() { AutoStart = configuration.StartOnLogon, StartMinimized = configuration.StartMinimized });

this.watchedFolderMonitor.WatchedFolderIDs = configuration.Folders.Where(x => x.IsWatched).Select(x => x.ID);

Expand Down

0 comments on commit 8753f8e

Please sign in to comment.