auth: Move key management to library

This commit is contained in:
Hylke Bons 2016-03-26 15:59:43 +00:00
parent cf8d82462f
commit 776c3a4c02
25 changed files with 109 additions and 275 deletions

View file

@ -0,0 +1,23 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace SparkleLib {
public abstract class AuthenticationInfo
{
}
}

View file

@ -15,12 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.IO;
using System.Diagnostics;
using SparkleLib;
namespace SparkleLib.Git {
public class SparkleGit : SparkleProcess {

View file

@ -22,7 +22,6 @@ using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using SparkleLib;
namespace SparkleLib.Git {

View file

@ -4,6 +4,8 @@ TARGET = library
ASSEMBLY_INFO_SOURCE = Defines.cs
SOURCES = \
AuthenticationInfo.cs \
SSHAuthenticationInfo.cs \
SparkleBackend.cs \
SparkleConfig.cs \
SparkleExtensions.cs \

View file

@ -87,11 +87,11 @@ namespace SparkleLib {
process.WaitForExit ();
if (process.ExitCode == 0) {
Properties ["PrivateKeyFilePath"] = key_file_path;
Properties ["PrivateKey"] = IO.File.ReadAllText (key_file_path);
PrivateKeyFilePath = key_file_path;
PrivateKey = IO.File.ReadAllText (key_file_path);
Properties ["PublicKeyFilePath"] = key_file_path + ".pub";
Properties ["PublicKey"] = IO.File.ReadAllText (key_file_path + ".pub");
PublicKeyFilePath = key_file_path + ".pub";
PublicKey = IO.File.ReadAllText (key_file_path + ".pub");
SparkleLogger.LogInfo ("Auth", "Created key pair: " + key_file_name);
return true;
@ -112,7 +112,7 @@ namespace SparkleLib {
SparkleLogger.LogInfo ("Auth", "No key agent running, starting one...");
SparkleProcess process = new SparkleProcess ("ssh-agent", "");
var process = new SparkleProcess ("ssh-agent", null);
string output = process.StartAndReadStandardOutput ();
Match auth_sock_match = new Regex (@"SSH_AUTH_SOCK=([^;\n\r]*)").Match (output);

View file

@ -123,7 +123,7 @@ namespace SparkleLib {
user_name = Environment.UserName;
}
// TODO: Don't do this manually
string n = Environment.NewLine;
File.WriteAllText (FullPath,
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + n +
@ -132,6 +132,7 @@ namespace SparkleLib {
" <name>" + user_name + "</name>" + n +
" <email>Unknown</email>" + n +
" </user>" + n +
" <notifications>True</notifications>" + n +
"</sparkleshare>");
}
@ -140,30 +141,19 @@ namespace SparkleLib {
get {
XmlNode name_node = SelectSingleNode ("/sparkleshare/user/name/text()");
XmlNode email_node = SelectSingleNode ("/sparkleshare/user/email/text()");
string user_name = name_node.Value;
string user_email = email_node.Value;
SparkleUser user = new SparkleUser (user_name, user_email);
string name = name_node.Value;
string email = email_node.Value;
string ssh_path = Path.Combine (Path.GetDirectoryName (FullPath), "ssh");
string [] private_key_file_paths = Directory.GetFiles(ssh_path, "*.key");
if (private_key_file_paths.Length > 0) {
user.PrivateKey = File.ReadAllText (private_key_file_paths [0]);
user.PrivateKeyFilePath = private_key_file_paths [0];
user.PublicKey = File.ReadAllText (private_key_file_paths [0] + ".pub");
user.PublicKeyFilePath = private_key_file_paths [0] + ".pub";
}
return user;
return new SparkleUser (name, email);
}
set {
SparkleUser user = (SparkleUser) value;
XmlNode name_node = SelectSingleNode ("/sparkleshare/user/name/text()");
XmlNode email_node = SelectSingleNode ("/sparkleshare/user/email/text()");
XmlNode name_node = SelectSingleNode ("/sparkleshare/user/name/text()");
XmlNode email_node = SelectSingleNode ("/sparkleshare/user/email/text()");
name_node.InnerText = user.Name;
email_node.InnerText = user.Email;

View file

@ -47,6 +47,8 @@
<Compile Include="Defines.cs" />
<Compile Include="SparkleFetcherSSH.cs" />
<Compile Include="SparkleProcess.cs" />
<Compile Include="AuthenticationInfo.cs" />
<Compile Include="SSHAuthenticationInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -19,12 +19,11 @@ using System;
using System.Diagnostics;
using System.IO;
namespace SparkleLib
{
namespace SparkleLib {
public class SparkleProcess : Process {
public SparkleProcess (string path, string args) : base ()
{
public SparkleProcess (string path, string args) {
StartInfo.FileName = path;
StartInfo.Arguments = args;
StartInfo.UseShellExecute = false;
@ -37,8 +36,8 @@ namespace SparkleLib
new public void Start ()
{
SparkleLogger.LogInfo ("Cmd | " + System.IO.Path.GetFileName (StartInfo.WorkingDirectory),
System.IO.Path.GetFileName (StartInfo.FileName) + " " + StartInfo.Arguments);
SparkleLogger.LogInfo ("Cmd | " + Path.GetFileName (StartInfo.WorkingDirectory),
Path.GetFileName (StartInfo.FileName) + " " + StartInfo.Arguments);
try {
base.Start ();
@ -86,7 +85,7 @@ namespace SparkleLib
protected string LocateCommand (string name)
{
string [] possible_command_paths = new string [] {
string [] possible_command_paths = {
Environment.GetFolderPath (Environment.SpecialFolder.Personal) + "/bin/" + name,
Defines.INSTALL_DIR + "/bin/" + name,
"/usr/local/bin/" + name,

View file

@ -15,8 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace SparkleLib {
public class SparkleUser {
@ -26,12 +24,6 @@ namespace SparkleLib {
public string AvatarFilePath;
public string PrivateKey;
public string PrivateKeyFilePath;
public string PublicKey;
public string PublicKeyFilePath;
public SparkleUser (string name, string email)
{

View file

@ -18,7 +18,6 @@ SOURCES = \
../SparkleEventLogController.cs \
../SparkleExtensions.cs \
../SparkleInvite.cs \
../SparkleKeys.cs \
../SparkleNoteController.cs \
../SparklePlugin.cs \
../SparkleSetupController.cs \

View file

@ -1,12 +1,11 @@
## Building on Linux distributions
You can choose to build SparkleShare from source or to get the package through your distribution's repositories.
To run SparkleShare, you'll need the following packages:
To run SparkleShare, you'll need these packages:
**Note:** Git 1.9 changed the way local projects without a history are handled, and may cause protocol errors. Until a solution is found, it's recommended to use Git 1.8.
```
git 1.8.x
git >= 2.3
gtk-sharp3
mono-core >= 2.8
notify-sharp
@ -67,7 +66,7 @@ https://github.com/xDarkice/appindicator-sharp
### Starting the build
### Start the build
You can build and install SparkleShare like this:
@ -78,7 +77,7 @@ $ sudo make install
```
### Resetting SparkleShare settings
### Reset SparkleShare settings
```
rm -Rf ~/SparkleShare
@ -86,8 +85,9 @@ rm -Rf ~/.config/sparkleshare
```
### Uninstalling
### Uninstall
```
sudo make uninstall
```

View file

@ -658,7 +658,7 @@ namespace SparkleShare {
HBox layout_horizontal = new HBox (false, 6);
Entry link_code_entry = new Entry () {
Text = Program.Controller.CurrentUser.PublicKey,
Text = Program.Controller.UserAuthenticationInfo.PublicKey,
Sensitive = false
};

View file

@ -230,7 +230,7 @@ namespace SparkleShare {
if (Controller.LinkCodeItemEnabled) {
link_code_item.Submenu = new Menu ();
string link_code = Program.Controller.CurrentUser.PublicKey.Substring (0, 20) + "...";
string link_code = Program.Controller.UserAuthenticationInfo.PublicKey.Substring (0, 20) + "...";
MenuItem code_item = new MenuItem (link_code) { Sensitive = false };
MenuItem copy_item = new MenuItem ("Copy to Clipboard");

View file

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>

View file

@ -18,7 +18,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using MonoMac.Foundation;
using MonoMac.AppKit;
@ -31,14 +30,15 @@ namespace SparkleShare {
public string Header;
new public string Description;
private NSImage side_splash;
private NSImageView side_splash_view;
private NSTextField header_text_field, description_text_field;
NSImage side_splash;
NSImageView side_splash_view;
NSTextField header_text_field;
NSTextField description_text_field;
public SparkleSetupWindow (IntPtr handle) : base (handle) { }
public SparkleSetupWindow () : base ()
public SparkleSetupWindow ()
{
SetFrame (new RectangleF (0, 0, 640, 420), true);
@ -132,13 +132,13 @@ namespace SparkleShare {
if (Program.UI != null)
Program.UI.UpdateDockIconVisibility ();
base.OrderFrontRegardless ();
OrderFrontRegardless ();
}
public override void PerformClose (NSObject sender)
{
base.OrderOut (this);
OrderOut (this);
NSApplication.SharedApplication.RemoveWindowsItem (this);
if (Program.UI != null)

View file

@ -33,6 +33,7 @@
<ConsolePause>False</ConsolePause>
<UseSGen>false</UseSGen>
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<Optimize>False</Optimize>
@ -53,6 +54,7 @@
<DebugSymbols>true</DebugSymbols>
<UseSGen>false</UseSGen>
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseDist|AnyCPU' ">
<Optimize>false</Optimize>
@ -69,6 +71,8 @@
</CustomCommands>
<EnableCodeSigning>false</EnableCodeSigning>
<CreatePackage>false</CreatePackage>
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -123,9 +127,6 @@
<Compile Include="..\SparkleInvite.cs">
<Link>SparkleInvite.cs</Link>
</Compile>
<Compile Include="..\SparkleKeys.cs">
<Link>SparkleKeys.cs</Link>
</Compile>
<Compile Include="..\SparkleAvatars.cs" />
<Compile Include="..\SparkleNoteController.cs">
<Link>SparkleNoteController.cs</Link>
@ -133,7 +134,7 @@
<Compile Include="SparkleNote.cs" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="MainMenu.xib" xmlns="" />
<InterfaceDefinition Include="MainMenu.xib" />
</ItemGroup>
<ItemGroup>
<None Include="Info.plist" />

View file

@ -34,7 +34,6 @@ Global
{CF5BC8DB-A633-4FCC-8A3E-E3AC9B59FABC}.ReleaseDist|Any CPU.Build.0 = ReleaseDist|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = SparkleShare.csproj
Policies = $0
$0.DotNetNamingPolicy = $1
$1.DirectoryNamespaceAssociation = None
@ -52,5 +51,6 @@ Global
$4.inheritsScope = text/plain
$4.scope = text/plain
version =
StartupItem = SparkleShare.csproj
EndGlobalSection
EndGlobal

View file

@ -135,7 +135,7 @@ namespace SparkleShare {
this.link_code_submenu = new NSMenu ();
this.code_item = new NSMenuItem ();
this.code_item.Title = Program.Controller.CurrentUser.PublicKey.Substring (0, 20) + "...";
this.code_item.Title = Program.Controller.UserAuthenticationInfo.PublicKey.Substring (0, 20) + "...";
this.copy_item = new NSMenuItem ();
this.copy_item.Title = "Copy to Clipboard";

View file

@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using MonoMac.Foundation;
using MonoMac.AppKit;
using MonoMac.Foundation;
namespace SparkleShare {
@ -33,6 +33,7 @@ namespace SparkleShare {
public static string FontName = "Helvetica Neue";
public SparkleUI ()
{
if (Environment.OSVersion.Version.Major < 14)
@ -72,8 +73,8 @@ namespace SparkleShare {
public void UpdateDockIconVisibility ()
{
// if (Setup.IsVisible || EventLog.IsVisible || About.IsVisible)
// NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Regular;
// if (Setup.IsVisible || EventLog.IsVisible || About.IsVisible)
// NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Regular;
}
}

View file

@ -68,7 +68,7 @@ namespace SparkleShare
return null;
}
WebClient client = new WebClient ();
var client = new WebClient ();
string url = "https://gravatar.com/avatar/" + email.MD5 () + ".png?s=" + size + "&d=404";
try {

View file

@ -22,6 +22,7 @@ using System.Linq;
using System.Threading;
using SparkleLib;
using SparkleLib.Git;
namespace SparkleShare {
@ -117,12 +118,16 @@ namespace SparkleShare {
return folders;
}
}
public SparkleUser CurrentUser {
get { return Config.User; }
set { Config.User = value; }
}
public SSHAuthenticationInfo UserAuthenticationInfo;
public bool NotificationsEnabled {
get {
string notifications_enabled = Config.GetConfigOption ("notifications");
@ -204,8 +209,9 @@ namespace SparkleShare {
public virtual void Initialize ()
{
SparkleLogger.LogInfo ("Environment", "SparkleShare version: " + SparkleLib.SparkleBackend.Version +
", Operating system: " + SparkleLib.SparkleBackend.Platform + " (" + Environment.OSVersion + ")");
SparkleLogger.LogInfo ("Environment", SparkleLib.SparkleBackend.Platform + " (" + Environment.OSVersion + ")");
// TODO: SparkleLogger.LogInfo ("Environment", "Git version: ");
SparkleLogger.LogInfo ("Environment", "SparkleShare " + SparkleLib.SparkleBackend.Version);
SparklePlugin.PluginsPath = PluginsPath;
InstallProtocolHandler ();
@ -217,50 +223,18 @@ namespace SparkleShare {
} catch (DirectoryNotFoundException) {
this.lost_folders_path = true;
}
bool keys_imported = false;
if (FirstRun) {
Config.SetConfigOption ("notifications", bool.TrueString);
new Thread (() => {
UserAuthenticationInfo = new SSHAuthenticationInfo ();
FolderListChanged (); // FIXME: Hacky way to update status icon menu to show the key
}).Start ();
} else {
string keys_path = Path.GetDirectoryName (Config.FullPath);
string key_file_path = "";
foreach (string file_path in Directory.GetFiles (keys_path)) {
string file_name = Path.GetFileName(file_path);
if (file_name.EndsWith (".key")) {
key_file_path = Path.Combine (keys_path, file_name);
// Replace spaces with underscores in old keys
if (file_name.Contains (" ")) {
string new_file_name = file_name.Replace (" ", "_");
File.Move (key_file_path, Path.Combine (keys_path, new_file_name));
File.Move (key_file_path + ".pub", Path.Combine (keys_path, new_file_name + ".pub"));
key_file_path = Path.Combine (keys_path, new_file_name);
}
SparkleKeys.ImportPrivateKey (key_file_path);
keys_imported = true;
break;
}
}
if (keys_imported) {
CurrentUser.PublicKey = File.ReadAllText (key_file_path + ".pub");
} else {
string [] key_pair = CreateKeys ();
SparkleKeys.ImportPrivateKey (key_pair [0]);
CurrentUser.PublicKey = File.ReadAllText (key_pair [1]);
}
SparkleKeys.ListPrivateKeys ();
FolderListChanged (); // FIXME: Hacky way to update status icon menu to show the key
UserAuthenticationInfo = new SSHAuthenticationInfo ();
FolderListChanged ();
}
// Watch the SparkleShare folder
this.watcher = new FileSystemWatcher () {
Filter = "*",
@ -269,9 +243,6 @@ namespace SparkleShare {
};
watcher.Created += OnFolderActivity;
// FIXME watcher.Deleted += OnFolderActivity;
// FIXME watcher.Renamed += OnFolderActivity;
watcher.EnableRaisingEvents = true;
}
@ -305,16 +276,6 @@ namespace SparkleShare {
if (FirstRun) {
ShowSetupWindow (PageType.Setup);
new Thread (() => {
string [] key_pair = CreateKeys ();
SparkleKeys.ImportPrivateKey (key_pair [0]);
CurrentUser.PublicKey = File.ReadAllText (key_pair [1]);
FolderListChanged (); // FIXME: Hacky way to update status icon menu to show the key
}).Start ();
} else {
new Thread (() => {
StartupInviteScan ();
@ -504,24 +465,16 @@ namespace SparkleShare {
AddRepository (repo);
repo.Initialize ();
}
private void OnFolderActivity (object o, FileSystemEventArgs args)
{
if (args != null && args.FullPath.EndsWith (".xml") &&
args.ChangeType == WatcherChangeTypes.Created) {
HandleInvite (args);
return;
}/* else { FIXME: on the fly folder removal doesn't always work. disabling for now
Thread.Sleep (1000);
if (Directory.Exists (args.FullPath) && args.ChangeType == WatcherChangeTypes.Created)
return;
CheckRepositories ();
}*/
}
}
@ -758,37 +711,25 @@ namespace SparkleShare {
Environment.Exit (0);
}
private void ClearDirectoryAttributes (string path)
{
if (!Directory.Exists (path))
return;
string [] folders = Directory.GetDirectories (path);
foreach (string folder in folders)
ClearDirectoryAttributes (folder);
string [] files = Directory.GetFiles(path);
string [] files = Directory.GetFiles (path);
foreach (string file in files)
if (!IsSymlink (file))
File.SetAttributes (file, FileAttributes.Normal);
}
private string [] CreateKeys ()
{
// TODO: Make a proper member for this
string config_path = Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath);
string keys_path = Path.Combine (config_path, "ssh");
string key_file_name = DateTime.Now.ToString ("yyyy-MM-dd_HH\\hmm");
return SparkleKeys.GenerateKeyPair (keys_path, key_file_name);
}
private bool IsSymlink (string file)
{

View file

@ -1,104 +0,0 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using SparkleLib;
namespace SparkleShare {
public static class SparkleKeys {
public static string [] GenerateKeyPair (string output_path, string key_name)
{
key_name += ".key";
string key_file_path = Path.Combine (output_path, key_name);
if (!Directory.Exists (output_path))
Directory.CreateDirectory (output_path);
if (File.Exists (key_file_path)) {
SparkleLogger.LogInfo ("Auth", "A key pair exists ('" + key_name + "'), leaving it untouched");
return new string [] { key_file_path, key_file_path + ".pub" };
}
string computer_name = System.Net.Dns.GetHostName ();
if (computer_name.EndsWith (".local"))
computer_name = computer_name.Substring (0, computer_name.Length - 6);
string arguments = "-t rsa " + // crypto type
"-b 4096 " + // key size
"-P \"\" " + // empty password
"-C \"" + computer_name + " (SparkleShare)\" " + // key comment
"-f \"" + key_name + "\""; // file name
SparkleProcess process = new SparkleProcess ("ssh-keygen", arguments);
process.StartInfo.WorkingDirectory = output_path;
process.Start ();
process.WaitForExit ();
return new string [] { key_file_path, key_file_path + ".pub" };
}
public static void ImportPrivateKey (string key_file_path)
{
StartKeyAgent ();
// Use forward slashes when dealing with Windows domain accounts
if (key_file_path.StartsWith ("\\\\"))
key_file_path = key_file_path.Replace ("\\", "/");
SparkleProcess process = new SparkleProcess ("ssh-add", "\"" + key_file_path + "\"");
process.Start ();
process.WaitForExit ();
}
public static void ListPrivateKeys ()
{
SparkleProcess process = new SparkleProcess ("ssh-add", "-l");
process.Start ();
string keys_in_use = process.StandardOutput.ReadToEnd ();
process.WaitForExit ();
SparkleLogger.LogInfo ("Auth", "The following keys may be used:\n" + keys_in_use.Trim ());
}
private static void StartKeyAgent ()
{
Process [] processes = Process.GetProcessesByName ("ssh-agent");
if (processes.Length == 0) {
SparkleLogger.LogInfo ("Auth", "No key agent running, starting one...");
SparkleProcess process = new SparkleProcess ("ssh-agent", "");
string output = process.StartAndReadStandardOutput ();
Match auth_sock_match = new Regex (@"SSH_AUTH_SOCK=([^;\n\r]*)").Match (output);
if (auth_sock_match.Success)
Environment.SetEnvironmentVariable ("SSH_AUTH_SOCK", auth_sock_match.Groups [1].Value);
}
}
}
}

View file

@ -435,7 +435,7 @@ namespace SparkleShare {
ChangePageEvent (PageType.Syncing, null);
new Thread (() => {
if (!PendingInvite.Accept (Program.Controller.CurrentUser.PublicKey)) {
if (!PendingInvite.Accept (Program.Controller.UserAuthenticationInfo.PublicKey)) {
PreviousUrl = PendingInvite.Address + PendingInvite.RemotePath.TrimStart ("/".ToCharArray ());
ChangePageEvent (PageType.Error, new string [] { "error: Failed to upload the public key" });
return;
@ -549,7 +549,7 @@ namespace SparkleShare {
public void CopyToClipboardClicked ()
{
Program.Controller.CopyToClipboard (Program.Controller.CurrentUser.PublicKey);
Program.Controller.CopyToClipboard (Program.Controller.UserAuthenticationInfo.PublicKey);
}

View file

@ -49,7 +49,7 @@ namespace SparkleShare {
string status_message = "Waiting to sync";
if (!this.repo.LastSync.Equals (DateTime.MinValue))
status_message = string.Format ("Synced {0}", this.repo.LastSync.ToPrettyDate ());
status_message = string.Format ("Synced {0}", this.repo.LastSync.ToPrettyDate ());
if (this.repo.Status == SyncStatus.SyncUp)
status_message = "Sending changes… " + this.repo.ProgressPercentage + "%";
@ -173,7 +173,7 @@ namespace SparkleShare {
public bool LinkCodeItemEnabled {
get {
return !string.IsNullOrEmpty (Program.Controller.CurrentUser.PublicKey);
return !string.IsNullOrEmpty (Program.Controller.UserAuthenticationInfo.PublicKey);
}
}
@ -282,7 +282,7 @@ namespace SparkleShare {
if (Projects.Length == 0)
return StateText = "Welcome to SparkleShare!";
else
return StateText = "Projects up to date " + GetPausedCount ();
return StateText = "Projects up to date " + GetPausedCount ();
}
@ -320,7 +320,7 @@ namespace SparkleShare {
public void CopyToClipboardClicked ()
{
Program.Controller.CopyToClipboard (Program.Controller.CurrentUser.PublicKey);
Program.Controller.CopyToClipboard (Program.Controller.UserAuthenticationInfo.PublicKey);
}
public void AboutClicked ()

View file

@ -108,9 +108,6 @@
<Compile Include="..\SparkleEventLogController.cs">
<Link>SparkleEventLogController.cs</Link>
</Compile>
<Compile Include="..\SparkleKeys.cs">
<Link>SparkleKeys.cs</Link>
</Compile>
<Compile Include="SparkleSetupWindow.cs" />
<Compile Include="..\Program.cs">
<Link>Program.cs</Link>
@ -291,4 +288,4 @@
<Name>SparkleLib</Name>
</ProjectReference>
</ItemGroup>
</Project>
</Project>