Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
Hylke Bons
ddf20e262d Save work 2018-09-16 18:07:24 +01:00
Hylke Bons
49afb63e3d Separate address page 2018-08-12 21:17:35 +01:00
Hylke Bons
4a9963d89a pages 2018-08-12 20:07:03 +01:00
Hylke Bons
f4b5fa196c switch 2018-08-11 20:18:50 +01:00
57 changed files with 2508 additions and 1668 deletions

1
.gitignore vendored
View file

@ -36,4 +36,3 @@ SparkleShare/Windows/build/
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/

View file

@ -83,10 +83,10 @@ namespace SparkleShare {
public event Action ShowEventLogWindowEvent = delegate { };
public event FolderFetchedEventHandler FolderFetched = delegate { };
public delegate void FolderFetchedEventHandler (string remote_url, string [] warnings);
public delegate void FolderFetchedEventHandler (string remote_url);
public event FolderFetchErrorHandler FolderFetchError = delegate { };
public delegate void FolderFetchErrorHandler (string remote_url, string [] errors);
public delegate void FolderFetchErrorHandler (string remote_url, string error, string error_details);
public event FolderFetchingHandler FolderFetching = delegate { };
public delegate void FolderFetchingHandler (double percentage, double speed, string information);
@ -270,7 +270,7 @@ namespace SparkleShare {
}
if (FirstRun) {
ShowSetupWindow (PageType.Setup);
ShowSetupWindow (PageType.User);
} else {
new Thread (() => {
@ -286,7 +286,7 @@ namespace SparkleShare {
public void ShowSetupWindow (PageType page_type)
{
ShowSetupWindowEvent (page_type);
ShowSetupWindowEvent (PageType.User);
}
@ -554,13 +554,13 @@ namespace SparkleShare {
}
public void StartFetcher (SparkleFetcherInfo info)
public void StartFetcher (FetcherInfo info)
{
string canonical_name = Path.GetFileName (info.RemotePath);
string backend = info.Backend;
string canonical_name = Path.GetFileName (info.Address.AbsolutePath);
string backend = info.Backend;
if (string.IsNullOrEmpty (backend))
backend = BaseFetcher.GetBackend (info.Address);
backend = BaseFetcher.GetBackend (info.Address.ToString ());
info.TargetDirectory = Path.Combine (Config.TmpPath, canonical_name);
@ -576,8 +576,8 @@ namespace SparkleShare {
Logger.LogInfo ("Controller",
"Failed to load '" + backend + "' backend for '" + canonical_name + "' " + e.Message);
FolderFetchError (Path.Combine (info.Address, info.RemotePath).Replace (@"\", "/"),
new string [] {"Failed to load \"" + backend + "\" backend for \"" + canonical_name + "\""});
FolderFetchError (info.Address.ToString ().Replace (@"\", "/"),
"Could not find backend", "\"" + backend + "\" is missing.");
return;
}
@ -590,10 +590,10 @@ namespace SparkleShare {
}
void FetcherFinishedDelegate (StorageType storage_type, string [] warnings)
void FetcherFinishedDelegate (StorageType storage_type)
{
if (storage_type == StorageType.Unknown) {
ShowSetupWindow (PageType.StorageSetup);
ShowSetupWindow (PageType.Storage);
return;
}
@ -606,10 +606,38 @@ namespace SparkleShare {
}
void FetcherFailedDelegate ()
void FetcherFailedDelegate (FetchResult result)
{
FolderFetchError (this.fetcher.RemoteUrl.ToString (), this.fetcher.Errors);
StopFetcher ();
string error = "Something went wrong";
string error_details = "Sorry, we're not quite sure what happened.";
if (result ==FetchResult.NoNetwork) {
error = "Could not connect to the internet";
error_details = "Please reconnect and try again.";
}
if (result == FetchResult.HostNotSupported) {
error = "Host not supported";
error_details = "The host wasn't set up right.";
}
if (result == FetchResult.HostChanged) {
error = "The host has changed its identity";
error_details = "Please contact its administrator.";
}
if (result == FetchResult.NotAuthenticated) {
error = "Could not authenticate";
error_details = "Please make sure the host knows your Public Key.";
}
if (result == FetchResult.UserInterrupted) {
error = "Cancelled.";
error_details = "";
}
FolderFetchError (this.fetcher.RemoteUrl.ToString (), error, error_details);
StopFetcher (); // TODO is this needed?
}
@ -688,7 +716,7 @@ namespace SparkleShare {
RepositoriesLoaded = true;
FolderListChanged ();
FolderFetched (this.fetcher.RemoteUrl.ToString (), this.fetcher.Warnings.ToArray ());
FolderFetched (this.fetcher.RemoteUrl.ToString ()); // TODO warning handling
this.fetcher.Dispose ();
this.fetcher = null;

View file

@ -73,8 +73,8 @@ namespace SparkleShare {
string html = HTML;
delay.Stop ();
if (!string.IsNullOrEmpty (html))
UpdateContentEvent (html);
if (!string.IsNullOrEmpty (html))
UpdateContentEvent (html);
UpdateSizeInfoEvent (Size, HistorySize);

View file

@ -0,0 +1,44 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace SparkleShare {
public abstract class Page : IDisposable {
protected PageController Controller;
protected PageType? RequestedType;
public string Header;
public string Description;
public object OptionArea;
public object [] Buttons;
public Page (PageType? page_type, PageController controller)
{
RequestedType = page_type;
Controller = controller;
}
public abstract object Render ();
public virtual void Dispose () { }
}
}

View file

@ -0,0 +1,198 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Collections.Generic;
using System.IO;
using System.Threading;
using Sparkles;
namespace SparkleShare {
public enum PageType {
None,
User,
Privacy,
Host,
Address,
Invite,
Progress,
Storage,
CryptoSetup,
CryptoPassword
}
public partial class PageController {
public bool WindowIsOpen { get; private set; }
public event Action ShowWindowEvent = delegate { };
public event Action HideWindowEvent = delegate { };
PageType current_page;
const int page_delay = 1000;
public event ChangePageEventHandler ChangePageEvent = delegate { };
public delegate void ChangePageEventHandler (PageType page);
public event PageCanContinueEventHandler PageCanContinueEvent = delegate { };
public delegate void PageCanContinueEventHandler (PageType page_type, bool can_continue);
public readonly List<Preset> Presets = new List<Preset> ();
public Preset SelectedPreset;
public Uri FetchAddress { get; private set; }
public double ProgressBarPercentage { get; private set; }
public SparkleInvite PendingInvite { get; private set; }
public PageController ()
{
LoadPresets ();
ChangePageEvent += delegate (PageType page_type) {
this.current_page = page_type;
};
SparkleShare.Controller.InviteReceived += delegate (SparkleInvite invite) {
PendingInvite = invite;
ChangePageEvent (PageType.Invite);
ShowWindowEvent ();
};
SparkleShare.Controller.ShowSetupWindowEvent += ShowSetupWindowEventHandler;
}
void ShowSetupWindowEventHandler (PageType page_type)
{
if (page_type == PageType.Storage ||
page_type == PageType.CryptoSetup ||
page_type == PageType.CryptoPassword) {
ChangePageEvent (page_type);
return;
}
if (PendingInvite != null) {
WindowIsOpen = true;
ShowWindowEvent ();
return;
}
if (this.current_page == PageType.Progress ||
this.current_page == PageType.CryptoSetup ||
this.current_page == PageType.CryptoPassword) {
ShowWindowEvent ();
return;
}
if (page_type == PageType.Host) {
if (WindowIsOpen) {
if (this.current_page == PageType.None)
ChangePageEvent (PageType.Host);
} else if (!SparkleShare.Controller.FirstRun) {
WindowIsOpen = true;
ChangePageEvent (PageType.Host);
}
ShowWindowEvent ();
return;
}
WindowIsOpen = true;
ChangePageEvent (page_type);
ShowWindowEvent ();
}
public void QuitClicked ()
{
SparkleShare.Controller.Quit ();
}
public void CancelClicked (PageType? canceled_page)
{
if (canceled_page == PageType.Progress) {
SparkleShare.Controller.StopFetcher ();
if (PendingInvite != null)
ChangePageEvent (PageType.Invite);
else
ChangePageEvent (PageType.Host);
return;
}
if (canceled_page == PageType.CryptoSetup ||
canceled_page == PageType.CryptoPassword) {
CancelClicked (PageType.Progress);
return;
}
Reset ();
WindowIsOpen = false;
HideWindowEvent ();
}
PageType [] page_order = { PageType.User, PageType.Privacy,
PageType.Host, PageType.Address, PageType.Progress };
public void BackClicked (PageType? page_type)
{
PageType page = (PageType) page_type;
int current_index = Array.IndexOf (page_order, page);
int back_index = current_index - 1;
if (back_index > -1)
ChangePageEvent (page_order [back_index]);
}
public void Reset ()
{
PendingInvite = null;
SelectedPreset = Presets [0];
FetchAddress = null;
}
public void ProgressPageCompleted ()
{
this.current_page = PageType.None;
Reset ();
WindowIsOpen = false;
HideWindowEvent ();
}
}
}

View file

@ -0,0 +1,76 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public event AddressPagePublicKeyEventHandler AddressPagePublicKeyEvent = delegate { };
public delegate void AddressPagePublicKeyEventHandler (bool has_key, string auth_status, string key_entry_hint);
public void CheckAddressPage (string address, string remote_path)
{
address = address.Trim ();
remote_path = remote_path.Trim ();
bool fields_valid = (!string.IsNullOrEmpty (address) &&
!string.IsNullOrEmpty (remote_path) && !remote_path.Contains ("\""));
PageCanContinueEvent (PageType.Address, fields_valid);
}
public void AddressPageCompleted (string address, string remote_path)
{
ProgressBarPercentage = 1.0;
ChangePageEvent (PageType.Progress);
address = Uri.EscapeUriString (address.Trim ());
remote_path = remote_path.Trim ();
if (SelectedPreset.PathUsesLowerCase)
remote_path = remote_path.ToLower ();
FetchAddress = new Uri (address + remote_path);
SparkleShare.Controller.FolderFetched += ProgressPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError += ProgressPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching += ProgressPageFetchingDelegate;
var info = new FetcherInfo (FetchAddress) {
Backend = SelectedPreset.Backend,
Fingerprint = SelectedPreset.Fingerprint,
AnnouncementsUrl = SelectedPreset.AnnouncementsUrl
};
new Thread (() => { SparkleShare.Controller.StartFetcher (info); }).Start ();
}
public void CopyToClipboardClicked ()
{
SparkleShare.Controller.CopyToClipboard (SparkleShare.Controller.UserAuthenticationInfo.PublicKey);
}
}
}

View file

@ -0,0 +1,55 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public void CheckCryptoSetupPage (string password)
{
new Thread (() => {
bool is_valid_password = (password.Length > 0 && !password.StartsWith (" ") && !password.EndsWith (" "));
PageCanContinueEvent (PageType.CryptoSetup, is_valid_password);
}).Start ();
}
public void CheckCryptoPasswordPage (string password)
{
bool is_password_correct = SparkleShare.Controller.CheckPassword (password);
PageCanContinueEvent (PageType.CryptoPassword, is_password_correct);
}
public void CryptoPageCompleted (string password)
{
ProgressBarPercentage = 100.0;
ChangePageEvent (PageType.Progress);
new Thread (() => {
Thread.Sleep (page_delay);
SparkleShare.Controller.FinishFetcher (StorageType.Encrypted, password);
}).Start ();
}
}
}

View file

@ -0,0 +1,114 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.IO;
using System.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
void LoadPresets ()
{
int local_presets_count = 0;
string local_presets_path = Preset.LocalPresetsPath;
if (Directory.Exists (local_presets_path))
// Local presets go first...
foreach (string xml_file_path in Directory.GetFiles (local_presets_path, "*.xml")) {
Presets.Add (new Preset (xml_file_path));
local_presets_count++;
}
// ...system presets after that...
if (Directory.Exists (SparkleShare.Controller.PresetsPath)) {
foreach (string xml_file_path in Directory.GetFiles (SparkleShare.Controller.PresetsPath, "*.xml")) {
// ...and "Own server" at the very top
if (xml_file_path.EndsWith ("own-server.xml"))
Presets.Insert (0, new Preset (xml_file_path));
else
Presets.Add (new Preset (xml_file_path));
}
}
SelectedPreset = Presets [0];
}
public int SelectedPresetIndex {
get {
return Presets.IndexOf (SelectedPreset);
}
}
public void SelectedPresetChanged (int preset_index)
{
SelectedPreset = Presets [preset_index];
}
public void HostPageCompleted ()
{
ChangePageEvent (PageType.Address);
string host = SelectedPreset.Address;
if (string.IsNullOrEmpty (host)) {
if (string.IsNullOrEmpty (SelectedPreset.Host)) {
new Thread (() => {
Thread.Sleep (page_delay);
AddressPagePublicKeyEvent (false, "", SelectedPreset.KeyEntryHint);
}).Start ();
return;
}
host = "ssh://" + SelectedPreset.Host;
}
new Thread (() => {
bool authenticated = true;
string auth_status = "✓ Authenticated";
try {
authenticated = SSHFetcher.CanAuthenticateTo (
new Uri (host), SparkleShare.Controller.UserAuthenticationInfo);
} catch (NetworkException) {
authenticated = false;
}
if (authenticated) {
if (!string.IsNullOrEmpty (SelectedPreset.Address))
auth_status += string.Format (" to {0}", new Uri (host).Host);
} else {
string public_key = "<tt>" + SparkleShare.Controller.UserAuthenticationInfo.PublicKey.Substring (0, 40) + "…</tt>";
auth_status = public_key;
}
AddressPagePublicKeyEvent (authenticated, auth_status, SelectedPreset.KeyEntryHint);
}).Start ();
}
}
}

View file

@ -0,0 +1,79 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.IO;
using System.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public void InvitePageCompleted ()
{
// TODO PreviousAddress = PendingInvite.Address;
ChangePageEvent (PageType.Progress);
new Thread (() => {
if (!PendingInvite.Accept (SparkleShare.Controller.UserAuthenticationInfo.PublicKey)) {
FetchAddress = new Uri (PendingInvite.Address + PendingInvite.RemotePath.TrimStart ('/'));
// TODO ChangePageEvent (PageType.Error, new string [] { "error: Failed to upload the public key" });
return;
}
SparkleShare.Controller.FolderFetched += InvitePageFetchedDelegate;
SparkleShare.Controller.FolderFetchError += InvitePageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching += ProgressPageFetchingDelegate;
var info = new FetcherInfo (new Uri (Path.Combine (PendingInvite.Address, PendingInvite.RemotePath))) {
Fingerprint = PendingInvite.Fingerprint,
AnnouncementsUrl = PendingInvite.AnnouncementsUrl
};
SparkleShare.Controller.StartFetcher (info);
}).Start ();
}
void InvitePageFetchedDelegate (string remote_url)
{
PendingInvite = null;
// TODO success state + warning
SparkleShare.Controller.FolderFetched -= ProgressPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= ProgressPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= ProgressPageFetchingDelegate;
}
void InvitePageFetchErrorDelegate (string remote_url, string error, string error_details)
{
FetchAddress = new Uri (remote_url);
//TODO ChangePageEvent (PageType.Error, errors);
SparkleShare.Controller.FolderFetched -= ProgressPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= ProgressPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= ProgressPageFetchingDelegate;
}
}
}

View file

@ -0,0 +1,33 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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;
namespace SparkleShare {
public partial class PageController {
public void PrivacyPageCompleted (bool notification_service, bool crash_reports, bool gravatars)
{
SparkleShare.Controller.Config.SetConfigOption ("notification_service", notification_service.ToString ());
SparkleShare.Controller.Config.SetConfigOption ("crash_reports", crash_reports.ToString ());
SparkleShare.Controller.Config.SetConfigOption ("gravatars", gravatars.ToString ());
ChangePageEvent (PageType.Host);
}
}
}

View file

@ -0,0 +1,114 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.IO;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public event ProgressPageBarEventHandler ProgressPageBarEvent = delegate { };
public delegate void ProgressPageBarEventHandler (bool? success,
string status, string status_details,
double progress, string progress_details);
void ProgressPageFetchedDelegate (string remote_url)
{
// Create a local preset for succesfully added projects, so
// so the user can easily use the same host again
if (SelectedPresetIndex == 0) {
Preset new_preset;
Uri uri = new Uri (remote_url);
try {
string address = remote_url.Replace (uri.AbsolutePath, "");
new_preset = Preset.Create (uri.Host, address, address, "", "", "/path/to/project");
if (new_preset != null) {
Presets.Insert (1, new_preset);
Logger.LogInfo ("Controller", "Added preset for " + uri.Host);
}
} catch (Exception e) {
Logger.LogInfo ("Controller", "Failed adding preset for " + uri.Host, e);
}
}
SparkleShare.Controller.FolderFetched -= ProgressPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= ProgressPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= ProgressPageFetchingDelegate;
ProgressPageBarEvent (
success: true,
status: "Fetched all files from " + FetchAddress, status_details: null,
progress: 100.0, progress_details: null);
}
void ProgressPageFetchErrorDelegate (string remote_url, string error, string error_details)
{
FetchAddress = new Uri (remote_url);
ProgressPageBarEvent (
success: false,
status: error, status_details: error_details,
progress: ProgressBarPercentage, progress_details: "Could not download files from " + remote_url);
SparkleShare.Controller.FolderFetched -= ProgressPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= ProgressPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= ProgressPageFetchingDelegate;
}
void ProgressPageFetchingDelegate (double percentage, double speed ,string information)
{
ProgressBarPercentage = percentage;
if (speed > 0)
information = speed.ToSize () + " " + information;
ProgressPageBarEvent (
success: null,
status: "Getting files", status_details: null,
progress: ProgressBarPercentage, progress_details: information);
}
public void ErrorPageCompleted ()
{
if (PendingInvite != null)
ChangePageEvent (PageType.Invite);
else
ChangePageEvent (PageType.Address);
}
public void ShowFilesClicked ()
{
string folder_name = Path.GetFileName (FetchAddress.AbsolutePath);
folder_name = folder_name.ReplaceUnderscoreWithSpace ();
SparkleShare.Controller.OpenSparkleShareFolder (folder_name);
ProgressPageCompleted ();
}
}
}

View file

@ -0,0 +1,44 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public void StoragePageCompleted (StorageType storage_type)
{
if (storage_type == StorageType.Encrypted) {
ChangePageEvent (PageType.CryptoSetup);
return;
}
ProgressBarPercentage = 100.0;
ChangePageEvent (PageType.Progress);
new Thread (() => {
Thread.Sleep (page_delay);
SparkleShare.Controller.FinishFetcher (storage_type);
}).Start ();
}
}
}

