diff --git a/SparkleLib/Makefile.am b/SparkleLib/Makefile.am
index bfe7c9ee..e0886078 100644
--- a/SparkleLib/Makefile.am
+++ b/SparkleLib/Makefile.am
@@ -11,7 +11,8 @@ SOURCES = \
SparkleFetcher.cs \
SparkleGit.cs \
SparkleHelpers.cs \
- SparkleListener.cs \
+ SparkleListenerBase.cs \
+ SparkleListenerIrc.cs \
SparkleOptions.cs \
SparklePaths.cs \
SparkleRepo.cs
diff --git a/SparkleLib/SparkleLib.csproj b/SparkleLib/SparkleLib.csproj
index 76dfe1fb..1f437b48 100644
--- a/SparkleLib/SparkleLib.csproj
+++ b/SparkleLib/SparkleLib.csproj
@@ -46,7 +46,8 @@
-
+
+
@@ -65,4 +66,4 @@
-
\ No newline at end of file
+
diff --git a/SparkleLib/SparkleListener.cs b/SparkleLib/SparkleListener.cs
deleted file mode 100644
index 9d740816..00000000
--- a/SparkleLib/SparkleListener.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-// SparkleShare, an instant update workflow to Git.
-// Copyright (C) 2010 Hylke Bons
-//
-// 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 .
-
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using System.Security.Cryptography;
-
-using Meebey.SmartIrc4net;
-
-namespace SparkleLib {
-
- public enum NotificationServerType
- {
- Own,
- Central
- }
-
-
- // A persistent connection to the server that
- // listens for change notifications
- public class SparkleListener {
-
- private Thread thread;
-
- // FIXME: The IrcClient is a public property because
- // extending it causes crashes
- public IrcClient Client;
- public readonly string Server;
- public readonly string FallbackServer;
- public readonly string Channel;
- public readonly string Nick;
-
-
- public SparkleListener (string server, string folder_name,
- string user_email, NotificationServerType type)
- {
- if (type == NotificationServerType.Own) {
- Server = server;
- } else {
-
- // This is SparkleShare's centralized notification service.
- // Don't worry, we only use this server as a backup if you
- // don't have your own. All data needed to connect is hashed and
- // we don't store any personal information ever.
- Server = "204.62.14.135";
- }
-
- if (!String.IsNullOrEmpty (user_email))
- Nick = SHA1 (folder_name + user_email + "sparkles");
- else
- Nick = SHA1 (DateTime.Now.ToString () + "sparkles");
-
- Nick = "s" + Nick.Substring (0, 7);
- Channel = "#" + SHA1 (server + folder_name + "sparkles");
-
- Client = new IrcClient () {
- PingTimeout = 180,
- PingInterval = 90
- };
- }
-
-
- // Starts a new thread and listens to the channel
- public void Listen ()
- {
- this.thread = new Thread (
- new ThreadStart (delegate {
- try {
-
- // Connect, login, and join the channel
- Client.Connect (new string [] {Server}, 6667);
- Client.Login (Nick, Nick);
- Client.RfcJoin (Channel);
-
- // List to the channel, this blocks the thread
- Client.Listen ();
- Client.Disconnect ();
- } catch (Meebey.SmartIrc4net.ConnectionException e) {
- Console.WriteLine ("Could not connect: " + e.Message);
- }
- })
- );
-
- this.thread.Start ();
- }
-
-
- public void Announce (string message)
- {
- Client.SendMessage (SendType.Message, Channel, message);
- }
-
-
- // Frees all resources for this Listener
- public void Dispose ()
- {
- this.thread.Abort ();
- this.thread.Join ();
- }
-
-
- // Creates an SHA-1 hash of input
- private string SHA1 (string s)
- {
- SHA1 sha1 = new SHA1CryptoServiceProvider ();
- Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
- Byte[] encoded_bytes = sha1.ComputeHash (bytes);
- return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
- }
-
- }
-
-}
diff --git a/SparkleLib/SparkleListenerBase.cs b/SparkleLib/SparkleListenerBase.cs
new file mode 100644
index 00000000..8fbe1d9d
--- /dev/null
+++ b/SparkleLib/SparkleListenerBase.cs
@@ -0,0 +1,99 @@
+// SparkleShare, an instant update workflow to Git.
+// Copyright (C) 2010 Hylke Bons
+//
+// 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 .
+
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Security.Cryptography;
+
+using Meebey.SmartIrc4net;
+
+namespace SparkleLib {
+
+ public enum NotificationServerType
+ {
+ Own,
+ Central
+ }
+
+
+ // A persistent connection to the server that
+ // listens for change notifications
+ public abstract class SparkleListenerBase {
+
+ // We've connected to the server
+ public event ConnectedEventHandler Connected;
+ public delegate void ConnectedEventHandler ();
+
+ // We've disconnected from the server
+ public event DisconnectedEventHandler Disconnected;
+ public delegate void DisconnectedEventHandler ();
+
+ // We've been notified about a remote
+ // change by the channel
+ public event RemoteChangeEventHandler RemoteChange;
+ public delegate void RemoteChangeEventHandler (string change_id);
+
+ // Starts listening for remote changes
+ public abstract void Connect ();
+
+ // Announces to the channel that
+ // we've pushed changes to the server
+ public abstract void Announce (string message);
+
+ // Release all resources
+ public abstract void Dispose ();
+
+ public abstract bool IsConnected { get; }
+ public string Server;
+ public string Channel;
+
+ // Announcements that weren't sent off
+ // because we were disconnected
+ public List AnnounceQueue = new List ();
+
+ // Announcements of remote changes that we've received
+ public int ChangesQueue = 0;
+
+ public SparkleListenerBase (string server, string folder_identifier, NotificationServerType type) { }
+
+ public void DecrementChangesQueue ()
+ {
+ ChangesQueue--;
+ }
+
+ public void OnConnected ()
+ {
+ if (Connected != null)
+ Connected ();
+ }
+
+ public void OnDisconnected ()
+ {
+ if (Disconnected != null)
+ Disconnected ();
+ }
+
+ public void OnRemoteChange (string change_id)
+ {
+ if (RemoteChange != null)
+ RemoteChange (change_id);
+ }
+ }
+}
+
diff --git a/SparkleLib/SparkleListenerIrc.cs b/SparkleLib/SparkleListenerIrc.cs
new file mode 100644
index 00000000..ee9880e1
--- /dev/null
+++ b/SparkleLib/SparkleListenerIrc.cs
@@ -0,0 +1,161 @@
+// SparkleShare, an instant update workflow to Git.
+// Copyright (C) 2010 Hylke Bons
+//
+// 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 .
+
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Security.Cryptography;
+
+using Meebey.SmartIrc4net;
+
+namespace SparkleLib {
+
+ public class SparkleListenerIrc : SparkleListenerBase {
+
+ private Thread thread;
+ private IrcClient client;
+ private string nick;
+
+ public SparkleListenerIrc (string server, string folder_identifier,
+ NotificationServerType type) : base (server, folder_identifier, type)
+ {
+ if (type == NotificationServerType.Own) {
+ Server = server;
+ } else {
+
+ // This is SparkleShare's centralized notification service.
+ // Don't worry, we only use this server as a backup if you
+ // don't have your own. All data needed to connect is hashed and
+ // we don't store any personal information ever
+ Server = "204.62.14.135";
+ }
+
+ // Try to get a uniqueish nickname
+ this.nick = SHA1 (DateTime.Now.ToString ("ffffff") + "sparkles");
+
+ // Most irc servers don't allow nicknames starting
+ // with a number, so prefix an alphabetic character
+ this.nick = "s" + this.nick.Substring (0, 7);
+
+ // Hash and salt the folder identifier, so
+ // nobody knows any possible folder details
+ Channel = "#" + SHA1 (folder_identifier + "sparkles");
+
+ this.client = new IrcClient () {
+ PingTimeout = 180,
+ PingInterval = 90
+ };
+
+ this.client.OnConnected += delegate {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Connected to " + Channel + " on " + Server);
+
+ OnConnected ();
+
+ if (AnnounceQueue.Count > 0) {
+ string message = AnnounceQueue [AnnounceQueue.Count - 1];
+ AnnounceQueue = new List ();
+
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Delivering queued messages...");
+ Announce (message);
+ }
+ };
+
+ this.client.OnDisconnected += delegate {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Disconnected from " + Channel + " on " + Server);
+
+ OnDisconnected ();
+ };
+
+ this.client.OnChannelMessage += delegate (object o, IrcEventArgs args) {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Got message from " + Channel + " on " + Server);
+ string message = args.Data.Message.Trim ();
+ ChangesQueue++;
+
+ OnRemoteChange (message);
+ };
+ }
+
+
+ // Starts a new thread and listens to the channel
+ public override void Connect ()
+ {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Connecting to " + Channel + " on " + Server);
+
+ this.thread = new Thread (
+ new ThreadStart (delegate {
+ try {
+
+ // Connect, login, and join the channel
+ this.client.Connect (new string [] {Server}, 6667);
+ this.client.Login (this.nick, this.nick);
+ this.client.RfcJoin (Channel);
+
+ // List to the channel, this blocks the thread
+ this.client.Listen ();
+
+ // Disconnect when we time out
+ this.client.Disconnect ();
+ } catch (Meebey.SmartIrc4net.ConnectionException e) {
+ Console.WriteLine ("Could not connect: " + e.Message);
+ }
+ })
+ );
+
+ this.thread.Start ();
+ }
+
+
+ public override void Announce (string message)
+ {
+ if (IsConnected) {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Announcing to " + Channel + " on " + Server);
+ this.client.SendMessage (SendType.Message, Channel, message);
+ } else {
+ SparkleHelpers.DebugInfo ("ListenerIrc", "Not connected. Queuing message");
+ AnnounceQueue.Add (message);
+ }
+ }
+
+
+ public override bool IsConnected {
+ get {
+ return this.client.IsConnected;
+ }
+ }
+
+
+ public override void Dispose ()
+ {
+ this.thread.Abort ();
+ this.thread.Join ();
+ }
+
+
+ // Creates a SHA-1 hash of input
+ private string SHA1 (string s)
+ {
+ SHA1 sha1 = new SHA1CryptoServiceProvider ();
+ Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
+ Byte[] encoded_bytes = sha1.ComputeHash (bytes);
+ return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
+ }
+
+ }
+
+}
+
diff --git a/SparkleLib/SparkleRepo.cs b/SparkleLib/SparkleRepo.cs
index e1b8d5e7..bbb819bd 100644
--- a/SparkleLib/SparkleRepo.cs
+++ b/SparkleLib/SparkleRepo.cs
@@ -33,9 +33,7 @@ namespace SparkleLib {
private Timer local_timer;
private FileSystemWatcher watcher;
private System.Object change_lock;
- private int fetch_queue;
- private int announce_queue;
- private SparkleListener listener;
+ private SparkleListenerBase listener;
private List sizebuffer;
private bool has_changed;
@@ -153,8 +151,6 @@ namespace SparkleLib {
this.server_online = true;
this.has_changed = false;
this.change_lock = new Object ();
- this.fetch_queue = 0;
- this.announce_queue = 0;
if (IsEmpty)
this.current_hash = null;
@@ -184,11 +180,13 @@ namespace SparkleLib {
this.watcher.Deleted += new FileSystemEventHandler (OnFileActivity);
this.watcher.Renamed += new RenamedEventHandler (OnFileActivity);
- // Listen to the irc channel on the server...
+ NotificationServerType server_type;
if (UsesNotificationCenter)
- this.listener = new SparkleListener (Domain, RemoteName, UserEmail, NotificationServerType.Central);
+ server_type = NotificationServerType.Central;
else
- this.listener = new SparkleListener (Domain, RemoteName, UserEmail, NotificationServerType.Own);
+ server_type = NotificationServerType.Own;
+
+ this.listener = new SparkleListenerIrc (Domain, RemoteName, server_type);
// ...fetch remote changes every 60 seconds if that fails
this.remote_timer = new Timer () {
@@ -199,10 +197,8 @@ namespace SparkleLib {
if (this.is_polling) {
CheckForRemoteChanges ();
- if (!this.listener.Client.IsConnected) {
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Trying to reconnect...");
- this.listener.Listen ();
- }
+ if (!this.listener.IsConnected)
+ this.listener.Connect ();
}
if (this.has_unsynced_changes)
@@ -210,7 +206,7 @@ namespace SparkleLib {
};
// Stop polling when the connection to the irc channel is succesful
- this.listener.Client.OnConnected += delegate {
+ this.listener.Connected += delegate {
this.is_polling = false;
// Check for changes manually one more time
@@ -219,59 +215,34 @@ namespace SparkleLib {
// Push changes that were made since the last disconnect
if (this.has_unsynced_changes)
Push ();
-
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Connected. Now listening... (" + this.listener.Server + ")");
-
- if (this.announce_queue > 0) {
- this.listener.Announce (this.current_hash);
- this.announce_queue = 0;
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Queued messages delivered. (" + this.listener.Server + ")");
- }
- };
-
- // Start polling when the connection to the irc channel is lost
- this.listener.Client.OnConnectionError += delegate {
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Lost connection. Falling back to polling...");
- this.is_polling = true;
};
// Start polling when the connection to the irc channel is lost
- this.listener.Client.OnDisconnected += delegate {
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Lost connection. Falling back to polling...");
+ this.listener.Disconnected += delegate {
+ SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Falling back to polling");
this.is_polling = true;
};
// Fetch changes when there is a message in the irc channel
- this.listener.Client.OnChannelMessage += delegate (object o, IrcEventArgs args) {
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Was notified of a remote change...");
- string message = args.Data.Message.Trim ();
-
- if (!message.Equals (this.current_hash) && message.Length == 40) {
- this.fetch_queue++;
-
- if (this.is_buffering) {
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] ...but we're busy adding files. We'll fetch them later.");
- } else if (!this.is_fetching) {
- while (this.fetch_queue > 0) {
+ this.listener.RemoteChange += delegate (string change_id) {
+ if (!change_id.Equals (this.current_hash) && change_id.Length == 40) {
+ if (!this.is_fetching && !this.is_buffering) {
+ while (this.listener.ChangesQueue > 0) {
Fetch ();
- this.fetch_queue--;
+ this.listener.DecrementChangesQueue ();
}
-
+
this.watcher.EnableRaisingEvents = false;
Rebase ();
this.watcher.EnableRaisingEvents = true;
}
- } else {
- // Not really needed as we won't be notified about our own messages
- SparkleHelpers.DebugInfo ("Irc",
- "[" + Name + "] False alarm, already up to date. (" + this.current_hash + ")");
}
};
// Start listening
- this.listener.Listen ();
+ this.listener.Connect ();
- this.sizebuffer = new List ();
+ this.sizebuffer = new List ();
// Keep a timer that checks if there are changes and
// whether they have settled
@@ -304,7 +275,7 @@ namespace SparkleLib {
if (git.ExitCode != 0)
return;
- string remote_hash = git.StandardOutput.ReadToEnd ();
+ string remote_hash = git.StandardOutput.ReadToEnd ().TrimEnd ();
if (!remote_hash.StartsWith (this.current_hash)) {
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_hash + ")");
@@ -760,12 +731,7 @@ namespace SparkleLib {
if (PushingFinished != null)
PushingFinished (this, args);
- if (this.listener.Client.IsConnected) {
- this.listener.Announce (this.current_hash);
- } else {
- this.announce_queue++;
- SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Could not deliver notification, added it to the queue");
- }
+ this.listener.Announce (this.current_hash);
}
};