diff --git a/SparkleLib/SparkleListenerBase.cs b/SparkleLib/SparkleListenerBase.cs index 39c39c1f..ef27895e 100755 --- a/SparkleLib/SparkleListenerBase.cs +++ b/SparkleLib/SparkleListenerBase.cs @@ -16,8 +16,10 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Timers; +using System.Linq; namespace SparkleLib { @@ -80,7 +82,7 @@ namespace SparkleLib { listeners.Add (new SparkleListenerTcp (announce_uri, folder_identifier)); break; } - + SparkleHelpers.DebugInfo ("ListenerFactory", "Issued new listener for " + announce_uri); return (SparkleListenerBase) listeners [listeners.Count - 1]; } @@ -104,20 +106,21 @@ namespace SparkleLib { public event AnnouncementEventHandler Announcement; public delegate void AnnouncementEventHandler (SparkleAnnouncement announcement); - public abstract void Connect (); public abstract void Announce (SparkleAnnouncement announcent); public abstract void AlsoListenTo (string folder_identifier); public abstract bool IsConnected { get; } - - protected List channels = new List (); - protected List queue_up = new List (); - protected List queue_down = new List (); + protected List channels = new List (); + protected Dictionary> recent_announcements = new Dictionary> (); + protected int max_recent_announcements = 10; + protected Dictionary queue_up = new Dictionary (); + protected Dictionary queue_down = new Dictionary (); protected bool is_connecting; protected Uri server; protected Timer reconnect_timer = new Timer { Interval = 60 * 1000, Enabled = true }; + public SparkleListenerBase (Uri server, string folder_identifier) { this.server = server; @@ -133,30 +136,23 @@ namespace SparkleLib { public void AnnounceBase (SparkleAnnouncement announcement) { - if (IsConnected) { - SparkleHelpers.DebugInfo ("Listener", - "Announcing to " + announcement.FolderIdentifier + " on " + this.server); + if (!this.IsRecentAnnounement (announcement)) { + if (IsConnected) { + SparkleHelpers.DebugInfo ("Listener", + "Announcing message " + announcement.Message + " to " + announcement.FolderIdentifier + " on " + this.server); - Announce (announcement); + Announce (announcement); + this.AddRecentAnnouncement (announcement); + } else { + SparkleHelpers.DebugInfo ("Listener", "Can't send message to " + this.server + ". Queuing message"); + this.queue_up [announcement.FolderIdentifier] = announcement; + } } else { - SparkleHelpers.DebugInfo ("Listener", "Not connected to " + this.server + ". Queuing message"); - this.queue_up.Add (announcement); - } - } - - - public string NextQueueDownMessage (string folder_identifier) - { - foreach (SparkleAnnouncement announcement in this.queue_down.GetRange (0, this.queue_down.Count)) { - if (announcement.FolderIdentifier.Equals (folder_identifier)) { - string message = announcement.Message; - this.queue_down.Remove (announcement); - return message; - } + SparkleHelpers.DebugInfo ("Listener", + "Already processed message " + announcement.Message + " to " + announcement.FolderIdentifier + " from " + this.server); } - return null; } @@ -177,10 +173,12 @@ namespace SparkleLib { if (this.queue_up.Count > 0) { SparkleHelpers.DebugInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages..."); - foreach (SparkleAnnouncement announcement in this.queue_up.GetRange (0, this.queue_up.Count)) { + foreach (KeyValuePair item in this.queue_up) { + SparkleAnnouncement announcement = item.Value; AnnounceBase (announcement); - this.queue_up.Remove (announcement); } + + this.queue_down.Clear (); } } @@ -196,15 +194,71 @@ namespace SparkleLib { public void OnAnnouncement (SparkleAnnouncement announcement) { - SparkleHelpers.DebugInfo ("Listener", "Got message from " + announcement.FolderIdentifier + " on " + this.server); + SparkleHelpers.DebugInfo ("Listener", + "Got message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server); - this.queue_down.Add (announcement); + if (IsRecentAnnounement(announcement) ){ + SparkleHelpers.DebugInfo ("Listener", + "Ignoring previously processed message " + announcement.Message + + " from " + announcement.FolderIdentifier + " on " + this.server); + + return; + } + + SparkleHelpers.DebugInfo ("Listener", + "Processing message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server); + + AddRecentAnnouncement (announcement); + this.queue_down [announcement.FolderIdentifier] = announcement; if (Announcement != null) Announcement (announcement); } + private bool IsRecentAnnounement (SparkleAnnouncement announcement) + { + if (!HasRecentAnnouncements (announcement.FolderIdentifier)) { + return false; + + } else { + foreach (SparkleAnnouncement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) { + if (recent_announcement.Message.Equals (announcement.Message)) + return true; + } + + return false; + } + } + + + private List GetRecentAnnouncements (string folder_identifier) + { + if (!this.recent_announcements.ContainsKey (folder_identifier)) + this.recent_announcements [folder_identifier] = new List (); + + return (List) this.recent_announcements [folder_identifier]; + } + + + private void AddRecentAnnouncement (SparkleAnnouncement announcement) + { + List recent_announcements = this.GetRecentAnnouncements (announcement.FolderIdentifier); + + if (!IsRecentAnnounement (announcement)) + recent_announcements.Add (announcement); + + if (recent_announcements.Count > this.max_recent_announcements) + recent_announcements.RemoveRange (0, (recent_announcements.Count - this.max_recent_announcements)); + } + + + private bool HasRecentAnnouncements (string folder_identifier) + { + return this.recent_announcements.ContainsKey (folder_identifier); + } + + public virtual void Dispose () { this.reconnect_timer.Dispose (); @@ -225,3 +279,4 @@ namespace SparkleLib { } } } + diff --git a/SparkleLib/SparkleListenerTcp.cs b/SparkleLib/SparkleListenerTcp.cs index c844bf02..6f464c13 100755 --- a/SparkleLib/SparkleListenerTcp.cs +++ b/SparkleLib/SparkleListenerTcp.cs @@ -29,7 +29,7 @@ namespace SparkleLib { public class SparkleListenerTcp : SparkleListenerBase { private Thread thread; - + // these are shared private readonly Object mutex = new Object(); private Socket socket; @@ -93,11 +93,14 @@ namespace SparkleLib { if (bytes_read > 0) { string received = Encoding.UTF8.GetString (bytes); - string folder_identifier = received.Substring (0, received.IndexOf ("!")); - string message = received.Substring (received.IndexOf ("!") + 1); - - OnAnnouncement (new SparkleAnnouncement (folder_identifier, message)); - + string line = received.Substring (0, received.IndexOf ("\n")); + if (!line.Contains ("!")) + continue; + string folder_identifier = line.Substring (0, line.IndexOf ("!")); + string message = this.CleanMessage (line.Substring (line.IndexOf ("!") + 1)); + if (!folder_identifier.Equals("debug") && + !String.IsNullOrEmpty(message)) + OnAnnouncement (new SparkleAnnouncement (folder_identifier, message)); } else { SparkleHelpers.DebugInfo ("ListenerTcp", "Error on socket"); @@ -109,9 +112,9 @@ namespace SparkleLib { } } } - + SparkleHelpers.DebugInfo ("ListenerTcp", "Disconnected from " + Server.Host); - + } catch (SocketException e) { SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message); @@ -173,5 +176,10 @@ namespace SparkleLib { this.thread.Join (); base.Dispose (); } + + private string CleanMessage(string message) + { + return message.Trim ().Replace ("\n", "").Replace ("\0", ""); + } } } diff --git a/SparkleLib/SparkleRepoBase.cs b/SparkleLib/SparkleRepoBase.cs index 9168db04..7cb2ca73 100755 --- a/SparkleLib/SparkleRepoBase.cs +++ b/SparkleLib/SparkleRepoBase.cs @@ -110,12 +110,6 @@ namespace SparkleLib { if (CheckForRemoteChanges ()) SyncDownBase (); - - string message; - while ((message = this.listener.NextQueueDownMessage (Identifier)) != null) { - if (!message.Equals (CurrentRevision)) - SyncDownBase (); - } } // In the unlikely case that we haven't synced up our @@ -265,19 +259,18 @@ namespace SparkleLib { if (announcement.FolderIdentifier.Equals (identifier) && !announcement.Message.Equals (CurrentRevision)) { - if ((Status != SyncStatus.SyncUp) && - (Status != SyncStatus.SyncDown) && - !this.is_buffering) { - - string message; - while ((message = this.listener.NextQueueDownMessage (identifier)) != null) { - if (!message.Equals (CurrentRevision)) - SyncDownBase (); - } + while (this.IsSyncing ()) { + System.Threading.Thread.Sleep (100); } + SparkleHelpers.DebugInfo ("Listener", "Syncing due to Announcement"); + if (!announcement.Message.Equals (CurrentRevision)) + SyncDownBase (); + } else { + if (announcement.FolderIdentifier.Equals (identifier)) + SparkleHelpers.DebugInfo ("Listener", "Not syncing message is for current revision"); } }; - + // Start listening if (!this.listener.IsConnected && !this.listener.IsConnecting) { this.listener.Connect (); @@ -285,13 +278,21 @@ namespace SparkleLib { } + private bool IsSyncing () + { + if (Status == SyncStatus.SyncUp || Status == SyncStatus.SyncDown || this.is_buffering) + return true; + return false; + } + + private void CheckForChanges () { lock (this.change_lock) { if (this.has_changed) { if (this.sizebuffer.Count >= 4) this.sizebuffer.RemoveAt (0); - + DirectoryInfo dir_info = new DirectoryInfo (LocalPath); this.sizebuffer.Add (CalculateFolderSize (dir_info)); @@ -303,7 +304,7 @@ namespace SparkleLib { SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled."); this.is_buffering = false; this.has_changed = false; - + DisableWatching (); while (AnyDifferences) SyncUpBase (); @@ -447,6 +448,8 @@ namespace SparkleLib { if (SyncStatusChanged != null) SyncStatusChanged (SyncStatus.SyncDown); + string pre_sync_revision = CurrentRevision; + if (SyncDown ()) { SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Done"); this.server_online = true; @@ -454,24 +457,27 @@ namespace SparkleLib { if (SyncStatusChanged != null) SyncStatusChanged (SyncStatus.Idle); - List change_sets = GetChangeSets (1); - if (change_sets != null && change_sets.Count > 0) { - SparkleChangeSet change_set = change_sets [0]; + if (!pre_sync_revision.Equals (CurrentRevision)) { + List change_sets = GetChangeSets (1); - bool note_added = false; - foreach (string added in change_set.Added) { - if (added.Contains (".notes")) { - if (NewNote != null) - NewNote (change_set.User.Name, change_set.User.Email); + if (change_sets != null && change_sets.Count > 0) { + SparkleChangeSet change_set = change_sets [0]; - note_added = true; - break; + bool note_added = false; + foreach (string added in change_set.Added) { + if (added.Contains (".notes")) { + if (NewNote != null) + NewNote (change_set.User.Name, change_set.User.Email); + + note_added = true; + break; + } } - } - if (!note_added) { - if (NewChangeSet != null) - NewChangeSet (change_set); + if (!note_added) { + if (NewChangeSet != null) + NewChangeSet (change_set); + } } } diff --git a/SparkleShare/SparkleControllerBase.cs b/SparkleShare/SparkleControllerBase.cs index 3d89e086..14a269a0 100755 --- a/SparkleShare/SparkleControllerBase.cs +++ b/SparkleShare/SparkleControllerBase.cs @@ -81,6 +81,7 @@ namespace SparkleShare { public abstract string PluginsPath { get; } private SparkleFetcherBase fetcher; + private List failed_avatars = new List (); // Short alias for the translations @@ -941,6 +942,8 @@ namespace SparkleShare { } } + } else if (this.failed_avatars.Contains (email)) { + break; } else { WebClient client = new WebClient (); string url = "http://gravatar.com/avatar/" + GetMD5 (email) + @@ -962,8 +965,11 @@ namespace SparkleShare { SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email); // Stop downloading further avatars if we have no internet access - if (e.Status == WebExceptionStatus.Timeout) + if (e.Status == WebExceptionStatus.Timeout){ break; + } else { + this.failed_avatars.Add (email); + } } } } diff --git a/data/git-hooks/post-update b/data/git-hooks/post-update new file mode 100755 index 00000000..45078f3c --- /dev/null +++ b/data/git-hooks/post-update @@ -0,0 +1,29 @@ +#!/bin/sh + +# for use with gitolite +# +# http://sitaramc.github.com/gitolite/hooks.html +# copy this file to +# .gitolite/hooks/common/post-update +# run gl-setup again + +# for use with standard ssh/http(s)/git repos +# +# simply move this file to +# .git/hooks/post-update in the remote repository + +# make sure to chmod -x in all cases after the file has been copied + +#To supress all output +exec > /dev/null 2>&1 + +# for information on running your own server +# https://github.com/hbons/fanout.node.js +SERVER="204.62.14.135" +PORT="1986" +CHANNEL=$(git rev-list --reverse HEAD | head -n 1) +MESSAGE=$(git rev-list HEAD | head -n 1) +DATA="announce ${CHANNEL} ${MESSAGE}" +echo "${DATA}\n" | socat - TCP-CONNECT:${SERVER}:${PORT} & + +exit 0