View file

@ -0,0 +1,45 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Threading;
using Sparkles;
namespace SparkleShare {
public partial class PageController {
public void CheckUserPage (string name, string email)
{
name = name.Trim ();
email = email.Trim ();
bool fields_valid = (!string.IsNullOrEmpty (name) && email.Contains ("@"));
PageCanContinueEvent (PageType.User, fields_valid);
}
public void UserPageCompleted (string full_name, string email)
{
SparkleShare.Controller.CurrentUser = new User (full_name, email);
new Thread (() => SparkleShare.Controller.CreateStartupItem ()).Start ();
ChangePageEvent (PageType.Privacy);
}
}
}

View file

@ -1,22 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<preset>
<info>
<name>Bitbucket</name>
<description>Free code hosting for Git and Mercurial</description>
<icon>bitbucket.png</icon>
<backend>Git</backend>
<fingerprint>cf:35:d0:39:74:91:04:48:94:b6:e1:3c:02:29:09:60:ac:1b:1d:ac:6f:49:cd:28:8d:ec:fd:61:76:86:a7:50</fingerprint>
</info>
<address>
<value>ssh://git@bitbucket.org/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
<uses_lower_case>True</uses_lower_case>
</path>
</preset>
<preset>
<info>
<name>Bitbucket</name>
<description>Free code hosting for Git and Mercurial</description>
<icon>bitbucket.png</icon>
<backend>Git</backend>
<host>bitbucket.org</host>
<fingerprint>cf:35:d0:39:74:91:04:48:94:b6:e1:3c:02:29:09:60:ac:1b:1d:ac:6f:49:cd:28:8d:ec:fd:61:76:86:a7:50</fingerprint>
</info>
<address>
<value>ssh://git@bitbucket.org/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
<uses_lower_case>True</uses_lower_case>
</path>
<key_entry_hint>Go to <b>Settings → SSH Keys</b> on <a href="https://bitbucket.org/">bitbucket.org</a> and add this SSH key:</key_entry_hint>
</preset>
</sparkleshare>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -1,21 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<preset>
<info>
<name>GitHub</name>
<description>The biggest collection of Open Source projects</description>
<icon>github.png</icon>
<backend>Git</backend>
<storage_type>LargeFiles</storage_type>
<fingerprint>9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f</fingerprint>
</info>
<address>
<value>ssh://git@github.com/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
</path>
</preset>
<preset>
<info>
<name>GitHub</name>
<description>The biggest collection of Open Source projects</description>
<icon>github.png</icon>
<logo>github-logo.png</logo>
<backend>Git</backend>
<host>github.com</host>
<fingerprint>9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f</fingerprint>
<storage_type>LargeFiles</storage_type>
</info>
<address>
<value>ssh://git@github.com/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
</path>
<key>
<hint>Go to <a href="https://github.com/settings/keys">github.com/settings/keys</a> and add this SSH key:</hint>
</key>
</preset>
</sparkleshare>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -1,21 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<preset>
<info>
<name>GitLab</name>
<description>Open Source alternative to GitHub</description>
<icon>gitlab.png</icon>
<backend>Git</backend>
<fingerprint>44:e4:05:bc:f4:e1:1a:b5:b8:46:e5:8b:a0:bf:6d:ab:d2:3d:cc:9e:36:7c:ae:17:cb:0c:91:b5:b3:b3:fc:44</fingerprint>
</info>
<address>
<value>ssh://git@gitlab.com/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
</path>
</preset>
<preset>
<info>
<name>GitLab</name>
<description>Open Source alternative to GitHub</description>
<icon>gitlab.png</icon>
<logo>gitlab-logo.png</logo>
<backend>Git</backend>
<host>gitlab.com</host>
<fingerprint>44:e4:05:bc:f4:e1:1a:b5:b8:46:e5:8b:a0:bf:6d:ab:d2:3d:cc:9e:36:7c:ae:17:cb:0c:91:b5:b3:b3:fc:44</fingerprint>
</info>
<address>
<value>ssh://git@gitlab.com/</value>
<example/>
</address>
<path>
<value/>
<example>/username/project</example>
</path>
<key>
<hint>Go to <a href="https://gitlab.com/profile/keys">gitlab.com/profile/keys</a> and add this SSH key:</hint>
</key>
</preset>
</sparkleshare>

View file

