Replace abstract out Listener by ListenerBase + ListenerIrc
This commit is contained in:
parent
c76e4f2434
commit
5f36f8a9e0
|
@ -11,7 +11,8 @@ SOURCES = \
|
|||
SparkleFetcher.cs \
|
||||
SparkleGit.cs \
|
||||
SparkleHelpers.cs \
|
||||
SparkleListener.cs \
|
||||
SparkleListenerBase.cs \
|
||||
SparkleListenerIrc.cs \
|
||||
SparkleOptions.cs \
|
||||
SparklePaths.cs \
|
||||
SparkleRepo.cs
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
<Compile Include="SparkleEvents.cs" />
|
||||
<Compile Include="SparkleOptions.cs" />
|
||||
<Compile Include="SparkleCommit.cs" />
|
||||
<Compile Include="SparkleListener.cs" />
|
||||
<Compile Include="SparkleListenerBase.cs" />
|
||||
<Compile Include="SparkleListenerIrc.cs" />
|
||||
<Compile Include="SparkleGit.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
|
@ -65,4 +66,4 @@
|
|||
</Properties>
|
||||
</MonoDevelop>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
// SparkleShare, an instant update workflow to Git.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.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 ("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
99
SparkleLib/SparkleListenerBase.cs
Normal file
99
SparkleLib/SparkleListenerBase.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SparkleShare, an instant update workflow to Git.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.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<string> AnnounceQueue = new List<string> ();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
161
SparkleLib/SparkleListenerIrc.cs
Normal file
161
SparkleLib/SparkleListenerIrc.cs
Normal file
|
@ -0,0 +1,161 @@
|
|||
// SparkleShare, an instant update workflow to Git.
|
||||
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System;
|
||||
using System.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<string> ();
|
||||
|
||||
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 ("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <double> 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 <double> ();
|
||||
this.sizebuffer = new List <double> ();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue