Merge branch 'invites'

This commit is contained in:
Hylke Bons 2012-02-17 00:52:24 +01:00
commit 419ad68a8d
10 changed files with 435 additions and 406 deletions

View file

@ -42,6 +42,7 @@ namespace SparkleLib {
public string RemoteUrl; public string RemoteUrl;
public string [] ExcludeRules; public string [] ExcludeRules;
public string [] Warnings; public string [] Warnings;
public bool IsActive { get; private set; }
private Thread thread; private Thread thread;
@ -50,6 +51,7 @@ namespace SparkleLib {
{ {
TargetFolder = target_folder; TargetFolder = target_folder;
RemoteUrl = server + "/" + remote_folder; RemoteUrl = server + "/" + remote_folder;
IsActive = false;
ExcludeRules = new string [] { ExcludeRules = new string [] {
// gedit and emacs // gedit and emacs
@ -125,6 +127,7 @@ namespace SparkleLib {
// Clones the remote repository // Clones the remote repository
public void Start () public void Start ()
{ {
IsActive = true;
SparkleHelpers.DebugInfo ("Fetcher", "[" + TargetFolder + "] Fetching folder: " + RemoteUrl); SparkleHelpers.DebugInfo ("Fetcher", "[" + TargetFolder + "] Fetching folder: " + RemoteUrl);
if (Started != null) if (Started != null)
@ -149,6 +152,7 @@ namespace SparkleLib {
SparkleHelpers.DebugInfo ("Fetcher", "Finished"); SparkleHelpers.DebugInfo ("Fetcher", "Finished");
EnableHostKeyCheckingForHost (host); EnableHostKeyCheckingForHost (host);
IsActive = false;
if (Finished != null) if (Finished != null)
Finished (Warnings); Finished (Warnings);
@ -157,6 +161,7 @@ namespace SparkleLib {
SparkleHelpers.DebugInfo ("Fetcher", "Failed"); SparkleHelpers.DebugInfo ("Fetcher", "Failed");
EnableHostKeyCheckingForHost (host); EnableHostKeyCheckingForHost (host);
IsActive = false;
if (Failed != null) if (Failed != null)
Failed (); Failed ();

View file

@ -33,7 +33,7 @@ namespace SparkleShare {
public SparkleSetupController Controller = new SparkleSetupController (); public SparkleSetupController Controller = new SparkleSetupController ();
private NSButton ContinueButton; private NSButton ContinueButton;
private NSButton SyncButton; private NSButton AddButton;
private NSButton TryAgainButton; private NSButton TryAgainButton;
private NSButton CancelButton; private NSButton CancelButton;
private NSButton SkipTutorialButton; private NSButton SkipTutorialButton;
@ -65,6 +65,18 @@ namespace SparkleShare {
public SparkleSetup () : base () public SparkleSetup () : base ()
{ {
Controller.HideWindowEvent += delegate {
InvokeOnMainThread (delegate {
PerformClose (this);
});
};
Controller.ShowWindowEvent += delegate {
InvokeOnMainThread (delegate {
OrderFrontRegardless ();
});
};
Controller.ChangePageEvent += delegate (PageType type, string [] warnings) { Controller.ChangePageEvent += delegate (PageType type, string [] warnings) {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
Reset (); Reset ();
@ -72,6 +84,7 @@ namespace SparkleShare {
switch (type) { switch (type) {
case PageType.Setup: { case PageType.Setup: {
// TODO: Improve text
Header = "Welcome to SparkleShare!"; Header = "Welcome to SparkleShare!";
Description = "We'll need some info to mark your changes in the event log. " + Description = "We'll need some info to mark your changes in the event log. " +
"Don't worry, this stays between you and your peers."; "Don't worry, this stays between you and your peers.";
@ -159,10 +172,85 @@ namespace SparkleShare {
break; break;
} }
case PageType.Invite: {
Header = "You've received an invite!";
Description = "Do you want to add this project to SparkleShare?";
AddressLabel = new NSTextField () {
Alignment = NSTextAlignment.Right,
BackgroundColor = NSColor.WindowBackground,
Bordered = false,
Editable = false,
Frame = new RectangleF (165, Frame.Height - 240, 160, 17),
StringValue = "Address:",
Font = SparkleUI.Font
};
PathLabel = new NSTextField () {
Alignment = NSTextAlignment.Right,
BackgroundColor = NSColor.WindowBackground,
Bordered = false,
Editable = false,
Frame = new RectangleF (165, Frame.Height - 264, 160, 17),
StringValue = "Remote Path:",
Font = SparkleUI.Font
};
AddressTextField = new NSTextField () {
Alignment = NSTextAlignment.Left,
BackgroundColor = NSColor.WindowBackground,
Bordered = false,
Editable = false,
Frame = new RectangleF (330, Frame.Height - 240, 260, 17),
StringValue = Controller.PendingInvite.Address,
Font = SparkleUI.BoldFont
};
PathTextField = new NSTextField () {
Alignment = NSTextAlignment.Left,
BackgroundColor = NSColor.WindowBackground,
Bordered = false,
Editable = false,
Frame = new RectangleF (330, Frame.Height - 264, 260, 17),
StringValue = Controller.PendingInvite.RemotePath,
Font = SparkleUI.BoldFont
};
ContentView.AddSubview (AddressLabel);
ContentView.AddSubview (PathLabel);
ContentView.AddSubview (AddressTextField);
ContentView.AddSubview (PathTextField);
CancelButton = new NSButton () {
Title = "Cancel"
};
CancelButton.Activated += delegate {
Controller.PageCancelled ();
};
AddButton = new NSButton () {
Title = "Add"
};
AddButton.Activated += delegate {
Controller.InvitePageCompleted ();
};
Buttons.Add (AddButton);
Buttons.Add (CancelButton);
break;
}
case PageType.Add: { case PageType.Add: {
Header = "Where's your project hosted?"; Header = "Where's your project hosted?";
Description = ""; Description = "";
AddressLabel = new NSTextField () { AddressLabel = new NSTextField () {
Alignment = NSTextAlignment.Left, Alignment = NSTextAlignment.Left,
@ -171,7 +259,7 @@ namespace SparkleShare {
Editable = false, Editable = false,
Frame = new RectangleF (190, Frame.Height - 308, 160, 17), Frame = new RectangleF (190, Frame.Height - 308, 160, 17),
StringValue = "Address:", StringValue = "Address:",
Font = SparkleUI.Font Font = SparkleUI.BoldFont
}; };
AddressTextField = new NSTextField () { AddressTextField = new NSTextField () {
@ -190,7 +278,7 @@ namespace SparkleShare {
Editable = false, Editable = false,
Frame = new RectangleF (190 + 196 + 16, Frame.Height - 308, 160, 17), Frame = new RectangleF (190 + 196 + 16, Frame.Height - 308, 160, 17),
StringValue = "Remote Path:", StringValue = "Remote Path:",
Font = SparkleUI.Font Font = SparkleUI.BoldFont
}; };
PathTextField = new NSTextField () { PathTextField = new NSTextField () {
@ -325,7 +413,7 @@ namespace SparkleShare {
Controller.UpdateAddProjectButtonEvent += delegate (bool button_enabled) { Controller.UpdateAddProjectButtonEvent += delegate (bool button_enabled) {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
SyncButton.Enabled = button_enabled; AddButton.Enabled = button_enabled;
}); });
}; };
@ -338,28 +426,26 @@ namespace SparkleShare {
ContentView.AddSubview (PathTextField); ContentView.AddSubview (PathTextField);
ContentView.AddSubview (PathHelpLabel); ContentView.AddSubview (PathHelpLabel);
SyncButton = new NSButton () { AddButton = new NSButton () {
Title = "Add", Title = "Add",
Enabled = false Enabled = false
}; };
SyncButton.Activated += delegate { AddButton.Activated += delegate {
Controller.AddPageCompleted ( Controller.AddPageCompleted (
AddressTextField.StringValue, AddressTextField.StringValue,
PathTextField.StringValue PathTextField.StringValue
); );
}; };
Buttons.Add (SyncButton); Buttons.Add (AddButton);
CancelButton = new NSButton () { CancelButton = new NSButton () {
Title = "Cancel" Title = "Cancel"
}; };
CancelButton.Activated += delegate { CancelButton.Activated += delegate {
InvokeOnMainThread (delegate { Controller.PageCancelled ();
PerformClose (this);
});
}; };
Buttons.Add (CancelButton); Buttons.Add (CancelButton);
@ -505,10 +591,7 @@ namespace SparkleShare {
}; };
FinishButton.Activated += delegate { FinishButton.Activated += delegate {
InvokeOnMainThread (delegate { Controller.FinishPageCompleted ();
Controller.FinishedPageCompleted ();
PerformClose (this);
});
}; };
OpenFolderButton = new NSButton () { OpenFolderButton = new NSButton () {
@ -655,9 +738,7 @@ namespace SparkleShare {
}; };
FinishButton.Activated += delegate { FinishButton.Activated += delegate {
InvokeOnMainThread (delegate { Controller.TutorialPageCompleted ();
PerformClose (this);
});
}; };
string slide_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath, string slide_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath,

View file

@ -83,11 +83,6 @@ namespace SparkleShare {
Font = SparkleUI.Font Font = SparkleUI.Font
}; };
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
MakeKeyAndOrderFront (this);
OrderFrontRegardless ();
if (Program.UI != null) if (Program.UI != null)
Program.UI.UpdateDockIconVisibility (); Program.UI.UpdateDockIconVisibility ();
} }
@ -140,6 +135,9 @@ namespace SparkleShare {
public override void OrderFrontRegardless () public override void OrderFrontRegardless ()
{ {
NSApplication.SharedApplication.AddWindowsItem (this, "SparkleShare Setup", false); NSApplication.SharedApplication.AddWindowsItem (this, "SparkleShare Setup", false);
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
MakeKeyAndOrderFront (this);
base.OrderFrontRegardless (); base.OrderFrontRegardless ();
} }

View file

@ -230,26 +230,13 @@ namespace SparkleShare {
Menu.AddItem (NSMenuItem.SeparatorItem); Menu.AddItem (NSMenuItem.SeparatorItem);
SyncMenuItem = new NSMenuItem () { SyncMenuItem = new NSMenuItem () {
Title = "Add Hosted Project…", Title = "Add Hosted Project…",
Enabled = true Enabled = true
}; };
if (!Program.Controller.FirstRun) { if (!Program.Controller.FirstRun) {
SyncMenuItem.Activated += delegate { SyncMenuItem.Activated += delegate {
InvokeOnMainThread (delegate { Controller.AddHostedProjectClicked ();
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
if (SparkleUI.Setup == null) {
SparkleUI.Setup = new SparkleSetup ();
SparkleUI.Setup.Controller.ShowAddPage ();
}
if (!SparkleUI.Setup.IsVisible)
SparkleUI.Setup.Controller.ShowAddPage ();
SparkleUI.Setup.OrderFrontRegardless ();
SparkleUI.Setup.MakeKeyAndOrderFront (this);
});
}; };
} }
@ -262,7 +249,9 @@ namespace SparkleShare {
}; };
if (Controller.Folders.Length > 0) { if (Controller.Folders.Length > 0) {
// TODO: move this logic
RecentEventsMenuItem.Activated += delegate { RecentEventsMenuItem.Activated += delegate {
// Controller.OpenRecentEventsClicked ()
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
@ -304,8 +293,10 @@ namespace SparkleShare {
Title = "About SparkleShare", Title = "About SparkleShare",
Enabled = true Enabled = true
}; };
// TODO: move this logic
AboutMenuItem.Activated += delegate { AboutMenuItem.Activated += delegate {
// Controller.AboutClicked ();
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);

View file

@ -65,11 +65,11 @@ namespace SparkleShare {
StatusIcon = new SparkleStatusIcon (); StatusIcon = new SparkleStatusIcon ();
Bubbles = new SparkleBubbles (); Bubbles = new SparkleBubbles ();
Setup = new SparkleSetup ();
// About = new SparkleAbout ();
if (Program.Controller.FirstRun) { if (Program.Controller.FirstRun) {
Setup = new SparkleSetup (); Program.Controller.ShowSetupWindow (PageType.Setup);
Setup.Controller.ShowSetupPage ();
UpdateDockIconVisibility (); UpdateDockIconVisibility ();
} }
} }

View file

@ -28,10 +28,8 @@ namespace SparkleShare {
public SparkleBubblesController () public SparkleBubblesController ()
{ {
Program.Controller.ConflictNotificationRaised += delegate { Program.Controller.AlertNotificationRaised += delegate (string title, string message) {
ShowBubble ("Conflict detected.", ShowBubble (title, message, null);
"Don't worry, SparkleShare made a copy of each conflicting file.",
null);
}; };
Program.Controller.NotificationRaised += delegate (SparkleChangeSet change_set) { Program.Controller.NotificationRaised += delegate (SparkleChangeSet change_set) {

View file

@ -40,6 +40,17 @@ namespace SparkleShare {
public double ProgressPercentage = 0.0; public double ProgressPercentage = 0.0;
public string ProgressSpeed = ""; public string ProgressSpeed = "";
public event ShowSetupWindowEventHandler ShowSetupWindowEvent;
public delegate void ShowSetupWindowEventHandler (PageType page_type);
public event ShowAboutWindowEventHandler ShowAboutWindowEvent;
public delegate void ShowAboutWindowEventHandler ();
public event ShowEventsWindowEventHandler ShowEventsWindowEvent;
public delegate void ShowEventsWindowEventHandler ();
public event FolderFetchedEventHandler FolderFetched; public event FolderFetchedEventHandler FolderFetched;
public delegate void FolderFetchedEventHandler (string [] warnings); public delegate void FolderFetchedEventHandler (string [] warnings);
@ -64,15 +75,15 @@ namespace SparkleShare {
public event OnErrorHandler OnError; public event OnErrorHandler OnError;
public delegate void OnErrorHandler (); public delegate void OnErrorHandler ();
public event OnInviteHandler OnInvite; public event InviteReceivedHandler InviteReceived;
public delegate void OnInviteHandler (SparkleInvite invite); public delegate void InviteReceivedHandler (SparkleInvite invite);
public event ConflictNotificationRaisedHandler ConflictNotificationRaised;
public delegate void ConflictNotificationRaisedHandler ();
public event NotificationRaisedEventHandler NotificationRaised; public event NotificationRaisedEventHandler NotificationRaised;
public delegate void NotificationRaisedEventHandler (SparkleChangeSet change_set); public delegate void NotificationRaisedEventHandler (SparkleChangeSet change_set);
public event AlertNotificationRaisedEventHandler AlertNotificationRaised;
public delegate void AlertNotificationRaisedEventHandler (string title, string message);
public event NoteNotificationRaisedEventHandler NoteNotificationRaised; public event NoteNotificationRaisedEventHandler NoteNotificationRaised;
public delegate void NoteNotificationRaisedEventHandler (SparkleUser user, string folder_name); public delegate void NoteNotificationRaisedEventHandler (SparkleUser user, string folder_name);
@ -126,16 +137,27 @@ namespace SparkleShare {
FolderListChanged (); FolderListChanged ();
}; };
watcher.Created += delegate (object o, FileSystemEventArgs args) {
if (!args.FullPath.EndsWith (".xml"))
return;
SparkleInviteListener invite_listener = new SparkleInviteListener (1987); if (this.fetcher != null &&
this.fetcher.IsActive) {
invite_listener.InviteReceived += delegate (SparkleInvite invite) { if (AlertNotificationRaised != null)
if (OnInvite != null && !FirstRun) AlertNotificationRaised ("SparkleShare Setup is busy",
OnInvite (invite); "Please try again later");
} else {
if (InviteReceived != null) {
SparkleInvite invite = new SparkleInvite (args.FullPath);
if (invite.Valid)
InviteReceived (invite);
}
}
}; };
invite_listener.Start ();
new Thread (new ThreadStart (PopulateRepositories)).Start (); new Thread (new ThreadStart (PopulateRepositories)).Start ();
} }
@ -147,43 +169,11 @@ namespace SparkleShare {
} }
// Uploads the user's public key to the server
public bool AcceptInvitation (string server, string folder, string token)
{
// The location of the user's public key for SparkleShare
string public_key_file_path = SparkleHelpers.CombineMore (SparkleConfig.DefaultConfig.HomePath,
".ssh", "sparkleshare." + UserEmail + ".key.pub");
if (!File.Exists (public_key_file_path))
return false;
StreamReader reader = new StreamReader (public_key_file_path);
string public_key = reader.ReadToEnd ();
reader.Close ();
string url = "https://" + server + "/?folder=" + folder +
"&token=" + token + "&pubkey=" + public_key;
SparkleHelpers.DebugInfo ("WebRequest", url);
HttpWebRequest request = (HttpWebRequest) WebRequest.Create (url);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK) {
response.Close ();
return true;
} else {
response.Close ();
return false;
}
}
public List<string> Folders { public List<string> Folders {
get { get {
List<string> folders = SparkleConfig.DefaultConfig.Folders; List<string> folders = SparkleConfig.DefaultConfig.Folders;
folders.Sort (); folders.Sort ();
return folders; return folders;
} }
} }
@ -194,6 +184,7 @@ namespace SparkleShare {
List<string> hosts = SparkleConfig.DefaultConfig.HostsWithUsername; List<string> hosts = SparkleConfig.DefaultConfig.HostsWithUsername;
hosts.AddRange(SparkleConfig.DefaultConfig.Hosts); hosts.AddRange(SparkleConfig.DefaultConfig.Hosts);
hosts.Sort (); hosts.Sort ();
return hosts; return hosts;
} }
} }
@ -213,6 +204,28 @@ namespace SparkleShare {
} }
public void ShowSetupWindow (PageType page_type)
{
if (ShowSetupWindowEvent != null)
ShowSetupWindowEvent (page_type);
}
public void ShowAboutWindow ()
{
if (ShowAboutWindowEvent != null)
ShowAboutWindowEvent ();
}
public void ShowEventsWindow ()
{
if (ShowEventsWindowEvent != null)
ShowEventsWindowEvent ();
}
public List<SparkleChangeSet> GetLog () public List<SparkleChangeSet> GetLog ()
{ {
List<SparkleChangeSet> list = new List<SparkleChangeSet> (); List<SparkleChangeSet> list = new List<SparkleChangeSet> ();
@ -584,7 +597,6 @@ namespace SparkleShare {
repo.NewChangeSet += delegate (SparkleChangeSet change_set) { repo.NewChangeSet += delegate (SparkleChangeSet change_set) {
if (NotificationRaised != null) if (NotificationRaised != null)
NotificationRaised (change_set); NotificationRaised (change_set);
}; };
@ -596,8 +608,9 @@ namespace SparkleShare {
}; };
repo.ConflictResolved += delegate { repo.ConflictResolved += delegate {
if (ConflictNotificationRaised != null) if (AlertNotificationRaised != null)
ConflictNotificationRaised (); AlertNotificationRaised ("Conflict detected.",
"Don't worry, SparkleShare made a copy of each conflicting file.");
}; };
repo.SyncStatusChanged += delegate (SyncStatus status) { repo.SyncStatusChanged += delegate (SyncStatus status) {
@ -851,7 +864,6 @@ namespace SparkleShare {
} }
foreach (string raw_email in emails) { foreach (string raw_email in emails) {
// Gravatar wants lowercase emails // Gravatar wants lowercase emails
string email = raw_email.ToLower (); string email = raw_email.ToLower ();
string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email); string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email);
@ -866,9 +878,6 @@ namespace SparkleShare {
old_avatars.Add (email); old_avatars.Add (email);
} catch (FileNotFoundException) { } catch (FileNotFoundException) {
// FIXME: For some reason the previous File.Exists () check
// doesn't cover all cases sometimes, so we catch any errors
if (old_avatars.Contains (email)) if (old_avatars.Contains (email))
old_avatars.Remove (email); old_avatars.Remove (email);
} }
@ -876,11 +885,11 @@ namespace SparkleShare {
} else if (this.failed_avatars.Contains (email)) { } else if (this.failed_avatars.Contains (email)) {
break; break;
} else { } else {
WebClient client = new WebClient (); WebClient client = new WebClient ();
string url = "http://gravatar.com/avatar/" + GetMD5 (email) + string url = "http://gravatar.com/avatar/" + GetMD5 (email) +
".jpg?s=" + size + "&d=404"; ".jpg?s=" + size + "&d=404";
try { try {
// Fetch the avatar // Fetch the avatar
byte [] buffer = client.DownloadData (url); byte [] buffer = client.DownloadData (url);
@ -900,11 +909,10 @@ namespace SparkleShare {
SparkleHelpers.DebugInfo ("Avatar", "Failed fetching gravatar for " + email); SparkleHelpers.DebugInfo ("Avatar", "Failed fetching gravatar for " + email);
// Stop downloading further avatars if we have no internet access // Stop downloading further avatars if we have no internet access
if (e.Status == WebExceptionStatus.Timeout){ if (e.Status == WebExceptionStatus.Timeout)
break; break;
} else { else
this.failed_avatars.Add (email); this.failed_avatars.Add (email);
}
} }
} }
} }

View file

@ -16,12 +16,8 @@
using System; using System;
using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Xml; using System.Xml;
using System.Threading;
using SparkleLib; using SparkleLib;
@ -29,32 +25,22 @@ namespace SparkleShare {
public class SparkleInvite { public class SparkleInvite {
public readonly Uri FullAddress; public string Address { get; private set; }
public readonly string Token; public string RemotePath { get; private set; }
public Uri AcceptUrl { get; private set; }
public string Host { public bool Valid {
get { get {
return FullAddress.Host; return (!string.IsNullOrEmpty (Address) &&
} !string.IsNullOrEmpty (RemotePath) &&
} !string.IsNullOrEmpty (AcceptUrl.ToString ()));
public string Path {
get {
return FullAddress.AbsolutePath;
} }
} }
public SparkleInvite (string host, string path, string token) public SparkleInvite (string address, string remote_path, string accept_url)
{ {
if (path.StartsWith ("/")) Initialize (address, remote_path, accept_url);
path = path.Substring (1);
if (!host.EndsWith ("/"))
host = host + "/";
FullAddress = new Uri ("ssh://" + host + path);
Token = token;
} }
@ -63,166 +49,62 @@ namespace SparkleShare {
XmlDocument xml_document = new XmlDocument (); XmlDocument xml_document = new XmlDocument ();
XmlNode node; XmlNode node;
string host = "", path = "", token = ""; string address = "";
string remote_path = "";
string accept_url = "";
try { try {
xml_document.Load (xml_file_path); xml_document.Load (xml_file_path);
node = xml_document.SelectSingleNode ("/sparkleshare/invite/host/text()"); node = xml_document.SelectSingleNode ("/sparkleshare/invite/address/text()");
if (node != null) { host = node.Value; } if (node != null) { address = node.Value; }
node = xml_document.SelectSingleNode ("/sparkleshare/invite/path/text()"); node = xml_document.SelectSingleNode ("/sparkleshare/invite/remote_path/text()");
if (node != null) { path = node.Value; } if (node != null) { remote_path = node.Value; }
node = xml_document.SelectSingleNode ("/sparkleshare/invite/token/text()"); node = xml_document.SelectSingleNode ("/sparkleshare/invite/accept_url/text()");
if (node != null) { token = node.Value; } if (node != null) { accept_url = node.Value; }
Initialize (address, remote_path, accept_url);
} catch (XmlException e) { } catch (XmlException e) {
SparkleHelpers.DebugInfo ("Invite", "Invalid XML: " + e.Message); SparkleHelpers.DebugInfo ("Invite", "Invalid XML: " + e.Message);
return; return;
} }
if (path.StartsWith ("/"))
path = path.Substring (1);
if (!host.EndsWith ("/"))
host = host + "/";
FullAddress = new Uri ("ssh://" + host + path);
Token = token;
}
}
public class SparkleInviteListener {
public event InviteReceivedHandler InviteReceived;
public delegate void InviteReceivedHandler (SparkleInvite invite);
private Thread thread;
private TcpListener tcp_listener;
public SparkleInviteListener (int port)
{
this.tcp_listener = new TcpListener (IPAddress.Loopback, port);
this.thread = new Thread(new ThreadStart (Listen));
} }
public void Start () public bool Accept ()
{ {
this.thread.Start (); WebClient web_client = new WebClient ();
}
try {
web_client.DownloadData (AcceptUrl);
SparkleHelpers.DebugInfo ("Invite", "Uploaded public key");
private void Listen () return true;
{
this.tcp_listener.Start ();
while (true) } catch (WebException e) {
{ SparkleHelpers.DebugInfo ("Invite",
// Blocks until a client connects "Failed uploading public key: " + e.Message);
TcpClient client = this.tcp_listener.AcceptTcpClient ();
// Create a thread to handle communications return false;
Thread client_thread = new Thread (HandleClient);
client_thread.Start (client);
} }
} }
private void HandleClient (object client) private void Initialize (string address, string remote_path, string accept_url)
{ {/*
TcpClient tcp_client = (TcpClient) client; if (!remote_path.StartsWith ("/"))
NetworkStream client_stream = tcp_client.GetStream (); remote_path = "/" + remote_path;
byte [] message = new byte [4096]; if (!address.EndsWith ("/"))
int bytes_read; address = address + "/";
*/
while (true) Address = address;
{ RemotePath = remote_path;
bytes_read = 0; AcceptUrl = new Uri (accept_url);
try {
// Blocks until the client sends a message
bytes_read = client_stream.Read (message, 0, 4096);
} catch {
Console.WriteLine ("Socket error...");
}
// The client has disconnected
if (bytes_read == 0)
break;
ASCIIEncoding encoding = new ASCIIEncoding ();
string received_message = encoding.GetString (message, 0, bytes_read);
string invite_xml = "";
if (received_message.StartsWith (Uri.UriSchemeHttp) ||
received_message.StartsWith (Uri.UriSchemeHttps)) {
WebClient web_client = new WebClient ();
try {
// Fetch the invite file
byte [] buffer = web_client.DownloadData (received_message);
SparkleHelpers.DebugInfo ("Invite", "Received: " + received_message);
invite_xml = ASCIIEncoding.ASCII.GetString (buffer);
} catch (WebException e) {
SparkleHelpers.DebugInfo ("Invite", "Failed downloading: " +
received_message + " " + e.Message);
continue;
}
} else if (received_message.StartsWith (Uri.UriSchemeFile)) {
try {
received_message = received_message.Replace (Uri.UriSchemeFile + "://", "");
invite_xml = File.ReadAllText (received_message);
} catch {
SparkleHelpers.DebugInfo ("Invite", "Failed opening: " + received_message);
continue;
}
} else {
SparkleHelpers.DebugInfo ("Invite",
"Path to invite must use either the file:// or http(s):// scheme");
continue;
}
XmlDocument xml_document = new XmlDocument ();
XmlNode node;
string host = "", path = "", token = "";
try {
xml_document.LoadXml (invite_xml);
node = xml_document.SelectSingleNode ("/sparkleshare/invite/host/text()");
if (node != null) { host = node.Value; }
node = xml_document.SelectSingleNode ("/sparkleshare/invite/path/text()");
if (node != null) { path = node.Value; }
node = xml_document.SelectSingleNode ("/sparkleshare/invite/token/text()");
if (node != null) { token = node.Value; }
} catch (XmlException e) {
SparkleHelpers.DebugInfo ("Invite", "Invalid XML: " + received_message + " " + e.Message);
return;
}
if (InviteReceived != null)
InviteReceived (new SparkleInvite (host, path, token));
}
tcp_client.Close ();
} }
} }
} }

View file

@ -19,6 +19,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using SparkleLib; using SparkleLib;
@ -27,6 +28,7 @@ namespace SparkleShare {
public enum PageType { public enum PageType {
Setup, Setup,
Add, Add,
Invite,
Syncing, Syncing,
Error, Error,
Finished, Finished,
@ -41,6 +43,12 @@ namespace SparkleShare {
public class SparkleSetupController { public class SparkleSetupController {
public event ShowWindowEventHandler ShowWindowEvent;
public delegate void ShowWindowEventHandler ();
public event HideWindowEventHandler HideWindowEvent;
public delegate void HideWindowEventHandler ();
public event ChangePageEventHandler ChangePageEvent; public event ChangePageEventHandler ChangePageEvent;
public delegate void ChangePageEventHandler (PageType page, string [] warnings); public delegate void ChangePageEventHandler (PageType page, string [] warnings);
@ -58,12 +66,17 @@ namespace SparkleShare {
string example_text, FieldState state); string example_text, FieldState state);
public event ChangePathFieldEventHandler ChangePathFieldEvent; public event ChangePathFieldEventHandler ChangePathFieldEvent;
public delegate void ChangePathFieldEventHandler (string text, public delegate void ChangePathFieldEventHandler (string text, string example_text, FieldState state);
string example_text, FieldState state);
public readonly List<SparklePlugin> Plugins = new List<SparklePlugin> (); public readonly List<SparklePlugin> Plugins = new List<SparklePlugin> ();
public SparklePlugin SelectedPlugin; public SparklePlugin SelectedPlugin;
public int TutorialPageNumber { 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 int SelectedPluginIndex { public int SelectedPluginIndex {
get { get {
@ -71,42 +84,6 @@ namespace SparkleShare {
} }
} }
public int TutorialPageNumber {
get {
return this.tutorial_page_number;
}
}
public string PreviousUrl {
get {
return this.previous_url;
}
}
public string PreviousAddress {
get {
return this.previous_address;
}
}
public string PreviousPath {
get {
return this.previous_path;
}
}
public string SyncingFolder {
get {
return this.syncing_folder;
}
}
public PageType PreviousPage {
get {
return this.previous_page;
}
}
public string GuessedUserName { public string GuessedUserName {
get { get {
return Program.Controller.UserName; return Program.Controller.UserName;
@ -123,16 +100,14 @@ namespace SparkleShare {
} }
private string previous_address = "";
private string previous_path = "";
private string previous_url = "";
private string syncing_folder = "";
private int tutorial_page_number = 1;
private PageType previous_page;
public SparkleSetupController () public SparkleSetupController ()
{ {
TutorialPageNumber = 1;
PreviousAddress = "";
PreviousPath = "";
PreviousUrl = "";
SyncingFolder = "";
string local_plugins_path = SparkleHelpers.CombineMore ( string local_plugins_path = SparkleHelpers.CombineMore (
Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
"sparkleshare", "plugins"); "sparkleshare", "plugins");
@ -152,16 +127,37 @@ namespace SparkleShare {
SelectedPlugin = Plugins [0]; SelectedPlugin = Plugins [0];
ChangePageEvent += delegate (PageType page, string [] warning) {
this.previous_page = page; Program.Controller.InviteReceived += delegate (SparkleInvite invite) {
PendingInvite = invite;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Invite, null);
if (ShowWindowEvent != null)
ShowWindowEvent ();
};
Program.Controller.ShowSetupWindowEvent += delegate (PageType page_type) {
if (ChangePageEvent != null)
ChangePageEvent (page_type, null);
if (ShowWindowEvent != null)
ShowWindowEvent ();
if (page_type == PageType.Add)
SelectedPluginChanged (SelectedPluginIndex);
}; };
} }
public void ShowSetupPage () public void PageCancelled ()
{ {
if (ChangePageEvent != null) // PendingInvite = null;
ChangePageEvent (PageType.Setup, null);
if (HideWindowEvent != null)
HideWindowEvent ();
} }
@ -192,112 +188,27 @@ namespace SparkleShare {
} }
public void TutorialPageCompleted ()
{
this.tutorial_page_number++;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Tutorial, null);
}
public void TutorialSkipped () public void TutorialSkipped ()
{ {
this.tutorial_page_number = 4; TutorialPageNumber = 4;
if (ChangePageEvent != null) if (ChangePageEvent != null)
ChangePageEvent (PageType.Tutorial, null); ChangePageEvent (PageType.Tutorial, null);
} }
public void ShowAddPage () public void TutorialPageCompleted ()
{ {
if (ChangePageEvent != null) TutorialPageNumber++;
ChangePageEvent (PageType.Add, null);
SelectedPluginChanged (SelectedPluginIndex); if (TutorialPageNumber == 4) {
} if (HideWindowEvent != null)
HideWindowEvent ();
} else {
public void CheckAddPage (string address, string remote_path, int selected_plugin)
{
if (SelectedPluginIndex != selected_plugin)
SelectedPluginChanged (selected_plugin);
address = address.Trim ();
remote_path = remote_path.Trim ();
bool fields_valid = address != null && address.Trim().Length > 0 &&
remote_path != null && remote_path.Trim().Length > 0;
if (UpdateAddProjectButtonEvent != null)
UpdateAddProjectButtonEvent (fields_valid);
}
public void AddPageCompleted (string address, string path)
{
this.syncing_folder = Path.GetFileNameWithoutExtension (path);
this.previous_address = address;
this.previous_path = path;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Syncing, null);
// TODO: Remove events afterwards
Program.Controller.FolderFetched += delegate (string [] warnings) {
if (ChangePageEvent != null) if (ChangePageEvent != null)
ChangePageEvent (PageType.Finished, warnings); ChangePageEvent (PageType.Tutorial, null);
}
this.previous_address = "";
this.syncing_folder = "";
this.previous_url = "";
SelectedPlugin = Plugins [0];
};
Program.Controller.FolderFetchError += delegate (string remote_url) {
this.previous_url = remote_url;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Error, null);
this.syncing_folder = "";
};
Program.Controller.FolderFetching += delegate (double percentage) {
if (UpdateProgressBarEvent != null)
UpdateProgressBarEvent (percentage);
};
Program.Controller.FetchFolder (address, path);
}
public void ErrorPageCompleted ()
{
if (ChangePageEvent != null)
ChangePageEvent (PageType.Add, null);
}
public void SyncingCancelled ()
{
Program.Controller.StopFetcher ();
if (ChangePageEvent != null)
ChangePageEvent (PageType.Add, null);
}
// TODO: public void WindowClosed () { }
public void FinishedPageCompleted ()
{
this.previous_address = "";
this.previous_path = "";
Program.Controller.UpdateState ();
} }
@ -332,6 +243,155 @@ namespace SparkleShare {
} }
public void CheckAddPage (string address, string remote_path, int selected_plugin)
{
if (SelectedPluginIndex != selected_plugin)
SelectedPluginChanged (selected_plugin);
address = address.Trim ();
remote_path = remote_path.Trim ();
bool fields_valid = address != null && address.Trim().Length > 0 &&
remote_path != null && remote_path.Trim().Length > 0;
if (UpdateAddProjectButtonEvent != null)
UpdateAddProjectButtonEvent (fields_valid);
}
public void AddPageCompleted (string address, string path)
{
SyncingFolder = Path.GetFileNameWithoutExtension (path);
PreviousAddress = address;
PreviousPath = path;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Syncing, null);
// TODO: Remove events afterwards
Program.Controller.FolderFetched += delegate (string [] warnings) {
if (ChangePageEvent != null)
ChangePageEvent (PageType.Finished, warnings);
PreviousAddress = "";
SyncingFolder = "";
PreviousUrl = "";
SelectedPlugin = Plugins [0];
};
Program.Controller.FolderFetchError += delegate (string remote_url) {
Thread.Sleep (1000);
PreviousUrl = remote_url;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Error, null);
SyncingFolder = "";
};
Program.Controller.FolderFetching += delegate (double percentage) {
if (UpdateProgressBarEvent != null)
UpdateProgressBarEvent (percentage);
};
Program.Controller.FetchFolder (address, path);
}
// TODO: trailing slash should work
public SparkleInvite PendingInvite = new SparkleInvite ("ssh://git@github.com/",
"/hbons/Stuff", "http://www.sparkleshare.org/");
public void InvitePageCompleted ()
{
SyncingFolder = Path.GetFileNameWithoutExtension (PendingInvite.RemotePath);
PreviousAddress = PendingInvite.Address;
PreviousPath = PendingInvite.RemotePath;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Syncing, null);
if (!PendingInvite.Accept ()) {
if (ChangePageEvent != null)
ChangePageEvent (PageType.Error, null);
return;
}
// TODO: Remove events afterwards
Program.Controller.FolderFetched += delegate (string [] warnings) {
if (ChangePageEvent != null)
ChangePageEvent (PageType.Finished, warnings);
PreviousAddress = "";
SyncingFolder = "";
PreviousUrl = "";
SelectedPlugin = Plugins [0];
PendingInvite = null;
};
Program.Controller.FolderFetchError += delegate (string remote_url) {
Thread.Sleep (1000);
PreviousUrl = remote_url;
if (ChangePageEvent != null)
ChangePageEvent (PageType.Error, null);
SyncingFolder = "";
};
Program.Controller.FolderFetching += delegate (double percentage) {
if (UpdateProgressBarEvent != null)
UpdateProgressBarEvent (percentage);
};
Program.Controller.FetchFolder (PendingInvite.Address, PendingInvite.RemotePath);
}
public void SyncingCancelled ()
{
Program.Controller.StopFetcher ();
if (ChangePageEvent == null)
return;
if (PendingInvite != null)
ChangePageEvent (PageType.Invite, null);
else
ChangePageEvent (PageType.Add, null);
}
public void ErrorPageCompleted ()
{
if (ChangePageEvent == null)
return;
if (PendingInvite != null)
ChangePageEvent (PageType.Invite, null);
else
ChangePageEvent (PageType.Add, null);
}
public void FinishPageCompleted ()
{
PreviousAddress = "";
PreviousPath = "";
Program.Controller.UpdateState ();
if (HideWindowEvent != null)
HideWindowEvent ();
}
private bool IsValidEmail (string email) private bool IsValidEmail (string email)
{ {
Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$",

View file

@ -129,5 +129,11 @@ namespace SparkleShare {
UpdateMenuEvent (IconState.Error); UpdateMenuEvent (IconState.Error);
}; };
} }
public void AddHostedProjectClicked ()
{
Program.Controller.ShowSetupWindow (PageType.Add);
}
} }
} }