@ -1,9 +1,18 @@
preset_files = ['github.xml', 'github.png',
'gitlab.xml', 'gitlab.png',
'bitbucket.xml', 'bitbucket.png',
'planio.xml', 'planio.png',
'own-server.xml', 'own-server.png'
]
# Github
preset_files = ['github.xml', 'github.png', 'github-logo.png']
# GitLab
preset_files += ['gitlab.xml', 'gitlab.png', 'gitlab-logo.png']
# Bitbucket
preset_files += ['bitbucket.xml', 'bitbucket.png']
# Planio
preset_files += ['planio.xml', 'planio.png', 'planio-logo.png']
# Other
preset_files += ['own-server.xml', 'own-server.png']
install_data(
sources: preset_files,

View file

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<preset>
<info>
<name>Own server</name>
<description>Everything under my control</description>
<icon>own-server.png</icon>
<backend>Git</backend>
</info>
<address>
<value/>
<example>ssh://[user@]hostname[:port]</example>
</address>
<path>
<value/>
<example>/path/to/project</example>
</path>
</preset>
<preset>
<info>
<name>Own server</name>
<description>Everything under my control</description>
<key_entry_hint>Ask the person administrating the server to add this SSH key:</key_entry_hint>
<icon>own-server.png</icon>
<backend>Git</backend>
</info>
<address>
<value/>
<example>ssh://[user@]hostname[:port]</example>
</address>
<path>
<value/>
<example>/path/to/project</example>
</path>
</preset>
</sparkleshare>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -1,22 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<preset>
<info>
<name>Planio</name>
<description>Online project management</description>
<icon>planio.png</icon>
<backend>Git</backend>
<fingerprint>38:29:13:0f:7c:74:67:22:c5:a6:a6:95:b4:b8:e0:ef:3d:e0:9e:87:02:58:ff:37:2c:8b:34:5a:ed:73:5d:81</fingerprint>
<announcements_url>tcp://sparkleshare-notifications.plan.io:443</announcements_url>
</info>
<address>
<value/>
<example>ssh://git@[account].plan.io</example>
</address>
<path>
<value/>
<example>/[account]-[project].git</example>
</path>
</preset>
<preset>
<info>
<name>Planio</name>
<description>Online project management</description>
<icon>planio.png</icon>
<logo>planio-logo.png</logo>
<backend>Git</backend>
<host>plan.io</host>
<fingerprint>38:29:13:0f:7c:74:67:22:c5:a6:a6:95:b4:b8:e0:ef:3d:e0:9e:87:02:58:ff:37:2c:8b:34:5a:ed:73:5d:81</fingerprint>
<announcements_url>tcp://sparkleshare-notifications.plan.io:443</announcements_url>
</info>
<address>
<value/>
<example>ssh://git@[account].plan.io</example>
</address>
<path>
<value/>
<example>/[account]-[project].git</example>
</path>
<key>
<hint>Go to <b>My account → Public keys</b> on <a href="https://plan.io/">plan.io</a> and add this key:</hint>
</key>
</preset>
</sparkleshare>

View file

@ -1,566 +0,0 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using Sparkles;
namespace SparkleShare {
public enum PageType {
None,
Setup,
Add,
Invite,
Syncing,
Error,
Finished,
StorageSetup,
CryptoSetup,
CryptoPassword
}
public enum FieldState {
Enabled,
Disabled
}
public class SetupController {
public event Action ShowWindowEvent = delegate { };
public event Action HideWindowEvent = delegate { };
public event ChangePageEventHandler ChangePageEvent = delegate { };
public delegate void ChangePageEventHandler (PageType page, string [] warnings);
public event UpdateProgressBarEventHandler UpdateProgressBarEvent = delegate { };
public delegate void UpdateProgressBarEventHandler (double percentage, string information);
public event UpdateSetupContinueButtonEventHandler UpdateSetupContinueButtonEvent = delegate { };
public delegate void UpdateSetupContinueButtonEventHandler (bool button_enabled);
public event UpdateCryptoSetupContinueButtonEventHandler UpdateCryptoSetupContinueButtonEvent = delegate { };
public delegate void UpdateCryptoSetupContinueButtonEventHandler (bool button_enabled);
public event UpdateCryptoPasswordContinueButtonEventHandler UpdateCryptoPasswordContinueButtonEvent = delegate { };
public delegate void UpdateCryptoPasswordContinueButtonEventHandler (bool button_enabled);
public event UpdateAddProjectButtonEventHandler UpdateAddProjectButtonEvent = delegate { };
public delegate void UpdateAddProjectButtonEventHandler (bool button_enabled);
public event ChangeAddressFieldEventHandler ChangeAddressFieldEvent = delegate { };
public delegate void ChangeAddressFieldEventHandler (string text, string example_text, FieldState state);
public event ChangePathFieldEventHandler ChangePathFieldEvent = delegate { };
public delegate void ChangePathFieldEventHandler (string text, string example_text, FieldState state);
public readonly List<Preset> Presets = new List<Preset> ();
public Preset SelectedPreset;
public bool WindowIsOpen { get; private set; }
public SparkleInvite PendingInvite { get; private set; }
public string PreviousUrl { get; private set; }
public string PreviousAddress { get; private set; }
public string PreviousPath { get; private set; }
public string SyncingFolder { get; private set; }
public double ProgressBarPercentage { get; private set; }
public int SelectedPresetIndex {
get {
return Presets.IndexOf (SelectedPreset);
}
}
public bool FetchPriorHistory {
get {
return this.fetch_prior_history;
}
}
private PageType current_page;
private string saved_address = "";
private string saved_remote_path = "";
private bool fetch_prior_history = false;
public SetupController ()
{
ChangePageEvent += delegate (PageType page_type, string [] warnings) {
this.current_page = page_type;
};
PreviousAddress = "";
PreviousPath = "";
PreviousUrl = "";
SyncingFolder = "";
string local_presets_path = Preset.LocalPresetsPath;
int local_presets_count = 0;
// Import all of the presets
if (Directory.Exists (local_presets_path))
// Local presets go first...
foreach (string xml_file_path in Directory.GetFiles (local_presets_path, "*.xml")) {
Presets.Add (new Preset (xml_file_path));
local_presets_count++;
}
// ...system presets after that...
if (Directory.Exists (SparkleShare.Controller.PresetsPath)) {
foreach (string xml_file_path in Directory.GetFiles (SparkleShare.Controller.PresetsPath, "*.xml")) {
// ...and "Own server" at the very top
if (xml_file_path.EndsWith ("own-server.xml"))
Presets.Insert (0, new Preset (xml_file_path));
else
Presets.Add (new Preset (xml_file_path));
}
}
SelectedPreset = Presets [0];
SparkleShare.Controller.InviteReceived += delegate (SparkleInvite invite) {
PendingInvite = invite;
ChangePageEvent (PageType.Invite, null);
ShowWindowEvent ();
};
SparkleShare.Controller.ShowSetupWindowEvent += delegate (PageType page_type) {
if (page_type == PageType.StorageSetup ||
page_type == PageType.CryptoSetup ||
page_type == PageType.CryptoPassword) {
ChangePageEvent (page_type, null);
return;
}
if (PendingInvite != null) {
WindowIsOpen = true;
ShowWindowEvent ();
return;
}
if (this.current_page == PageType.Syncing ||
this.current_page == PageType.Finished ||
this.current_page == PageType.CryptoSetup ||
this.current_page == PageType.CryptoPassword) {
ShowWindowEvent ();
return;
}
if (page_type == PageType.Add) {
if (WindowIsOpen) {
if (this.current_page == PageType.Error ||
this.current_page == PageType.Finished ||
this.current_page == PageType.None) {
ChangePageEvent (PageType.Add, null);
}
} else if (!SparkleShare.Controller.FirstRun) {
WindowIsOpen = true;
ChangePageEvent (PageType.Add, null);
}
ShowWindowEvent ();
return;
}
WindowIsOpen = true;
ChangePageEvent (page_type, null);
ShowWindowEvent ();
};
}
public void PageCancelled ()
{
PendingInvite = null;
SelectedPreset = Presets [0];
PreviousAddress = "";
PreviousPath = "";
PreviousUrl = "";
this.saved_address = "";
this.saved_remote_path = "";
this.fetch_prior_history = false;
WindowIsOpen = false;
HideWindowEvent ();
}
public void CheckSetupPage (string full_name, string email)
{
full_name = full_name.Trim ();
email = email.Trim ();
bool fields_valid = (!string.IsNullOrEmpty (full_name) && IsValidEmail (email));
UpdateSetupContinueButtonEvent (fields_valid);
}
public void SetupPageCancelled ()
{
SparkleShare.Controller.Quit ();
}
public void SetupPageCompleted (string full_name, string email)
{
SparkleShare.Controller.CurrentUser = new User (full_name, email);
new Thread (() => SparkleShare.Controller.CreateStartupItem ()).Start ();
ChangePageEvent (PageType.Add, null);
}
public void HistoryItemChanged (bool fetch_prior_history)
{
this.fetch_prior_history = fetch_prior_history;
}
public void SelectedPresetChanged (int preset_index)
{
SelectedPreset = Presets [preset_index];
if (SelectedPreset.Address != null) {
ChangeAddressFieldEvent (SelectedPreset.Address, "", FieldState.Disabled);
} else if (SelectedPreset.AddressExample != null) {
ChangeAddressFieldEvent (this.saved_address, SelectedPreset.AddressExample, FieldState.Enabled);
} else {
ChangeAddressFieldEvent (this.saved_address, "", FieldState.Enabled);
}
if (SelectedPreset.Path != null) {
ChangePathFieldEvent (SelectedPreset.Path, "", FieldState.Disabled);
} else if (SelectedPreset.PathExample != null) {
ChangePathFieldEvent (this.saved_remote_path, SelectedPreset.PathExample, FieldState.Enabled);
} else {
ChangePathFieldEvent (this.saved_remote_path, "", FieldState.Enabled);
}
}
public void CheckAddPage (string address, string remote_path, int selected_preset)
{
address = address.Trim ();
remote_path = remote_path.Trim ();
if (selected_preset == 0)
this.saved_address = address;
this.saved_remote_path = remote_path;
bool fields_valid = (!string.IsNullOrEmpty (address) &&
!string.IsNullOrEmpty (remote_path) && !remote_path.Contains ("\""));
UpdateAddProjectButtonEvent (fields_valid);
}
public void AddPageCompleted (string address, string remote_path)
{
SyncingFolder = Path.GetFileName (remote_path);
if (remote_path.EndsWith (".git"))
SyncingFolder = remote_path.Substring (0, remote_path.Length - 4);
SyncingFolder = SyncingFolder.ReplaceUnderscoreWithSpace ();
ProgressBarPercentage = 1.0;
ChangePageEvent (PageType.Syncing, null);
address = Uri.EscapeUriString (address.Trim ());
remote_path = remote_path.Trim ();
remote_path = remote_path.TrimEnd ("/".ToCharArray ());
if (SelectedPreset.PathUsesLowerCase)
remote_path = remote_path.ToLower ();
PreviousAddress = address;
PreviousPath = remote_path;
SparkleShare.Controller.FolderFetched += AddPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError += AddPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching += SyncingPageFetchingDelegate;
SparkleFetcherInfo info = new SparkleFetcherInfo {
Address = address,
Fingerprint = SelectedPreset.Fingerprint,
RemotePath = remote_path,
FetchPriorHistory = this.fetch_prior_history,
AnnouncementsUrl = SelectedPreset.AnnouncementsUrl,
Backend = SelectedPreset.Backend
};
new Thread (() => { SparkleShare.Controller.StartFetcher (info); }).Start ();
}
// The following private methods are
// delegates used by the previous method
private void AddPageFetchedDelegate (string remote_url, string [] warnings)
{
SyncingFolder = "";
// Create a local preset for succesfully added projects, so
// so the user can easily use the same host again
if (SelectedPresetIndex == 0) {
Preset new_preset;
Uri uri = new Uri (remote_url);
try {
string address = remote_url.Replace (uri.AbsolutePath, "");
new_preset = Preset.Create (uri.Host, address, address, "", "", "/path/to/project");
if (new_preset != null) {
Presets.Insert (1, new_preset);
Logger.LogInfo ("Controller", "Added preset for " + uri.Host);
}
} catch (Exception e) {
Logger.LogInfo ("Controller", "Failed adding preset for " + uri.Host, e);
}
}
ChangePageEvent (PageType.Finished, warnings);
SparkleShare.Controller.FolderFetched -= AddPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= AddPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= SyncingPageFetchingDelegate;
}
private void AddPageFetchErrorDelegate (string remote_url, string [] errors)
{
SyncingFolder = "";
PreviousUrl = remote_url;
ChangePageEvent (PageType.Error, errors);
SparkleShare.Controller.FolderFetched -= AddPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= AddPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= SyncingPageFetchingDelegate;
}
private void SyncingPageFetchingDelegate (double percentage, double speed ,string information)
{
ProgressBarPercentage = percentage;
if (speed > 0)
information = speed.ToSize () + " " + information;
UpdateProgressBarEvent (ProgressBarPercentage, information);
}
public void InvitePageCompleted ()
{
SyncingFolder = Path.GetFileName (PendingInvite.RemotePath);
if (PendingInvite.RemotePath.EndsWith (".git"))
SyncingFolder = PendingInvite.RemotePath.Substring (0, PendingInvite.RemotePath.Length - 4);
SyncingFolder = SyncingFolder.ReplaceUnderscoreWithSpace ();
PreviousAddress = PendingInvite.Address;
PreviousPath = PendingInvite.RemotePath;
ChangePageEvent (PageType.Syncing, null);
new Thread (() => {
if (!PendingInvite.Accept (SparkleShare.Controller.UserAuthenticationInfo.PublicKey)) {
PreviousUrl = PendingInvite.Address + PendingInvite.RemotePath.TrimStart ("/".ToCharArray ());
ChangePageEvent (PageType.Error, new string [] { "error: Failed to upload the public key" });
return;
}
SparkleShare.Controller.FolderFetched += InvitePageFetchedDelegate;
SparkleShare.Controller.FolderFetchError += InvitePageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching += SyncingPageFetchingDelegate;
SparkleFetcherInfo info = new SparkleFetcherInfo {
Address = PendingInvite.Address,
Fingerprint = PendingInvite.Fingerprint,
RemotePath = PendingInvite.RemotePath,
FetchPriorHistory = false, // TODO: checkbox on invite page
AnnouncementsUrl = PendingInvite.AnnouncementsUrl
};
SparkleShare.Controller.StartFetcher (info);
}).Start ();
}
// The following private methods are
// delegates used by the previous method
private void InvitePageFetchedDelegate (string remote_url, string [] warnings)
{
SyncingFolder = "";
PendingInvite = null;
ChangePageEvent (PageType.Finished, warnings);
SparkleShare.Controller.FolderFetched -= AddPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= AddPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= SyncingPageFetchingDelegate;
}
private void InvitePageFetchErrorDelegate (string remote_url, string [] errors)
{
SyncingFolder = "";
PreviousUrl = remote_url;
ChangePageEvent (PageType.Error, errors);
SparkleShare.Controller.FolderFetched -= AddPageFetchedDelegate;
SparkleShare.Controller.FolderFetchError -= AddPageFetchErrorDelegate;
SparkleShare.Controller.FolderFetching -= SyncingPageFetchingDelegate;
}
public void SyncingCancelled ()
{
SparkleShare.Controller.StopFetcher ();
if (PendingInvite != null)
ChangePageEvent (PageType.Invite, null);
else
ChangePageEvent (PageType.Add, null);
}
public void ErrorPageCompleted ()
{
if (PendingInvite != null)
ChangePageEvent (PageType.Invite, null);
else
ChangePageEvent (PageType.Add, null);
}
public void StoragePageCompleted (StorageType storage_type)
{
if (storage_type == StorageType.Encrypted) {
ChangePageEvent (PageType.CryptoSetup, null);
return;
}
ProgressBarPercentage = 100.0;
ChangePageEvent (PageType.Syncing, null);
new Thread (() => {
Thread.Sleep (1000);
SparkleShare.Controller.FinishFetcher (storage_type);
}).Start ();
}
public void CheckCryptoSetupPage (string password)
{
new Thread (() => {
bool is_valid_password = (password.Length > 0 && !password.StartsWith (" ") && !password.EndsWith (" "));
UpdateCryptoSetupContinueButtonEvent (is_valid_password);
}).Start ();
}
public void CheckCryptoPasswordPage (string password)
{
bool is_password_correct = SparkleShare.Controller.CheckPassword (password);
UpdateCryptoPasswordContinueButtonEvent (is_password_correct);
}
public void CryptoPageCancelled ()
{
SyncingCancelled ();
}
public void CryptoSetupPageCompleted (string password)
{
CryptoPasswordPageCompleted (password);
}
public void CryptoPasswordPageCompleted (string password)
{
ProgressBarPercentage = 100.0;
ChangePageEvent (PageType.Syncing, null);
new Thread (() => {
Thread.Sleep (1000);
SparkleShare.Controller.FinishFetcher (StorageType.Encrypted, password);
}).Start ();
}
public void CopyToClipboardClicked ()
{
SparkleShare.Controller.CopyToClipboard (SparkleShare.Controller.UserAuthenticationInfo.PublicKey);
}
public void ShowFilesClicked ()
{
string folder_name = Path.GetFileName (PreviousPath);
folder_name = folder_name.ReplaceUnderscoreWithSpace ();
SparkleShare.Controller.OpenSparkleShareFolder (folder_name);
FinishPageCompleted ();
}
public void FinishPageCompleted ()
{
SelectedPreset = Presets [0];
PreviousUrl = "";
PreviousAddress = "";
PreviousPath = "";
this.fetch_prior_history = false;
this.saved_address = "";
this.saved_remote_path = "";
this.current_page = PageType.None;
WindowIsOpen = false;
HideWindowEvent ();
}
private bool IsValidEmail (string email)
{
return email.Contains ("@");
}
}
}

View file

@ -46,14 +46,14 @@ namespace SparkleShare {
public string StatusMessage {
get {
string status_message = "Waiting to sync";
if (!repo.LastSync.Equals (DateTime.MinValue))
string status_message = "Ready to sync";
if (repo.LastSync > DateTime.MinValue)
status_message = string.Format ("✓ Synced Last change {0}", repo.LastSync.ToPrettyDate ());
if (repo.Status == SyncStatus.SyncUp)
status_message = "Sending… " + (int) repo.ProgressPercentage + "%";
if (repo.Status == SyncStatus.SyncDown)
status_message = "Receiving… " + (int) repo.ProgressPercentage + "%";
@ -136,7 +136,7 @@ namespace SparkleShare {
public delegate void UpdateQuitItemEventHandler (bool quit_item_enabled);
public IconState CurrentState = IconState.Idle;
public string StateText = "Welcome to SparkleShare!";
public string StateText = "Welcome to SparkleShare";
public ProjectInfo [] Projects = new ProjectInfo [0];
@ -147,11 +147,6 @@ namespace SparkleShare {
}
}
public bool LinkCodeItemEnabled {
get {
return !string.IsNullOrEmpty (SparkleShare.Controller.UserAuthenticationInfo.PublicKey);
}
}
public bool QuitItemEnabled {
get {
@ -307,13 +302,9 @@ namespace SparkleShare {
public void AddHostedProjectClicked ()
{
new Thread (() => SparkleShare.Controller.ShowSetupWindow (PageType.Add)).Start ();
new Thread (() => SparkleShare.Controller.ShowSetupWindow (PageType.Host)).Start ();
}
public void CopyToClipboardClicked ()
{
SparkleShare.Controller.CopyToClipboard (SparkleShare.Controller.UserAuthenticationInfo.PublicKey);
}
public void AboutClicked ()
{

View file

@ -1,704 +0,0 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Gtk;
using Mono.Unix;
using Sparkles;
namespace SparkleShare {
public class Setup : SetupWindow {
public SetupController Controller = new SetupController ();
public Setup ()
{
Controller.HideWindowEvent += delegate {
Application.Invoke (delegate { Hide (); });
};
Controller.ShowWindowEvent += delegate {
Application.Invoke (delegate {
ShowAll ();
Present ();
});
};
Controller.ChangePageEvent += delegate (PageType type, string [] warnings) {
Application.Invoke (delegate {
Reset ();
ShowPage (type, warnings);
ShowAll ();
});
};
}
public void ShowPage (PageType type, string [] warnings)
{
if (type == PageType.Setup) {
Header = "Welcome to SparkleShare!";
Description = "First off, whats your name and email?\n(visible only to team members)";
Table table = new Table (2, 3, true) {
RowSpacing = 6,
ColumnSpacing = 6
};
Label name_label = new Label ("<b>" + "Your Name:" + "</b>") {
UseMarkup = true,
Xalign = 1
};
Entry name_entry = new Entry () {
Xalign = 0,
ActivatesDefault = true
};
try {
UnixUserInfo user_info = UnixUserInfo.GetRealUser ();
if (user_info != null && user_info.RealName != null)
// Some systems append a series of "," for some reason, TODO: Report upstream
name_entry.Text = user_info.RealName.TrimEnd (",".ToCharArray ());
} catch (ArgumentException) {
// No username, not a big deal
}
Entry email_entry = new Entry () {
Xalign = 0,
ActivatesDefault = true
};
Label email_label = new Label ("<b>" + "Email:" + "</b>") {
UseMarkup = true,
Xalign = 1
};
table.Attach (name_label, 0, 1, 0, 1);
table.Attach (name_entry, 1, 2, 0, 1);
table.Attach (email_label, 0, 1, 1, 2);
table.Attach (email_entry, 1, 2, 1, 2);
VBox wrapper = new VBox (false, 9);
wrapper.PackStart (table, true, false, 0);
Button cancel_button = new Button ("Cancel");
Button continue_button = new Button ("Continue") { Sensitive = false };
Controller.UpdateSetupContinueButtonEvent += delegate (bool button_enabled) {
Application.Invoke (delegate { continue_button.Sensitive = button_enabled; });
};
name_entry.Changed += delegate { Controller.CheckSetupPage (name_entry.Text, email_entry.Text); };
email_entry.Changed += delegate { Controller.CheckSetupPage (name_entry.Text, email_entry.Text); };
cancel_button.Clicked += delegate { Controller.SetupPageCancelled (); };
continue_button.Clicked += delegate {
Controller.SetupPageCompleted (name_entry.Text, email_entry.Text);
};
AddButton (cancel_button);
AddButton (continue_button);
Add (wrapper);
Controller.CheckSetupPage (name_entry.Text, email_entry.Text);
if (name_entry.Text.Equals (""))
name_entry.GrabFocus ();
else
email_entry.GrabFocus ();
}
if (type == PageType.Add) {
Header = "Wheres your project hosted?";
VBox layout_vertical = new VBox (false, 16);
HBox layout_fields = new HBox (true, 32);
VBox layout_address = new VBox (true, 0);
VBox layout_path = new VBox (true, 0);
ListStore store = new ListStore (typeof (string), typeof (Gdk.Pixbuf), typeof (string), typeof (Preset));
SparkleTreeView tree_view = new SparkleTreeView (store) {
HeadersVisible = false,
SearchColumn = -1,
EnableSearch = false
};
ScrolledWindow scrolled_window = new ScrolledWindow () { ShadowType = ShadowType.In };
scrolled_window.SetPolicy (PolicyType.Never, PolicyType.Automatic);
// Padding column
tree_view.AppendColumn ("Padding", new Gtk.CellRendererText (), "text", 0);
tree_view.Columns [0].Cells [0].Xpad = 8;
// Icon column
tree_view.AppendColumn ("Icon", new Gtk.CellRendererPixbuf (), "pixbuf", 1);
tree_view.Columns [1].Cells [0].Xpad = 6;
// Service column
TreeViewColumn service_column = new TreeViewColumn () { Title = "Service" };
CellRendererText service_cell = new CellRendererText () { Ypad = 12 };
service_column.PackStart (service_cell, true);
service_column.SetCellDataFunc (service_cell, new TreeCellDataFunc (RenderServiceColumn));
foreach (Preset preset in Controller.Presets) {
store.AppendValues ("", new Gdk.Pixbuf (preset.ImagePath),
"<span><b>" + preset.Name + "</b>\n" +
"<span size=\"small\" fgcolor=\"" + SparkleShare.UI.SecondaryTextColor + "\">" + preset.Description + "</span>" +
"</span>", preset);
}
tree_view.AppendColumn (service_column);
scrolled_window.Add (tree_view);
Entry address_entry = new Entry () {
Text = Controller.PreviousAddress,
Sensitive = (Controller.SelectedPreset.Address == null),
ActivatesDefault = true
};
Entry path_entry = new Entry () {
Text = Controller.PreviousPath,
Sensitive = (Controller.SelectedPreset.Path == null),
ActivatesDefault = true
};
tree_view.ButtonReleaseEvent += delegate {
path_entry.GrabFocus ();
};
Label address_example = new Label () {
Xalign = 0,
UseMarkup = true,
Markup = "<span size=\"small\" fgcolor=\"" +
SparkleShare.UI.SecondaryTextColor + "\">" + Controller.SelectedPreset.AddressExample + "</span>"
};
Label path_example = new Label () {
Xalign = 0,
UseMarkup = true,
Markup = "<span size=\"small\" fgcolor=\"" +
SparkleShare.UI.SecondaryTextColor + "\">" + Controller.SelectedPreset.PathExample + "</span>"
};
TreeSelection default_selection = tree_view.Selection;
TreePath default_path = new TreePath ("" + Controller.SelectedPresetIndex);
default_selection.SelectPath (default_path);
tree_view.Model.Foreach (new TreeModelForeachFunc (
delegate (ITreeModel model, TreePath path, TreeIter iter) {
string address;
try {
address = (model.GetValue (iter, 2) as Preset).Address;
} catch (NullReferenceException) {
address = "";
}
if (!string.IsNullOrEmpty (address) &&
address.Equals (Controller.PreviousAddress)) {
tree_view.SetCursor (path, service_column, false);
Preset preset = (Preset) model.GetValue (iter, 2);
if (preset.Address != null)
address_entry.Sensitive = false;
if (preset.Path != null)
path_entry.Sensitive = false;
return true;
} else {
return false;
}
}
));
layout_address.PackStart (new Label () {
Markup = "<b>" + "Address" + "</b>",
Xalign = 0
}, true, true, 0);
layout_address.PackStart (address_entry, false, false, 0);
layout_address.PackStart (address_example, false, false, 0);
path_entry.Changed += delegate {
Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree_view.SelectedRow);
};
layout_path.PackStart (new Label () {
Markup = "<b>" + "Remote Path" + "</b>",
Xalign = 0
}, true, true, 0);
layout_path.PackStart (path_entry, false, false, 0);
layout_path.PackStart (path_example, false, false, 0);
layout_fields.PackStart (layout_address, true, true, 0);
layout_fields.PackStart (layout_path, true, true, 0);
layout_vertical.PackStart (scrolled_window, true, true, 0);
layout_vertical.PackStart (layout_fields, false, false, 0);
tree_view.ScrollToCell (new TreePath ("" + Controller.SelectedPresetIndex), null, true, 0, 0);
Add (layout_vertical);
if (string.IsNullOrEmpty (path_entry.Text)) {
address_entry.GrabFocus ();
address_entry.Position = -1;
} else {
path_entry.GrabFocus ();
path_entry.Position = -1;
}
Button cancel_button = new Button ("Cancel");
Button add_button = new Button ("Add") { Sensitive = false };
Controller.ChangeAddressFieldEvent += delegate (string text,
string example_text, FieldState state) {
Application.Invoke (delegate {
address_entry.Text = text;
address_entry.Sensitive = (state == FieldState.Enabled);
address_example.Markup = "<span size=\"small\" fgcolor=\"" +
SparkleShare.UI.SecondaryTextColor + "\">" + example_text + "</span>";
});
};
Controller.ChangePathFieldEvent += delegate (string text,
string example_text, FieldState state) {
Application.Invoke (delegate {
path_entry.Text = text;
path_entry.Sensitive = (state == FieldState.Enabled);
path_example.Markup = "<span size=\"small\" fgcolor=\""
+ SparkleShare.UI.SecondaryTextColor + "\">" + example_text + "</span>";
});
};
Controller.UpdateAddProjectButtonEvent += delegate (bool button_enabled) {
Application.Invoke (delegate { add_button.Sensitive = button_enabled; });
};
tree_view.CursorChanged += delegate (object sender, EventArgs e) {
Controller.SelectedPresetChanged (tree_view.SelectedRow);
};
address_entry.Changed += delegate {
Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree_view.SelectedRow);
};
cancel_button.Clicked += delegate { Controller.PageCancelled (); };
add_button.Clicked += delegate { Controller.AddPageCompleted (address_entry.Text, path_entry.Text); };
CheckButton check_button = new CheckButton ("Fetch prior revisions") { Active = false };
check_button.Toggled += delegate { Controller.HistoryItemChanged (check_button.Active); };
AddOption (check_button);
AddButton (cancel_button);
AddButton (add_button);
Controller.HistoryItemChanged (check_button.Active);
Controller.CheckAddPage (address_entry.Text, path_entry.Text, 1);
}
if (type == PageType.Invite) {
Header = "Youve received an invite!";
Description = "Do you want to add this project to SparkleShare?";
Table table = new Table (2, 3, true) {
RowSpacing = 6,
ColumnSpacing = 6
};
Label address_label = new Label ("Address:") { Xalign = 1 };
Label path_label = new Label ("Remote Path:") { Xalign = 1 };
Label address_value = new Label ("<b>" + Controller.PendingInvite.Address + "</b>") {
UseMarkup = true,
Xalign = 0
};
Label path_value = new Label ("<b>" + Controller.PendingInvite.RemotePath + "</b>") {
UseMarkup = true,
Xalign = 0
};
table.Attach (address_label, 0, 1, 0, 1);
table.Attach (address_value, 1, 2, 0, 1);
table.Attach (path_label, 0, 1, 1, 2);
table.Attach (path_value, 1, 2, 1, 2);
VBox wrapper = new VBox (false, 9);
wrapper.PackStart (table, true, false, 0);
Button cancel_button = new Button ("Cancel");
Button add_button = new Button ("Add");
cancel_button.Clicked += delegate { Controller.PageCancelled (); };
add_button.Clicked += delegate { Controller.InvitePageCompleted (); };
AddButton (cancel_button);
AddButton (add_button);
Add (wrapper);
}
if (type == PageType.Syncing) {
Header = String.Format ("Adding project {0}’…", Controller.SyncingFolder);
Description = "This may take a while for large projects.\nIsnt it coffee-oclock?";
ProgressBar progress_bar = new ProgressBar ();
progress_bar.Fraction = Controller.ProgressBarPercentage / 100;
Button cancel_button = new Button () { Label = "Cancel" };
Button finish_button = new Button ("Finish") { Sensitive = false };
Label progress_label = new Label ("Preparing to fetch files…") {
Justify = Justification.Right,
Xalign = 1
};
Controller.UpdateProgressBarEvent += delegate (double percentage, string speed) {
Application.Invoke (delegate {
progress_bar.Fraction = percentage / 100;
progress_label.Text = speed;
});
};
cancel_button.Clicked += delegate { Controller.SyncingCancelled (); };
VBox bar_wrapper = new VBox (false, 0);
bar_wrapper.PackStart (progress_bar, false, false, 21);
bar_wrapper.PackStart (progress_label, false, true, 0);
Add (bar_wrapper);
AddButton (cancel_button);
AddButton (finish_button);
}
if (type == PageType.Error) {
Header = "Oops! Something went wrong" + "…";
VBox points = new VBox (false, 0);
Image list_point_one = new Image (UserInterfaceHelpers.GetIcon ("list-point", 16));
Image list_point_two = new Image (UserInterfaceHelpers.GetIcon ("list-point", 16));
Image list_point_three = new Image (UserInterfaceHelpers.GetIcon ("list-point", 16));
Label label_one = new Label () {
Markup = "<b>" + Controller.PreviousUrl + "</b> is the address weve compiled. " +
"Does this look alright?",
Wrap = true,
Xalign = 0
};
Label label_two = new Label () {
Text = "Is this computers Client ID known by the host?",
Wrap = true,
Xalign = 0
};
points.PackStart (new Label ("Please check the following:") { Xalign = 0 }, false, false, 6);
HBox point_one = new HBox (false, 0);
point_one.PackStart (list_point_one, false, false, 0);
point_one.PackStart (label_one, true, true, 12);
points.PackStart (point_one, false, false, 12);
HBox point_two = new HBox (false, 0);
point_two.PackStart (list_point_two, false, false, 0);
point_two.PackStart (label_two, true, true, 12);
points.PackStart (point_two, false, false, 12);
if (warnings.Length > 0) {
string warnings_markup = "";
foreach (string warning in warnings)
warnings_markup += "\n<b>" + warning + "</b>";
Label label_three = new Label () {
Markup = "Heres the raw error message:" + warnings_markup,
Wrap = true,
Xalign = 0
};
HBox point_three = new HBox (false, 0);
point_three.PackStart (list_point_three, false, false, 0);
point_three.PackStart (label_three, true, true, 12);
points.PackStart (point_three, false, false, 12);
}
points.PackStart (new Label (""), true, true, 0);
Button cancel_button = new Button ("Cancel");
Button try_again_button = new Button ("Retry") { Sensitive = true };
cancel_button.Clicked += delegate { Controller.PageCancelled (); };
try_again_button.Clicked += delegate { Controller.ErrorPageCompleted (); };
AddButton (cancel_button);
AddButton (try_again_button);
Add (points);
}
if (type == PageType.StorageSetup) {
Header = string.Format ("Storage type for {0}", Controller.SyncingFolder);
Description = "What type of storage would you like to use?";
VBox layout_vertical = new VBox (false, 0);
VBox layout_radio_buttons = new VBox (false, 0) { BorderWidth = 12 };
foreach (StorageTypeInfo storage_type in SparkleShare.Controller.FetcherAvailableStorageTypes) {
RadioButton radio_button = new RadioButton (null,
storage_type.Name + "\n" + storage_type.Description);
(radio_button.Child as Label).Markup = string.Format(
"<b>{0}</b>\n<span fgcolor=\"{1}\">{2}</span>",
storage_type.Name, SparkleShare.UI.SecondaryTextColor, storage_type.Description);
(radio_button.Child as Label).Xpad = 9;
layout_radio_buttons.PackStart (radio_button, false, false, 9);
radio_button.Group = (layout_radio_buttons.Children [0] as RadioButton).Group;
}
layout_vertical.PackStart (new Label (""), true, true, 0);
layout_vertical.PackStart (layout_radio_buttons, false, false, 0);
layout_vertical.PackStart (new Label (""), true, true, 0);
Add (layout_vertical);
Button cancel_button = new Button ("Cancel");
Button continue_button = new Button ("Continue");
continue_button.Clicked += delegate {
int checkbox_index= 0;
foreach (RadioButton radio_button in layout_radio_buttons.Children) {
if (radio_button.Active) {
StorageTypeInfo selected_storage_type = SparkleShare.Controller.FetcherAvailableStorageTypes [checkbox_index];
Controller.StoragePageCompleted (selected_storage_type.Type);
return;
}
checkbox_index++;
}
};
cancel_button.Clicked += delegate {
Controller.SyncingCancelled ();
};
AddButton (cancel_button);
AddButton (continue_button);
}
if (type == PageType.CryptoSetup || type == PageType.CryptoPassword) {
if (type == PageType.CryptoSetup) {
Header = string.Format ("Encryption password for {0}", Controller.SyncingFolder);
Description = "Please a provide a strong password that you dont use elsewhere.";
} else {
Header = string.Format ("{0} contains encrypted files", Controller.SyncingFolder);
Description = "Please enter the password to see their contents.";
}
Label password_label = new Label ("<b>" + "Password" + "</b>") {
UseMarkup = true,
Xalign = 1
};
Entry password_entry = new Entry () {
Xalign = 0,
Visibility = false,
ActivatesDefault = true
};
CheckButton show_password_check_button = new CheckButton ("Make visible") {
Active = false,
Xalign = 0,
};
Table table = new Table (2, 3, true) {
RowSpacing = 6,
ColumnSpacing = 6
};
table.Attach (password_label, 0, 1, 0, 1);
table.Attach (password_entry, 1, 2, 0, 1);
table.Attach (show_password_check_button, 1, 2, 1, 2);
VBox wrapper = new VBox (false, 9);
wrapper.PackStart (table, true, false, 0);
Image warning_image = new Image (
UserInterfaceHelpers.GetIcon ("dialog-information", 24));
Label warning_label = new Label () {
Xalign = 0,
Wrap = true,
Text = "This password cant be changed later, and your files cant be recovered if its forgotten."
};
HBox warning_layout = new HBox (false, 0);
warning_layout.PackStart (warning_image, false, false, 15);
warning_layout.PackStart (warning_label, true, true, 0);
VBox warning_wrapper = new VBox (false, 0);
warning_wrapper.PackStart (warning_layout, false, false, 15);
if (type == PageType.CryptoSetup)
wrapper.PackStart (warning_wrapper, false, false, 0);
Button cancel_button = new Button ("Cancel");
Button continue_button = new Button ("Continue") { Sensitive = false };
Controller.UpdateCryptoSetupContinueButtonEvent += delegate (bool button_enabled) {
Application.Invoke (delegate { continue_button.Sensitive = button_enabled; });
};
Controller.UpdateCryptoPasswordContinueButtonEvent += delegate (bool button_enabled) {
Application.Invoke (delegate { continue_button.Sensitive = button_enabled; });
};
show_password_check_button.Toggled += delegate {
password_entry.Visibility = !password_entry.Visibility;
};
password_entry.Changed += delegate {
if (type == PageType.CryptoSetup)
Controller.CheckCryptoSetupPage (password_entry.Text);
else
Controller.CheckCryptoPasswordPage (password_entry.Text);
};
cancel_button.Clicked += delegate { Controller.CryptoPageCancelled (); };
continue_button.Clicked += delegate {
if (type == PageType.CryptoSetup)
Controller.CryptoSetupPageCompleted (password_entry.Text);
else
Controller.CryptoPasswordPageCompleted (password_entry.Text);
};
Add (wrapper);
AddButton (cancel_button);
AddButton (continue_button);
password_entry.GrabFocus ();
}
if (type == PageType.Finished) {
Header = "Your shared project is ready!";
Description = "You can find the files in your SparkleShare folder.";
UrgencyHint = true;
Button show_files_button = new Button ("Show Files");
Button finish_button = new Button ("Finish");
show_files_button.Clicked += delegate { Controller.ShowFilesClicked (); };
finish_button.Clicked += delegate { Controller.FinishPageCompleted (); };
if (warnings.Length > 0) {
Image warning_image = new Image (UserInterfaceHelpers.GetIcon ("dialog-information", 24));
Label warning_label = new Label (warnings [0]) {
Xalign = 0,
Wrap = true
};
HBox warning_layout = new HBox (false, 0);
warning_layout.PackStart (warning_image, false, false, 15);
warning_layout.PackStart (warning_label, true, true, 0);
VBox warning_wrapper = new VBox (false, 0);
warning_wrapper.PackStart (warning_layout, false, false, 0);
Add (warning_wrapper);
} else {
Add (null);
}
AddButton (show_files_button);
AddButton (finish_button);
}
}
private void RenderServiceColumn (TreeViewColumn column, CellRenderer cell,
ITreeModel model, TreeIter iter)
{
string markup = (string) model.GetValue (iter, 2);
TreeSelection selection = (column.TreeView as TreeView).Selection;
if (selection.IterIsSelected (iter))
markup = markup.Replace (SparkleShare.UI.SecondaryTextColor, SparkleShare.UI.SecondaryTextColorSelected);
else
markup = markup.Replace (SparkleShare.UI.SecondaryTextColorSelected, SparkleShare.UI.SecondaryTextColor);
(cell as CellRendererText).Markup = markup;
}
private class SparkleTreeView : TreeView {
public int SelectedRow
{
get {
TreeIter iter;
ITreeModel model;
Selection.GetSelected (out model, out iter);
return int.Parse (model.GetPath (iter).ToString ());
}
}
public SparkleTreeView (ListStore store) : base (store)
{
}
}
}
}

View file

@ -0,0 +1,186 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Sparkles;
using Gtk;
namespace SparkleShare {
public class AddressPage : Page {
Entry address_entry;
DescriptionLabel address_example;
Entry path_entry;
DescriptionLabel path_example;
Label public_key_label;
Button copy_button;
Button add_button;
public AddressPage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = "Wheres your project hosted?";
Description = "";
Controller.PageCanContinueEvent += AddressPageCanContinueEventHandler;
Controller.AddressPagePublicKeyEvent += AddressPagePublicKeyEventHandler;
}
public override void Dispose ()
{
Controller.PageCanContinueEvent -= AddressPageCanContinueEventHandler;
Controller.AddressPagePublicKeyEvent -= AddressPagePublicKeyEventHandler;
}
public override object Render ()
{
// Address entry
address_entry = new Entry () {
Text = Controller.SelectedPreset.Address,
ActivatesDefault = true
};
address_example = new DescriptionLabel (Controller.SelectedPreset.AddressExample) { Xalign = 1 };
address_entry.Changed += delegate { Controller.CheckAddressPage (address_entry.Text, path_entry.Text); };
VBox layout_address = new VBox (false, 6);
layout_address.PackStart (new Label () {
Markup = "<b>Address</b>",
Xalign = 0 }, true, true, 0);
layout_address.PackStart (address_entry, false, false, 0);
layout_address.PackStart (address_example, false, false, 0);
path_entry = new Entry () {
Text = Controller.SelectedPreset.Path,
ActivatesDefault = true
};
path_entry.ClipboardPasted += delegate {
try {
path_entry.Text = new Uri (path_entry.Text).AbsolutePath;
path_entry.Position = path_entry.Text.Length;
} catch {
}
};
path_example = new DescriptionLabel (Controller.SelectedPreset.Path) { Xalign = 1 };
path_entry.Changed += delegate { Controller.CheckAddressPage (address_entry.Text, path_entry.Text); };
VBox layout_path = new VBox (false, 6);
layout_path.PackStart (new Label () {
Markup = "<b>Remote Path</b>",
Xalign = 0 }, true, true, 0);
layout_path.PackStart (path_entry, false, false, 0);
layout_path.PackStart (path_example, false, false, 0);
if (string.IsNullOrEmpty (path_entry.Text)) {
address_entry.GrabFocus ();
address_entry.Position = -1;
} else {
path_entry.GrabFocus ();
path_entry.Position = -1;
}
public_key_label = new Label () {
Markup = "Authenticating…\n",
UseMarkup = true,
Xalign = 0,
Yalign = 0
};
copy_button = new Button ("Copy") {
Visible = false,
NoShowAll = true
};
copy_button.Clicked += delegate {
SparkleShare.Controller.CopyToClipboard (SparkleShare.Controller.UserAuthenticationInfo.PublicKey);
};
var copy_layout = new HBox (false, 0);
copy_layout.PackStart (copy_button, false, false, 0);
VBox layout_fields = new VBox (false, 0) { BorderWidth = 24 };
layout_fields.PackStart (layout_address, false, false, 12);
layout_fields.PackStart (layout_path, false, false, 12);
layout_fields.PackStart (new Label () {
Markup = "<b>" + "Public Key" + "</b>",
Xalign = 0,
Yalign = 0
}, false, false, 12);
layout_fields.PackStart (public_key_label, false, false, 0);
layout_fields.PackStart (copy_layout, false, false, 12);
// Buttons
Button cancel_button = new Button ("Cancel");
add_button = new Button ("Add") { Sensitive = false };
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
add_button.Clicked += delegate { Controller.AddressPageCompleted ( address_entry.Text, path_entry.Text); };
Button back_button = new Button ("Back");
back_button.Clicked += delegate { Controller.BackClicked (RequestedType); };
Buttons = new Button [] { cancel_button, null, back_button, add_button };
var padding = new HBox (false, 0);
padding.PackStart (layout_fields, true, true, 128);
// Layout
return padding;
}
void AddressPagePublicKeyEventHandler (bool authenticated, string auth_status, string key_entry_hint)
{
Application.Invoke (delegate {
public_key_label.Markup = auth_status;
if (!authenticated) {
public_key_label.Markup = key_entry_hint + "\n" + "<span fgcolor=\"" +
SparkleShare.UI.SecondaryTextColor + "\">" + auth_status + "</span>";
copy_button.Show ();
}
});
}
void AddressPageCanContinueEventHandler (PageType page_type, bool can_continue)
{
if (page_type == RequestedType)
Application.Invoke (delegate { add_button.Sensitive = can_continue; });
}
}
}

View file

@ -0,0 +1,153 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Gtk;
namespace SparkleShare {
public class CryptoSetupPage : Page {
Button continue_button;
public CryptoSetupPage (PageType page_type, PageController controller) : base (page_type, controller)
{
if (RequestedType == PageType.CryptoSetup) {
Header = string.Format ("Encryption password for {0}", Controller.FetchAddress.AbsolutePath);
Description = "Please a provide a strong password that you dont use elsewhere.";
} else {
Header = string.Format ("{0} contains encrypted files", Controller.FetchAddress.AbsolutePath);
Description = "Please enter the password to see their contents.";
}
Controller.PageCanContinueEvent += CryptoPageCanContinueEventHandler;
}
public override void Dispose ()
{
Controller.PageCanContinueEvent -= CryptoPageCanContinueEventHandler;
}
public override object Render ()
{
// Password entry
Label password_label = new Label ("<b>" + "Password" + "</b>") {
UseMarkup = true,
Xalign = 1
};
Entry password_entry = new Entry () {
Xalign = 0,
Visibility = false,
ActivatesDefault = true
};
password_entry.Changed += delegate {
if (RequestedType == PageType.CryptoSetup)
Controller.CheckCryptoSetupPage (password_entry.Text);
else
Controller.CheckCryptoPasswordPage (password_entry.Text);
};
password_entry.GrabFocus ();
// Checkbox
CheckButton show_password_check_button = new CheckButton ("Make visible") {
Active = false,
Xalign = 0,
};
show_password_check_button.Toggled += delegate {
password_entry.Visibility = !password_entry.Visibility;
};
// Buttons
Button cancel_button = new Button ("Cancel");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
continue_button = new Button ("Continue") { Sensitive = false };
continue_button.Clicked += delegate { Controller.CryptoPageCompleted (password_entry.Text); };
Buttons = new Button [] { cancel_button, continue_button };
// Layout
Table table = new Table (2, 3, true) {
RowSpacing = 6,
ColumnSpacing = 6
};
table.Attach (password_label, 0, 1, 0, 1);
table.Attach (password_entry, 1, 2, 0, 1);
table.Attach (show_password_check_button, 1, 2, 1, 2);
VBox wrapper = new VBox (false, 9);
wrapper.PackStart (table, true, false, 0);
if (RequestedType == PageType.CryptoSetup)
wrapper.PackStart (RenderWarning (), false, false, 0);
return wrapper;
}
VBox RenderWarning ()
{
var image = new Image (
UserInterfaceHelpers.GetIcon ("dialog-information-symbolic", 24));
Label label = new Label () {
Xalign = 0,
Wrap = true,
Text = "This password cant be changed later, and your files cant be recovered if its forgotten."
};
// Layout
HBox layout = new HBox (false, 0);
layout.PackStart (image, false, false, 16);
layout.PackStart (label, true, true, 0);
VBox wrapper = new VBox (false, 0);
wrapper.PackStart (layout, false, false, 16);
return wrapper;
}
void CryptoPageCanContinueEventHandler (PageType page_type, bool can_continue)
{
if (page_type == RequestedType)
Application.Invoke (delegate { continue_button.Sensitive = can_continue; });
}
}
public class CryptoPasswordPage : CryptoSetupPage {
public CryptoPasswordPage (PageType page_type, PageController controller) : base (page_type, controller)
{
}
}
}

View file

@ -0,0 +1,169 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Sparkles;
using Gtk;
namespace SparkleShare {
public class HostPage : Page {
SparkleTreeView tree_view;
TreeViewColumn service_column;
public HostPage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = "Wheres your project hosted?";
Description = "";
}
public override object Render ()
{
// Host selection
ListStore store = new ListStore (typeof (string), typeof (Gdk.Pixbuf), typeof (string), typeof (Preset));
tree_view = new SparkleTreeView (store) {
HeadersVisible = false,
SearchColumn = -1,
EnableSearch = false
};
tree_view.CursorChanged += delegate { Controller.SelectedPresetChanged (tree_view.SelectedRow); };
// Padding column
tree_view.AppendColumn ("Padding", new Gtk.CellRendererText (), "text", 0);
tree_view.Columns [0].Cells [0].Xpad = 8;
// Icon column
tree_view.AppendColumn ("Icon", new Gtk.CellRendererPixbuf (), "pixbuf", 1);
tree_view.Columns [1].Cells [0].Xpad = 6;
// Service column
service_column = new TreeViewColumn () { Title = "Service" };
CellRendererText service_cell = new CellRendererText () { Ypad = 12 };
service_column.PackStart (service_cell, true);
service_column.SetCellDataFunc (service_cell, new TreeCellDataFunc (RenderServiceColumn));
tree_view.AppendColumn (service_column);
// Fill the list
foreach (Preset preset in Controller.Presets) {
store.AppendValues ("", new Gdk.Pixbuf (preset.IconPath),
string.Format ("<span><b>{0}</b>\n<span size='small' fgcolor='{1}'>{2}</span></span>",
preset.Name, SparkleShare.UI.SecondaryTextColor, preset.Description),
preset);
}
tree_view.Model.Foreach (new TreeModelForeachFunc (TreeModelForeachFuncHandler));
TreeSelection default_selection = tree_view.Selection;
TreePath default_path = new TreePath ("" + Controller.SelectedPresetIndex);
default_selection.SelectPath (default_path);
tree_view.ScrollToCell (new TreePath ("" + Controller.SelectedPresetIndex), null, true, 0, 0);
ScrolledWindow scrolled_window = new ScrolledWindow () { ShadowType = ShadowType.In };
scrolled_window.SetPolicy (PolicyType.Never, PolicyType.Automatic);
scrolled_window.Add (tree_view);
// Buttons
Button cancel_button = new Button ("Cancel");
Button continue_button = new Button ("Continue");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
continue_button.Clicked += delegate { Controller.HostPageCompleted (); };
Button back_button = new Button ("Back");
back_button.Clicked += delegate { Controller.BackClicked (RequestedType); };
Buttons = new Button [] { cancel_button, null, back_button, continue_button };
// Layout
VBox layout_vertical = new VBox (false, 16);
layout_vertical.PackStart (scrolled_window, true, true, 0);
return layout_vertical;
}
void RenderServiceColumn (TreeViewColumn column, CellRenderer cell, ITreeModel model, TreeIter iter)
{
string markup = (string) model.GetValue (iter, 2);
TreeSelection selection = (column.TreeView as TreeView).Selection;
if (selection.IterIsSelected (iter))
markup = markup.Replace (SparkleShare.UI.SecondaryTextColor, SparkleShare.UI.SecondaryTextColorSelected);
else
markup = markup.Replace (SparkleShare.UI.SecondaryTextColorSelected, SparkleShare.UI.SecondaryTextColor);
(cell as CellRendererText).Markup = markup;
}
bool TreeModelForeachFuncHandler (ITreeModel model, TreePath path, TreeIter iter)
{
string address;
try {
address = (model.GetValue (iter, 2) as Preset).Address;
} catch (NullReferenceException) {
address = "";
}
if (!string.IsNullOrEmpty (address) &&
address.Equals (Controller.FetchAddress.Host)) { // TODO Check selection
tree_view.SetCursor (path, service_column, false);
//Preset preset = (Preset) model.GetValue (iter, 2);
return true;
} else {
return false;
}
}
class SparkleTreeView : TreeView {
public int SelectedRow
{
get {
TreeIter iter;
ITreeModel model;
Selection.GetSelected (out model, out iter);
return int.Parse (model.GetPath (iter).ToString ());
}
}
public SparkleTreeView (ListStore store) : base (store)
{
}
}
}
}

View file

@ -0,0 +1,75 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Gtk;
namespace SparkleShare {
public class InvitePage : Page {
public InvitePage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = "Youve received an invite!";
Description = "Do you want to add this project to SparkleShare?";
// Buttons
Button cancel_button = new Button ("Cancel");
Button add_button = new Button ("Add");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
add_button.Clicked += delegate { Controller.InvitePageCompleted (); };
Buttons = new Button [] {cancel_button, add_button };
}
public override object Render ()
{
// Labels
Label address_label = new Label ("Address:") { Xalign = 1 };
Label address_value = new Label ("<b>" + Controller.PendingInvite.Address + "</b>") {
UseMarkup = true,
Xalign = 0
};
Label path_label = new Label ("Remote Path:") { Xalign = 1 };
Label path_value = new Label ("<b>" + Controller.PendingInvite.RemotePath + "</b>") {
UseMarkup = true,
Xalign = 0
};
// Layout
Table table = new Table (2, 3, true) {
RowSpacing = 6,
ColumnSpacing = 6
};
table.Attach (address_label, 0, 1, 0, 1);
table.Attach (address_value, 1, 2, 0, 1);
table.Attach (path_label, 0, 1, 1, 2);
table.Attach (path_value, 1, 2, 1, 2);
VBox wrapper = new VBox (false, 9);
wrapper.PackStart (table, true, false, 0);
return wrapper;
}
}
}

View file

@ -0,0 +1,143 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Gtk;
namespace SparkleShare {
public class PrivacyPage : Page {
Switch notification_service_switch;
Switch crash_reports_switch;
Switch gravatars_switch;
public PrivacyPage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = "Privacy Preferences";
Description = "";
}
public override void Dispose ()
{
}
public override object Render ()
{
var intro_label = new Label () {
Text = "SparkleShare is decentralized, so no account of any sort is created.\n" +
"However, some features can affect your privacy:",
LineWrap = true,
LineWrapMode = Pango.WrapMode.Word,
MaxWidthChars = 48,
Xalign = 0
};
HBox notification_service_layout = SwitchLayout ("Notification Service",
"Instantly syncs when someone makes a change by sending a notification " +
"via <a href='https://www.sparkleshare.org/'>sparkleshare.org</a>. " +
"No personal or usage data is recorded.", recommended: true);
notification_service_switch = (Switch) (notification_service_layout.Children [0] as Box).Children [0];
HBox crash_reports_layout = SwitchLayout ("Crash Reports",
"In the unlikely event of a SparkleShare crash, sends an anonymized report to " +
"the project maintainers to help fix bugs.", recommended: false);
crash_reports_switch = (Switch) (crash_reports_layout.Children [0] as Box).Children [0];
HBox gravatars_layout = SwitchLayout ("Gravatars",
"Uses <a href='https://www.gravatar.com/'>gravatar.com</a> to download " +
"profile pictures.", recommended: false);
gravatars_switch = (Switch) (gravatars_layout.Children [0] as Box).Children [0];
// Tip
var tip_label = new Label ("You will be able to change these preferences later.") { Xalign = 0 };
// Buttons
Button quit_button = new Button ("Quit");
quit_button.Clicked += delegate { Controller.QuitClicked (); };
Button back_button = new Button ("Back");
back_button.Clicked += delegate { Controller.BackClicked (RequestedType); };
Button continue_button = new Button ("Continue");
continue_button.Clicked += ContinueButtonClickedHandler;
Buttons = new Button [] { quit_button, null, back_button, continue_button };
// Layout
var layout = new VBox (false, 0) { MarginLeft = 32, MarginRight = 12 };
layout.PackStart (intro_label, false, false, 32);
layout.PackStart (notification_service_layout, false, false, 12);
layout.PackStart (crash_reports_layout, false, false, 12);
layout.PackStart (gravatars_layout, false, false, 12);
layout.PackStart (tip_label, false, false, 32);
return layout;
}
void ContinueButtonClickedHandler (object sender, EventArgs args)
{
Controller.PrivacyPageCompleted (
notification_service: notification_service_switch.Active,
crash_reports: crash_reports_switch.Active,
gravatars: gravatars_switch.Active);
}
HBox SwitchLayout (string title, string description, bool recommended)
{
var light_switch = new Switch () { Active = recommended, HeightRequest = 36 };
string recommended_text = "";
if (recommended)
recommended_text = " Recommended";
var label = new Label () {
Markup = string.Format ("<b>{0}</b>{1}\n<span fgcolor='{2}'>{3}</span>",
title, recommended_text, SparkleShare.UI.SecondaryTextColor, description),
LineWrap = true,
LineWrapMode = Pango.WrapMode.Word,
MaxWidthChars = 48,
Xalign = 0
};
var box = new VBox (false, 0);
box.PackStart (light_switch, false, false, 6);
var layout = new HBox (false, 0) { MarginLeft = 24 };
layout.PackStart (box, false, false, 0);
layout.PackStart (label, true, true, 24);
return layout;
}
}
}

View file

@ -0,0 +1,186 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Sparkles; // TODO: remove
using Gtk;
namespace SparkleShare {
public class ProgressPage : Page {
Image image;
Label status_label;
Label status_details_label;
ProgressBar progress_bar;
Label progress_label;
public ProgressPage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = String.Format ("Getting files from {0}", Controller.SelectedPreset.Name);
Description = "Depending on its size, it may be a good time to make some tea.";
Controller.ProgressPageBarEvent += ProgressPageBarEventHandler;
}
public override void Dispose ()
{
Controller.ProgressPageBarEvent -= ProgressPageBarEventHandler;
// TODO: State handling
}
public override object Render ()
{
// image = new Image ();
// Progress bar
progress_bar = new ProgressBar ();
progress_bar.Fraction = Controller.ProgressBarPercentage / 100;
progress_label = new Label () {
Markup = string.Format ("Getting files from <b>{0}</b>", Controller.FetchAddress.Authority),
Xalign = 0
};
Buttons = ProgressButtons ();
// UrgencyHint = true; TODO
//VBox wrapper = new VBox (false, 0);
/* TODO
// Buttons
Button show_files_button = new Button ("Show Files");
Button finish_button = new Button ("Finish");
show_files_button.Clicked += delegate { Controller.ShowFilesClicked (); };
finish_button.Clicked += delegate { Controller.FinishPageCompleted (); };
Buttons = new Button [] { show_files_button, finish_button };
if (warnings.Length > 0) {
Image warning_image = new Image (UserInterfaceHelpers.GetIcon ("dialog-information", 24));
Label warning_label = new Label (warnings [0]) {
Xalign = 0,
Wrap = true
};
HBox warning_layout = new HBox (false, 0);
warning_layout.PackStart (warning_image, false, false, 15);
warning_layout.PackStart (warning_label, true, true, 0);
VBox wrapper = new VBox (false, 0);
wrapper.PackStart (warning_layout, false, false, 0);
Add (wrapper);
} else {
Add (null);
}
*/
image = new Image (Controller.SelectedPreset.LogoPath);
image = new Image ("dialog-error-symbolic", (IconSize) 128);
status_label = new Label () { Markup = "<b>" + Header + "</b>" };
status_details_label = new Label ("error goes here");
var status_layout = new VBox (false, 0);
status_layout.PackStart (image, false, false, 12);
status_layout.PackStart (status_label, false, false, 6);
status_layout.PackStart (status_details_label, false, false, 0);
// Layout
VBox layout = new VBox (false, 0) { BorderWidth = 48 };
layout.PackStart (status_layout, true, true, 0);
layout.PackStart (progress_bar, false, false, 24);
layout.PackStart (progress_label, false, false, 0);
return layout;
}
void ProgressPageBarEventHandler (bool? success,
string status, string status_details,
double progress, string progress_details)
{
Application.Invoke (delegate {
status_label.Markup = "<b>" + status + "</b>";
status_details_label.Text = status_details;
progress_bar.Sensitive = success.GetValueOrDefault ();
progress_bar.Fraction = progress / 100;
progress_label.Text = progress_details;
});
}
Button [] ProgressButtons ()
{
var cancel_button = new Button ("Cancel");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
var back_button = new Button ("Back") { Sensitive = false };
var done_button = new Button ("Done") { Sensitive = false };
return new Button [] { cancel_button, null, back_button, done_button };
}
Button [] ErrorButtons ()
{
var cancel_button = new Button ("Cancel");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
var back_button = new Button ("Back");
back_button.Clicked += delegate { Controller.BackClicked (RequestedType); };
var retry_button = new Button ("Retry");
retry_button.Clicked += delegate {
// TODO
};
return new Button [] { cancel_button, null, back_button, retry_button };
}
Button [] SuccessButtons ()
{
var open_files_button = new Button ("Open Files");
open_files_button.Clicked += delegate {
Controller.ShowFilesClicked (); // TODO
};
var done_button = new Button ("Done");
done_button.Clicked += delegate {
Controller.ProgressPageCompleted ();
};
return new Button [] { open_files_button, done_button };
}
}
}

View file

@ -0,0 +1,85 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Sparkles;
using Gtk;
namespace SparkleShare {
public class StoragePage : Page {
public StoragePage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = string.Format ("Storage type for {0}", Controller.FetchAddress.AbsolutePath);
Description = "What type of storage would you like to use?";
}
public override object Render ()
{
// Radio buttons
VBox radio_buttons = new VBox (false, 0) { BorderWidth = 12 };
foreach (StorageTypeInfo storage_type in SparkleShare.Controller.FetcherAvailableStorageTypes) {
RadioButton radio_button = new RadioButton (null,
storage_type.Name + "\n" + storage_type.Description);
(radio_button.Child as Label).Markup =
string.Format("<b>{0}</b>\n<span fgcolor=\"{1}\">{2}</span>",
storage_type.Name, SparkleShare.UI.SecondaryTextColor, storage_type.Description);
(radio_button.Child as Label).Xpad = 9;
radio_buttons.PackStart (radio_button, false, false, 9);
radio_button.Group = (radio_buttons.Children [0] as RadioButton).Group;
}
// Buttons
Button cancel_button = new Button ("Cancel");
cancel_button.Clicked += delegate { Controller.CancelClicked (RequestedType); };
Button continue_button = new Button ("Continue");
continue_button.Clicked += delegate {
int checkbox_index= 0;
foreach (RadioButton radio_button in radio_buttons.Children) {
if (radio_button.Active) {
StorageTypeInfo selected_storage_type = SparkleShare.Controller.FetcherAvailableStorageTypes [checkbox_index];
Controller.StoragePageCompleted (selected_storage_type.Type);
return;
}
checkbox_index++;
}
};
Buttons = new Button [] { cancel_button, continue_button };
// Layout
VBox layout = new VBox (false, 0);
layout.PackStart (new Label (""), true, true, 0);
layout.PackStart (radio_buttons, false, false, 0);
layout.PackStart (new Label (""), true, true, 0);
return layout;
}
}
}

View file

@ -0,0 +1,145 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Sparkles;
using Gtk;
using Mono.Unix;
namespace SparkleShare {
public class UserPage : Page {
Entry name_entry;
Entry email_entry;
Button continue_button;
public UserPage (PageType page_type, PageController controller) : base (page_type, controller)
{
Header = "Welcome to SparkleShare";
Description = "Hello. Let's get you set up.";
Controller.PageCanContinueEvent += UserPageCanContinueEventHandler;
}
public override void Dispose ()
{
Controller.PageCanContinueEvent -= UserPageCanContinueEventHandler;
}
public override object Render ()
{
// Name
Label name_label = new Label () {
Markup = "<b>Name</b>",
Xalign = 0
};
name_entry = new Entry ();
name_entry.Changed += delegate { Controller.CheckUserPage (name_entry.Text, email_entry.Text); };
// TODO: fill name and email when Back was pressed on privacy page
/* TODO broken
try {
UnixUserInfo user_info = UnixUserInfo.GetRealUser ();
if (user_info != null && user_info.RealName != null) {
// Some systems append a series of "," for some reason
string name = "" + user_info.RealName;
name_entry.Text = name;
}
} catch (Exception) {
// No username, not a big deal
}
*/
// Email
Label email_label = new Label () {
Markup = "<b>Email</b>",
Xalign = 0
};
email_entry = new Entry () { ActivatesDefault = true };
email_entry.Changed += delegate { Controller.CheckUserPage (name_entry.Text, email_entry.Text); };
var data_hint = new DescriptionLabel (
"The following information is used to mark your changes in a project's history. " +
"It won't be sent anywhere, but will be visible to those working on the same project.") {
LineWrap = true,
LineWrapMode = Pango.WrapMode.Word,
MaxWidthChars = 48
};
// Buttons
Button quit_button = new Button ("Quit");
quit_button.Clicked += delegate { Controller.QuitClicked (); };
continue_button = new Button ("Continue") { Sensitive = false };
continue_button.Clicked += delegate { Controller.UserPageCompleted (name_entry.Text, email_entry.Text); };
Buttons = new Button [] { quit_button, continue_button };
if (name_entry.Text.Equals (""))
name_entry.GrabFocus ();
else
email_entry.GrabFocus ();
Controller.CheckUserPage (name_entry.Text, email_entry.Text);
// Layout
VBox layout = new VBox (false, 9) { BorderWidth = 64 };
layout.PackStart (new Label (Description) { Xalign = 0 }, false, false, 0);
layout.PackStart (data_hint, false, false, 0);
VBox layout_fields = new VBox (false, 0) { BorderWidth = 32 };
VBox layout_name = new VBox (false, 6);
layout_name.PackStart (name_label, false, false, 0);
layout_name.PackStart (name_entry, false, false, 0);
VBox layout_email = new VBox (false, 6);
layout_email.PackStart (email_label, false, false, 0);
layout_email.PackStart (email_entry, false, false, 0);
layout_fields.PackStart (layout_name, false, false, 12);
layout_fields.PackStart (layout_email, false, false, 12);
layout.PackStart (layout_fields, false, false, 0);
return layout;
}
void UserPageCanContinueEventHandler (PageType page_type, bool can_continue)
{
if (page_type == RequestedType)
Application.Invoke (delegate { continue_button.Sensitive = can_continue; });
}
}
}

View file

@ -0,0 +1,76 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General private 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 private License for more details.
//
// You should have received a copy of the GNU General private License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Gtk;
using Sparkles;
namespace SparkleShare {
public class Setup : SetupWindow {
public PageController Controller = new PageController ();
Page active_page;
public Setup ()
{
Controller.HideWindowEvent += () => {
Application.Invoke (delegate {
Hide ();
if (active_page != null) {
active_page.Dispose ();
active_page = null;
}
});
};
Controller.ShowWindowEvent += () => {
Application.Invoke (delegate {
ShowAll ();
Present ();
});
};
Controller.ChangePageEvent += (PageType type) => {
Application.Invoke (delegate {
Reset ();
ShowPage (type);
ShowAll ();
Present ();
});
};
}
void ShowPage (PageType page_type)
{
System.Type t = System.Type.GetType ("SparkleShare." + page_type + "Page", throwOnError: true);
Page page = (Page) Activator.CreateInstance (t, new object [] { page_type, Controller });
Header = page.Header;
Description = page.Description;
Add (page.Render ());
AddButtons (page.Buttons);
active_page = page;
}
}
}

View file

@ -20,90 +20,45 @@ using Gtk;
namespace SparkleShare {
public class SetupWindow : Window {
public class SetupWindow : Window {
private EventBox content_area;
private EventBox option_area;
private HBox buttons;
public const int Spacing = 18;
public string Header;
public string Description;
EventBox content_area;
HBox buttons;
public SetupWindow () : base ("SparkleShare Setup")
public SetupWindow () : base ("SparkleShare")
{
IconName = "org.sparkleshare.SparkleShare";
Resizable = false;
WindowPosition = WindowPosition.CenterAlways;
WindowPosition = WindowPosition.Center;
Deletable = false;
TypeHint = Gdk.WindowTypeHint.Dialog;
SetSizeRequest (720, 540);
DeleteEvent += delegate (object sender, DeleteEventArgs args) { args.RetVal = true; };
VBox layout_vertical = new VBox (false, 16);
layout_vertical.BorderWidth = 16;
VBox layout_vertical = new VBox (false, 0);
layout_vertical.BorderWidth = Spacing;
layout_vertical.Spacing = Spacing;
this.content_area = new EventBox ();
this.option_area = new EventBox ();
this.buttons = CreateButtonBox ();
HBox layout_actions = new HBox (false , 16);
layout_actions.PackStart (this.option_area, true, true, 0);
layout_actions.PackStart (this.buttons, false, false, 0);
layout_vertical.PackStart (this.content_area, true, true, 0);
layout_vertical.PackStart (layout_actions, false, false, 0);
base.Add (layout_vertical);
}
private HBox CreateButtonBox ()
{
return new HBox () {
this.buttons = new HBox () {
BorderWidth = 0,
Homogeneous = false,
Spacing = 6
};
}
layout_vertical.PackStart (this.content_area, true, true, 0);
layout_vertical.PackStart (this.buttons, false, false, 0);
public void AddButton (Button button)
{
(button.Child as Label).Xpad = 15;
this.buttons.Add (button);
}
public void AddOption (Widget widget)
{
this.option_area.Add (widget);
}
new public void Add (Widget widget)
{
Title = Header;
VBox layout_vertical = new VBox (false, 0);
if (!string.IsNullOrEmpty (Description)) {
Label description = new Label (Description) {
Xalign = 0,
LineWrap = true,
LineWrapMode = Pango.WrapMode.WordChar
};
layout_vertical.PackStart (description, false, false, 0);
}
if (widget != null)
layout_vertical.PackStart (widget, true, true, 0);
this.content_area.Add (layout_vertical);
base.Add (layout_vertical);
}
@ -112,28 +67,47 @@ namespace SparkleShare {
Header = "";
Description = "";
if (this.option_area.Children.Length > 0)
this.option_area.Remove (this.option_area.Children [0]);
if (this.content_area.Children.Length > 0)
this.content_area.Remove (this.content_area.Children [0]);
foreach (Button button in this.buttons)
foreach (Widget button in this.buttons)
this.buttons.Remove (button);
}
public void AddButtons (object [] buttons)
{
if (!Array.Exists (buttons, button => button == null))
this.buttons.PackStart (new Label (""), true, true, 0);
foreach (Button button in buttons) {
if (button == null) {
this.buttons.PackStart (new Label (""), true, true, 0);
} else {
button.WidthRequest = 100;
this.buttons.PackStart (button, false, false, 0);
}
}
var default_button = (Button) buttons [buttons.Length - 1];
default_button.CanDefault = true;
default_button.StyleContext.AddClass ("suggested-action");
Default = default_button;
}
public void Add (object widget)
{
if (widget != null)
this.content_area.Add ((Widget) widget);
}
new public void ShowAll ()
{
if (this.buttons.Children.Length > 0) {
Button default_button = (Button) this.buttons.Children [this.buttons.Children.Length - 1];
default_button.CanDefault = true;
Default = default_button;
default_button.StyleContext.AddClass ("suggested-action");
}
Present ();
Title = Header;
base.ShowAll ();
}
}

View file

@ -216,34 +216,9 @@ namespace SparkleShare {
this.recent_events_item = new MenuItem ("Recent Changes…");
this.recent_events_item.Sensitive = Controller.RecentEventsItemEnabled;
if (!use_appindicator)
(folder_item.Submenu as Menu).Add (new SeparatorMenuItem ());
(folder_item.Submenu as Menu).Add (this.recent_events_item);
this.quit_item = new MenuItem ("Quit") { Sensitive = Controller.QuitItemEnabled };
MenuItem add_item = new MenuItem ("Sync Remote Project…");
MenuItem link_code_item = new MenuItem ("Computer ID");
if (Controller.LinkCodeItemEnabled) {
link_code_item.Submenu = new Menu ();
string link_code = SparkleShare.Controller.UserAuthenticationInfo.PublicKey.Substring (0, 20) + "...";
MenuItem code_item = new MenuItem (link_code) { Sensitive = false };
MenuItem copy_item = new MenuItem ("Copy to Clipboard");
copy_item.Activated += delegate { Controller.CopyToClipboardClicked (); };
(link_code_item.Submenu as Menu).Add (code_item);
if (!use_appindicator)
(link_code_item.Submenu as Menu).Add (new SeparatorMenuItem ());
(link_code_item.Submenu as Menu).Add (copy_item);
}
MenuItem about_item = new MenuItem ("About SparkleShare");
about_item.Activated += delegate { Controller.AboutClicked (); };
@ -252,9 +227,9 @@ namespace SparkleShare {
this.quit_item.Activated += delegate { Controller.QuitClicked (); };
this.menu.Add (new SeparatorMenuItem ());
this.menu.Add (this.recent_events_item);
this.menu.Add (add_item);
this.menu.Add (link_code_item);
this.menu.Add (new SeparatorMenuItem ());
this.menu.Add (new SeparatorMenuItem ());
this.menu.Add (about_item);
this.menu.Add (new SeparatorMenuItem ());
this.menu.Add (this.quit_item);
@ -266,7 +241,7 @@ namespace SparkleShare {
#endif
}
}
// Makes the menu visible
void ShowMenu (object o, EventArgs args)

View file

@ -165,5 +165,43 @@ namespace SparkleShare
SecondaryTextColor = UserInterfaceHelpers.ColorToHex (text_color);
SecondaryTextColorSelected = UserInterfaceHelpers.ColorToHex (text_color_selected);
}
// The bindings don't support Gtk.Widget.ScaleFactor yet
public int ScaleFactor {
get {
if (scale_factor > 0)
return scale_factor;
var gsettings = new Command ("gsettings", "get org.gnome.desktop.interface scaling-factor");
string output = gsettings.StartAndReadStandardOutput ();
// Output: "uint32 1"
try {
scale_factor = Int32.Parse (output.Substring (7));
} catch (Exception e) {
scale_factor = 1;
Logger.LogInfo ("UI", "Could not determine screen scale factor: ", e);
}
return scale_factor;
}
}
int scale_factor;
}
public class DescriptionLabel : Label
{
public DescriptionLabel (string text) : base ()
{
Sensitive = false;
Xalign = 0;
Markup = string.Format ("<span size='small'>{0}</span>", text);
}
}
}

View file

@ -31,7 +31,7 @@ namespace SparkleShare {
icon_theme.AppendSearchPath (Path.Combine (UserInterface.AssetsPath, "icons"));
foreach (string search_path in IconTheme.Default.SearchPath)
icon_theme.AppendSearchPath (search_path);
icon_theme.AppendSearchPath (search_path);
try {
return icon_theme.LoadIcon (name, size, IconLookupFlags.GenericFallback);
@ -51,6 +51,9 @@ namespace SparkleShare {
public static Image GetImage (string name)
{
// if (SparkleShare.UI.ScaleFactor > 1)
// name += string.Format ("@{0}x", SparkleShare.UI.ScaleFactor);
string image_path = Path.Combine (UserInterface.AssetsPath, "pixmaps", name);
return new Image (image_path);
}

View file

@ -5,18 +5,35 @@ sparkleshare_src = ['../Common/SparkleShare.cs',
'../Common/BaseController.cs',
'../Common/EventLogController.cs',
'../Common/NoteController.cs',
'../Common/SetupController.cs',
'../Common/Page.cs',
'../Common/PageController.cs',
'../Common/PageControllers/PageController.Address.cs',
'../Common/PageControllers/PageController.Crypto.cs',
'../Common/PageControllers/PageController.Host.cs',
'../Common/PageControllers/PageController.Invite.cs',
'../Common/PageControllers/PageController.Progress.cs',
'../Common/PageControllers/PageController.Privacy.cs',
'../Common/PageControllers/PageController.User.cs',
'../Common/PageControllers/PageController.Storage.cs',
'../Common/StatusIconController.cs',
'About.cs',
'Bubbles.cs',
'Controller.cs',
'EventLog.cs',
'Note.cs',
'Setup.cs',
'SetupWindow.cs',
'StatusIcon.cs',
'UserInterface.cs',
'UserInterfaceHelpers.cs']
'UserInterface/About.cs',
'UserInterface/Bubbles.cs',
'UserInterface/EventLog.cs',
'UserInterface/Note.cs',
'UserInterface/Setup.cs',
'UserInterface/SetupWindow.cs',
'UserInterface/StatusIcon.cs',
'UserInterface/UserInterface.cs',
'UserInterface/UserInterfaceHelpers.cs',
'UserInterface/Pages/Page.Address.cs',
'UserInterface/Pages/Page.Crypto.cs',
'UserInterface/Pages/Page.Host.cs',
'UserInterface/Pages/Page.Invite.cs',
'UserInterface/Pages/Page.Privacy.cs',
'UserInterface/Pages/Page.Progress.cs',
'UserInterface/Pages/Page.Storage.cs',
'UserInterface/Pages/Page.User.cs']
# Startup script

View file

@ -1,23 +0,0 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
//
// 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 Sparkles {
public abstract class AuthenticationInfo
{
}
}

View file

@ -22,9 +22,8 @@ using System.Threading;
namespace Sparkles {
public class SparkleFetcherInfo {
public string Address; // TODO: Uri object
public string RemotePath;
public class FetcherInfo {
public readonly Uri Address;
public string Fingerprint;
@ -33,22 +32,45 @@ namespace Sparkles {
public bool FetchPriorHistory;
public string AnnouncementsUrl; // TODO: Uri object
public FetcherInfo (Uri address)
{
Address = address;
}
}
public enum FetchResult {
Unknown,
BackendMissing,
NoNetwork,
HostNotSupported,
HostChanged,
NotAuthenticated,
UserInterrupted,
Success
}
public class NetworkException : Exception { }
public abstract class BaseFetcher {
public event Action Started = delegate { };
public event Action Failed = delegate { };
public event FailedEventHandler Failed = delegate { };
public delegate void FailedEventHandler (FetchResult result);
public event FinishedEventHandler Finished = delegate { };
public delegate void FinishedEventHandler (StorageType storage_type, string [] warnings);
public delegate void FinishedEventHandler (StorageType storage_type);
public event ProgressChangedEventHandler ProgressChanged = delegate { };
public delegate void ProgressChangedEventHandler (double percentage, double speed, string information);
public abstract bool Fetch ();
public abstract FetchResult Fetch ();
public abstract void Stop ();
public bool IsActive { get; protected set; }
public double ProgressPercentage { get; private set; }
@ -67,27 +89,10 @@ namespace Sparkles {
public string RequiredFingerprint { get; protected set; }
public readonly bool FetchPriorHistory;
public string TargetFolder { get; protected set; }
public SparkleFetcherInfo OriginalFetcherInfo;
public FetcherInfo OriginalFetcherInfo;
protected List<string> warnings = new List<string> ();
protected List<string> errors = new List<string> ();
public string [] Warnings {
get {
return warnings.ToArray ();
}
}
public string [] Errors {
get {
return errors.ToArray ();
}
}
protected BaseFetcher (SparkleFetcherInfo info)
protected BaseFetcher (FetcherInfo info)
{
FetchedRepoStorageType = StorageType.Unknown;
@ -95,10 +100,12 @@ namespace Sparkles {
new StorageTypeInfo (StorageType.Plain, "Plain Storage", "Nothing fancy;\nmaximum compatibility"));
OriginalFetcherInfo = info;
RequiredFingerprint = info.Fingerprint;
FetchPriorHistory = info.FetchPriorHistory;
string remote_path = info.RemotePath.Trim ("/".ToCharArray ());
string address = info.Address;
string remote_path = info.Address.AbsolutePath.Trim ("/".ToCharArray ());
string address = info.Address.ToString ();
if (address.EndsWith ("/", StringComparison.InvariantCulture))
address = address.Substring (0, address.Length - 1);
@ -130,30 +137,29 @@ namespace Sparkles {
Directory.Delete (TargetFolder, recursive: true);
} catch (IOException) {
errors.Add ("\"" + TargetFolder + "\" is read-only.");
Failed ();
Failed (FetchResult.Unknown);
return;
}
thread = new Thread (() => {
if (Fetch ()) {
FetchResult result = Fetch ();
if (result == FetchResult.Success) {
Thread.Sleep (500);
Logger.LogInfo ("Fetcher", "Finished");
IsActive = false;
Finished (FetchedRepoStorageType, Warnings);
Finished (FetchedRepoStorageType);
} else {
Thread.Sleep (500);
if (IsActive) {
Logger.LogInfo ("Fetcher", "Failed");
Failed ();
} else {
Logger.LogInfo ("Fetcher", "Failed: cancelled by user");
}
if (!IsActive)
result = FetchResult.UserInterrupted;
Failed (result);
Logger.LogInfo ("Fetcher", string.Format ("Failed ({0})", result));
IsActive = false;
}

View file

@ -37,14 +37,18 @@ namespace Sparkles {
StartInfo.FileName = path;
StartInfo.Arguments = args;
StartInfo.WorkingDirectory = Path.GetTempPath ();
StartInfo.CreateNoWindow = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.RedirectStandardError = true;
StartInfo.UseShellExecute = false;
StartInfo.RedirectStandardOutput = true;
StartInfo.RedirectStandardError = true;
EnableRaisingEvents = true;
// Force set the language because we do a lot of string parsing
SetEnvironmentVariable ("LANG", "en_US");
}
@ -103,8 +107,7 @@ namespace Sparkles {
return output.TrimEnd ();
}
public void SetEnvironmentVariable (string variable, string content)
public void SetEnvironmentVariable (string variable, string content)
{
if (StartInfo.EnvironmentVariables.ContainsKey (variable))
StartInfo.EnvironmentVariables [variable] = content;

View file

@ -72,7 +72,7 @@ namespace Sparkles {
FilePath = Path.Combine (config_path, config_file_name);
DirectoryPath = config_path;
BinPath = Path.Combine (config_path, "bin");
BinPath = Path.Combine (config_path, "bin");
if (!Directory.Exists (BinPath))
Directory.CreateDirectory (BinPath);
@ -328,7 +328,7 @@ namespace Sparkles {
}
Save ();
Logger.LogInfo ("Config", "Updated option " + name + ":" + content);
Logger.LogInfo ("Config", string.Format ("Set {0} {1}:{2}", FilePath, name, content));
}
@ -352,7 +352,6 @@ namespace Sparkles {
void Save ()
{
Save (FilePath);
Logger.LogInfo ("Config", "Wrote to '" + FilePath + "'");
}
}
}

View file

@ -98,8 +98,6 @@ namespace Sparkles.Git {
SetEnvironmentVariable ("GIT_CONFIG_NOSYSTEM", "1");
SetEnvironmentVariable ("PREFIX", "");
SetEnvironmentVariable ("HOME", "");
SetEnvironmentVariable ("LANG", "en_US");
}

View file

@ -39,7 +39,7 @@ namespace Sparkles.Git {
}
public GitFetcher (SparkleFetcherInfo fetcher_info, SSHAuthenticationInfo auth_info) : base (fetcher_info)
public GitFetcher (FetcherInfo fetcher_info, SSHAuthenticationInfo auth_info) : base (fetcher_info, auth_info)
{
this.auth_info = auth_info;
var uri_builder = new UriBuilder (RemoteUrl);
@ -72,15 +72,17 @@ namespace Sparkles.Git {
}
public override bool Fetch ()
public override FetchResult Fetch ()
{
if (!base.Fetch ())
return false;
FetchResult result = base.Fetch ();
if (result != FetchResult.Success)
return result;
StorageType? storage_type = DetermineStorageType ();
if (storage_type == null)
return false;
return FetchResult.NoNetwork; // TODO: or HostNotSupported
FetchedRepoStorageType = (StorageType) storage_type;
@ -111,6 +113,7 @@ namespace Sparkles.Git {
while (!output_stream.EndOfStream) {
string line = output_stream.ReadLine ();
// TODO: Make ParseProgress use FetchResult
ErrorStatus error = GitCommand.ParseProgress (line, out percentage, out speed, out information);
if (error != ErrorStatus.None) {
@ -118,7 +121,7 @@ namespace Sparkles.Git {
git_clone.Kill ();
git_clone.Dispose ();
return false;
return FetchResult.Unknown; // TODO
}
OnProgressChanged (percentage, speed, information);
@ -127,13 +130,14 @@ namespace Sparkles.Git {
git_clone.WaitForExit ();
if (git_clone.ExitCode != 0)
return false;
return FetchResult.Unknown; // TODO
// Smooth out progression
Thread.Sleep (500);
OnProgressChanged (100, 0, "");
Thread.Sleep (500);
return true;
return FetchResult.Success;
}

View file

@ -25,7 +25,7 @@ namespace Sparkles {
public class SparkleInvite : XmlDocument {
public string Address { get; private set; }
public string Address { get; private set; } // TODO: uri
public string RemotePath { get; private set; }
public string Fingerprint { get; private set; }
public string AcceptUrl { get; private set; }

View file

@ -32,6 +32,8 @@ namespace Sparkles {
new public string Name { get { return GetValue ("info", "name"); } }
public string Description { get { return GetValue ("info", "description"); } }
public string Host { get { return GetValue ("info", "host"); } }
public string KeyEntryHint { get { return GetValue ("key", "hint"); } }
public string Backend { get { return GetValue ("info", "backend"); } }
public string Fingerprint { get { return GetValue ("info", "fingerprint"); } }
public string AnnouncementsUrl { get { return GetValue ("info", "announcements_url"); } }
@ -40,7 +42,7 @@ namespace Sparkles {
public string Path { get { return GetValue ("path", "value"); } }
public string PathExample { get { return GetValue ("path", "example"); } }
public string ImagePath {
public string IconPath {
get {
string image_file_name = GetValue ("info", "icon");
string image_path = IO.Path.Combine (preset_directory, image_file_name);
@ -51,7 +53,19 @@ namespace Sparkles {
return IO.Path.Combine (PresetsPath, image_file_name);
}
}
public string LogoPath {
get {
string image_file_name = GetValue ("info", "logo");
string image_path = IO.Path.Combine (preset_directory, image_file_name);
if (IO.File.Exists (image_path))
return image_path;
return IO.Path.Combine (PresetsPath, image_file_name);
}
}
public bool PathUsesLowerCase {
get {
string uses_lower_case = GetValue ("path", "uses_lower_case");
@ -110,14 +124,14 @@ namespace Sparkles {
}
private string GetValue (string a, string b)
string GetValue (string a, string b)
{
XmlNode node = SelectSingleNode ("/sparkleshare/preset/" + a + "/" + b + "/text()");
XmlNode node = SelectSingleNode ("/sparkleshare/preset/" + a + "/" + b);
if (node != null && !string.IsNullOrEmpty (node.Value))
return node.Value;
else
if (node == null)
return null;
return node.InnerXml;
}
}
}

View file

@ -22,6 +22,11 @@ using IO = System.IO;
namespace Sparkles {
public abstract class AuthenticationInfo
{
}
public class SSHAuthenticationInfo : AuthenticationInfo {
public static SSHAuthenticationInfo DefaultAuthenticationInfo;
@ -82,7 +87,7 @@ namespace Sparkles {
}
bool CreateKeyPair ()
void CreateKeyPair ()
{
string key_file_name = DateTime.Now.ToString ("yyyy-MM-dd_HH\\hmm") + ".key";
string computer_name = Dns.GetHostName ();
@ -108,11 +113,11 @@ namespace Sparkles {
Logger.LogInfo ("Auth", "Created key pair: " + key_file_name);
ImportKeys ();
return true;
return;
}
Logger.LogInfo ("Auth", "Could not create key pair");
return false;
return;
}

View file

@ -20,48 +20,65 @@ using System.IO;
using System.Security.Cryptography;
namespace Sparkles {
public abstract class SSHFetcher : BaseFetcher {
public static string SSHKeyScan = "ssh-keyscan";
SSHAuthenticationInfo auth_info;
protected SSHFetcher (SparkleFetcherInfo info) : base (info)
protected SSHFetcher (FetcherInfo info, SSHAuthenticationInfo auth_info) : base (info)
{
this.auth_info = auth_info;
}
public override bool Fetch ()
public override FetchResult Fetch ()
{
bool host_key_missing = false;
string host_key = FetchHostKey ();
bool host_key_warning = false;
if (string.IsNullOrEmpty (RemoteUrl.Host) || host_key == null) {
Logger.LogInfo ("Auth", "Could not fetch host key");
errors.Add ("error: Could not fetch host key");
if (host_key == null)
host_key_missing = true;
return false;
}
if (RequiredFingerprint != null) {
string host_fingerprint = DeriveFingerprint (host_key);
if (host_fingerprint == null || RequiredFingerprint!= host_fingerprint) {
Logger.LogInfo ("Auth", "Fingerprint doesn't match");
errors.Add ("error: Host fingerprint doesn't match");
return false;
}
Logger.LogInfo ("Auth", "Fingerprint matches");
if (RequiredFingerprint == null) {
Logger.LogInfo ("Auth", "Skipping fingerprint check"); // TODO expose to UI
} else {
Logger.LogInfo ("Auth", "Skipping fingerprint check");
host_key_warning = true;
string host_fingerprint;
try {
host_fingerprint = DeriveFingerprint (host_key);
} catch {
return FetchResult.HostNotSupported;
}
if (RequiredFingerprint != host_fingerprint) {
Logger.LogInfo ("Auth", "Fingerprints don't match");
return FetchResult.HostChanged;
}
Logger.LogInfo ("Auth", "Fingerprints match");
}
AcceptHostKey (host_key, host_key_warning);
return true;
bool authenticated;
try {
authenticated = CanAuthenticateTo (RemoteUrl, this.auth_info);
} catch (NetworkException) {
return FetchResult.NoNetwork;
}
if (host_key_missing)
return FetchResult.HostNotSupported;
if (!authenticated)
return FetchResult.NotAuthenticated;
AcceptHostKey (host_key);
return FetchResult.Success;
}
@ -78,32 +95,12 @@ namespace Sparkles {
if (ssh_keyscan.ExitCode == 0 && !string.IsNullOrWhiteSpace (host_key))
return host_key;
Logger.LogInfo ("Auth", "Could not fetch host key");
return null;
}
string DeriveFingerprint (string public_key)
{
try {
SHA256 sha256 = new SHA256CryptoServiceProvider ();
string key = public_key.Split (" ".ToCharArray ()) [2];
byte [] base64_bytes = Convert.FromBase64String (key);
byte [] sha256_bytes = sha256.ComputeHash (base64_bytes);
string fingerprint = BitConverter.ToString (sha256_bytes);
fingerprint = fingerprint.ToLower ().Replace ("-", ":");
return fingerprint;
} catch (Exception e) {
Logger.LogInfo ("Fetcher", "Failed to create fingerprint: ", e);
return null;
}
}
void AcceptHostKey (string host_key, bool warn)
void AcceptHostKey (string host_key)
{
string ssh_config_path = Path.Combine (Configuration.DefaultConfiguration.DirectoryPath, "ssh");
string known_hosts_file_path = Path.Combine (ssh_config_path, "known_hosts");
@ -128,11 +125,57 @@ namespace Sparkles {
File.AppendAllText (known_hosts_file_path, host_key + "\n");
else
File.AppendAllText (known_hosts_file_path, "\n" + host_key + "\n");
}
Logger.LogInfo ("Auth", "Accepted host key for " + host);
if (warn)
warnings.Add ("The following host key has been accepted:\n" + DeriveFingerprint (host_key));
string DeriveFingerprint (string public_key)
{
try {
SHA256 sha256 = new SHA256CryptoServiceProvider ();
string key = public_key.Split (" ".ToCharArray ()) [2];
byte [] base64_bytes = Convert.FromBase64String (key);
byte [] sha256_bytes = sha256.ComputeHash (base64_bytes);
string fingerprint = BitConverter.ToString (sha256_bytes);
fingerprint = fingerprint.ToLower ().Replace ("-", ":");
return fingerprint;
} catch (Exception e) {
throw new FormatException ("Could not derive fingerprint from " + public_key, e);
}
}
public static bool CanAuthenticateTo (Uri host, SSHAuthenticationInfo auth_info)
{
if (host == null || auth_info == null)
throw new ArgumentNullException ();
const string AUTH_CHECK = "Authentication succeeded";
const string OFFLINE_CHECK = "Could not resolve hostname";
string server = host.Authority;
if (!string.IsNullOrEmpty (host.UserInfo))
server = host.UserInfo + "@" + host.Authority;
string [] args = new string [] {
"-T", // Non-interactive mode
"-v", // Verbose output to StandardError
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-i", auth_info.PrivateKeyFilePath,
server };
var ssh = new Command ("ssh", string.Join (" ", args));
string output = ssh.StartAndReadStandardError ();
if (output.Contains (OFFLINE_CHECK))
throw new NetworkException ();
return output.Contains (AUTH_CHECK);
}
}
}

View file

@ -73,14 +73,10 @@
<Compile Include="ListenerFactory.cs" />
<Compile Include="Logger.cs" />
<Compile Include="InstallationInfo.Directory.cs" />
<Compile Include="Command.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="SSHCommand.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="SSHFetcher.cs" />
<Compile Include="SSHAuthenticationInfo.cs" />
<Compile Include="Command.cs" />
<Compile Include="SSH\SSHCommand.cs" />
<Compile Include="SSH\SSHFetcher.cs" />
<Compile Include="SSH\SSHAuthenticationInfo.cs" />
<Compile Include="TcpListener.cs" />
<Compile Include="User.cs" />
<Compile Include="InstallationInfo.cs" />

View file

@ -3,8 +3,7 @@ directory_info_file = configure_file(
output: 'InstallationInfo.Directory.cs',
configuration: configuration)
sparkles_src = ['AuthenticationInfo.cs',
'BaseFetcher.cs',
sparkles_src = ['BaseFetcher.cs',
'BaseListener.cs',
'BaseRepository.cs',
'ChangeSet.cs',
@ -17,9 +16,9 @@ sparkles_src = ['AuthenticationInfo.cs',
'ListenerFactory.cs',
'Logger.cs',
'Preset.cs',
'SSHAuthenticationInfo.cs',
'SSHCommand.cs',
'SSHFetcher.cs',
'SSH/SSH.AuthenticationInfo.cs',
'SSH/SSH.Command.cs',
'SSH/SSH.Fetcher.cs',
'TcpListener.cs',
'User.cs',
'Watcher.cs']
@ -30,4 +29,3 @@ sparkles = library('Sparkles',
cs_args: '-r:System.Xml.Linq',
install: true,
install_dir: install_dir)

View file

@ -1,3 +1,2 @@
option('ubuntu', type: 'boolean', value: false)
option('nightly', type: 'boolean', value: false)