2011-06-01 23:08:05 +00:00
|
|
|
// SparkleShare, a collaboration and sharing tool.
|
2011-05-19 16:05:58 +00:00
|
|
|
// 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.IO;
|
2011-07-16 22:15:56 +00:00
|
|
|
using System.Security.Cryptography;
|
|
|
|
using System.Text;
|
2011-05-19 16:05:58 +00:00
|
|
|
using System.Text.RegularExpressions;
|
2011-11-13 00:57:00 +00:00
|
|
|
using System.Threading;
|
2011-05-19 16:05:58 +00:00
|
|
|
using System.Timers;
|
|
|
|
|
|
|
|
namespace SparkleLib {
|
|
|
|
|
|
|
|
public enum SyncStatus {
|
|
|
|
Idle,
|
|
|
|
SyncUp,
|
|
|
|
SyncDown,
|
|
|
|
Error
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public abstract class SparkleRepoBase {
|
|
|
|
|
2011-06-12 01:56:01 +00:00
|
|
|
private TimeSpan short_interval = new TimeSpan (0, 0, 3, 0);
|
|
|
|
private TimeSpan long_interval = new TimeSpan (0, 0, 10, 0);
|
|
|
|
|
2011-06-14 22:14:03 +00:00
|
|
|
private SparkleWatcher watcher;
|
2011-06-12 01:56:01 +00:00
|
|
|
private TimeSpan poll_interval;
|
2011-11-13 18:04:13 +00:00
|
|
|
private System.Timers.Timer local_timer = new System.Timers.Timer () { Interval = 0.25 * 1000 };
|
|
|
|
private System.Timers.Timer remote_timer = new System.Timers.Timer () { Interval = 10 * 1000 };
|
2011-12-30 00:44:35 +00:00
|
|
|
private DateTime last_poll = DateTime.Now;
|
2012-02-01 22:04:58 +00:00
|
|
|
private List<double> size_buffer = new List<double> ();
|
2011-12-30 00:44:35 +00:00
|
|
|
private bool has_changed = false;
|
|
|
|
private Object change_lock = new Object ();
|
|
|
|
private Object watch_lock = new Object ();
|
|
|
|
private double progress_percentage = 0.0;
|
|
|
|
private string progress_speed = "";
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2011-06-22 01:03:27 +00:00
|
|
|
protected SparkleListenerBase listener;
|
2011-06-02 23:35:51 +00:00
|
|
|
protected SyncStatus status;
|
|
|
|
protected bool is_buffering = false;
|
|
|
|
protected bool server_online = true;
|
|
|
|
|
|
|
|
public readonly string LocalPath;
|
|
|
|
public readonly string Name;
|
2012-01-25 18:30:29 +00:00
|
|
|
public readonly Uri Url;
|
2011-06-02 23:35:51 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
public abstract bool AnyDifferences { get; }
|
|
|
|
public abstract string Identifier { get; }
|
|
|
|
public abstract string CurrentRevision { get; }
|
|
|
|
public abstract bool SyncUp ();
|
|
|
|
public abstract bool SyncDown ();
|
2011-12-15 15:15:29 +00:00
|
|
|
public abstract double CalculateSize (DirectoryInfo parent);
|
2011-05-19 16:05:58 +00:00
|
|
|
public abstract bool HasUnsyncedChanges { get; set; }
|
2011-12-29 11:44:18 +00:00
|
|
|
public abstract List<string> ExcludePaths { get; }
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
public abstract double Size { get; }
|
|
|
|
public abstract double HistorySize { get; }
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
|
|
|
|
public event SyncStatusChangedEventHandler SyncStatusChanged;
|
|
|
|
|
2011-12-30 00:44:35 +00:00
|
|
|
public delegate void SyncProgressChangedEventHandler (double percentage, string speed);
|
|
|
|
public event SyncProgressChangedEventHandler SyncProgressChanged;
|
|
|
|
|
2011-07-23 21:23:14 +00:00
|
|
|
public delegate void NewChangeSetEventHandler (SparkleChangeSet change_set);
|
2011-05-19 16:05:58 +00:00
|
|
|
public event NewChangeSetEventHandler NewChangeSet;
|
2011-05-22 14:46:15 +00:00
|
|
|
|
2011-11-18 20:53:53 +00:00
|
|
|
public delegate void NewNoteEventHandler (SparkleUser user);
|
2011-07-23 21:23:14 +00:00
|
|
|
public event NewNoteEventHandler NewNote;
|
|
|
|
|
2011-05-22 14:46:15 +00:00
|
|
|
public delegate void ConflictResolvedEventHandler ();
|
2011-05-19 16:05:58 +00:00
|
|
|
public event ConflictResolvedEventHandler ConflictResolved;
|
2011-05-22 14:46:15 +00:00
|
|
|
|
|
|
|
public delegate void ChangesDetectedEventHandler ();
|
2011-05-19 16:05:58 +00:00
|
|
|
public event ChangesDetectedEventHandler ChangesDetected;
|
|
|
|
|
|
|
|
|
2012-01-29 20:33:12 +00:00
|
|
|
public SparkleRepoBase (string path)
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2012-01-25 18:30:29 +00:00
|
|
|
LocalPath = path;
|
|
|
|
Name = Path.GetFileName (LocalPath);
|
|
|
|
Url = new Uri (SparkleConfig.DefaultConfig.GetUrlForFolder (Name));
|
|
|
|
|
2011-06-12 01:56:01 +00:00
|
|
|
this.poll_interval = this.short_interval;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
|
|
SyncStatusChanged += delegate (SyncStatus status) {
|
|
|
|
this.status = status;
|
|
|
|
};
|
|
|
|
|
2011-06-23 12:56:25 +00:00
|
|
|
if (CurrentRevision == null)
|
2011-05-19 16:05:58 +00:00
|
|
|
CreateInitialChangeSet ();
|
|
|
|
|
2011-06-28 19:06:04 +00:00
|
|
|
CreateWatcher ();
|
2011-06-28 19:54:47 +00:00
|
|
|
CreateListener ();
|
2011-06-28 19:06:04 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
this.local_timer.Elapsed += delegate (object o, ElapsedEventArgs args) {
|
|
|
|
CheckForChanges ();
|
|
|
|
};
|
|
|
|
|
|
|
|
this.remote_timer.Elapsed += delegate {
|
2011-06-09 20:20:57 +00:00
|
|
|
bool time_to_poll = (DateTime.Compare (this.last_poll,
|
|
|
|
DateTime.Now.Subtract (this.poll_interval)) < 0);
|
|
|
|
|
2011-06-09 22:59:56 +00:00
|
|
|
if (time_to_poll) {
|
2011-06-09 20:20:57 +00:00
|
|
|
this.last_poll = DateTime.Now;
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
if (CheckForRemoteChanges ())
|
|
|
|
SyncDownBase ();
|
|
|
|
}
|
|
|
|
|
2011-05-22 17:37:36 +00:00
|
|
|
// In the unlikely case that we haven't synced up our
|
|
|
|
// changes or the server was down, sync up again
|
2011-12-03 11:40:03 +00:00
|
|
|
if (HasUnsyncedChanges && !IsSyncing && this.server_online)
|
2011-05-19 16:05:58 +00:00
|
|
|
SyncUpBase ();
|
|
|
|
};
|
|
|
|
|
2012-01-18 16:59:44 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Initialize ()
|
|
|
|
{
|
2011-05-19 16:05:58 +00:00
|
|
|
// Sync up everything that changed
|
2011-05-20 00:55:49 +00:00
|
|
|
// since we've been offline
|
2011-05-19 16:05:58 +00:00
|
|
|
if (AnyDifferences) {
|
2011-05-20 00:55:49 +00:00
|
|
|
DisableWatching ();
|
2011-05-19 16:05:58 +00:00
|
|
|
SyncUpBase ();
|
2011-05-20 00:55:49 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
while (HasUnsyncedChanges)
|
|
|
|
SyncUpBase ();
|
2011-11-13 00:27:25 +00:00
|
|
|
|
2011-05-20 00:55:49 +00:00
|
|
|
EnableWatching ();
|
|
|
|
}
|
2011-06-28 19:54:47 +00:00
|
|
|
|
|
|
|
this.remote_timer.Start ();
|
|
|
|
this.local_timer.Start ();
|
2011-05-20 00:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-22 14:46:15 +00:00
|
|
|
public bool ServerOnline {
|
|
|
|
get {
|
|
|
|
return this.server_online;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public SyncStatus Status {
|
|
|
|
get {
|
|
|
|
return this.status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-26 01:05:30 +00:00
|
|
|
|
2011-12-30 00:44:35 +00:00
|
|
|
public double ProgressPercentage {
|
|
|
|
get {
|
|
|
|
return this.progress_percentage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string ProgressSpeed {
|
|
|
|
get {
|
|
|
|
return this.progress_speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-29 19:28:49 +00:00
|
|
|
public virtual string [] UnsyncedFilePaths {
|
|
|
|
get {
|
|
|
|
return new string [0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-26 01:05:30 +00:00
|
|
|
public string Domain {
|
|
|
|
get {
|
2011-06-08 01:04:54 +00:00
|
|
|
Regex regex = new Regex (@"(@|://)([a-z0-9\.-]+)(/|:)");
|
2011-05-26 22:41:55 +00:00
|
|
|
Match match = regex.Match (SparkleConfig.DefaultConfig.GetUrlForFolder (Name));
|
2011-05-26 01:05:30 +00:00
|
|
|
|
|
|
|
if (match.Success)
|
|
|
|
return match.Groups [2].Value;
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-22 14:46:15 +00:00
|
|
|
protected void OnConflictResolved ()
|
|
|
|
{
|
2011-05-22 17:49:04 +00:00
|
|
|
HasUnsyncedChanges = true;
|
|
|
|
|
2011-05-22 14:46:15 +00:00
|
|
|
if (ConflictResolved != null)
|
|
|
|
ConflictResolved ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-03 11:40:03 +00:00
|
|
|
public virtual bool CheckForRemoteChanges () // TODO: HasRemoteChanges { get; }
|
2011-05-20 00:55:49 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-23 20:19:51 +00:00
|
|
|
public virtual List<SparkleChangeSet> GetChangeSets (int count) {
|
2011-05-20 00:55:49 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual bool UsesNotificationCenter {
|
|
|
|
get {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string RemoteName {
|
|
|
|
get {
|
2011-05-26 22:41:55 +00:00
|
|
|
string url = SparkleConfig.DefaultConfig.GetUrlForFolder (Name);
|
|
|
|
return Path.GetFileNameWithoutExtension (url);
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
2011-05-20 00:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsBuffering {
|
|
|
|
get {
|
|
|
|
return this.is_buffering;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Disposes all resourses of this object
|
|
|
|
public void Dispose ()
|
|
|
|
{
|
|
|
|
this.remote_timer.Dispose ();
|
|
|
|
this.local_timer.Dispose ();
|
|
|
|
this.listener.Dispose ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CreateWatcher ()
|
|
|
|
{
|
2011-06-14 22:14:03 +00:00
|
|
|
this.watcher = new SparkleWatcher (LocalPath);
|
|
|
|
this.watcher.ChangeEvent += delegate (FileSystemEventArgs args) {
|
|
|
|
OnFileActivity (args);
|
2011-05-20 00:55:49 +00:00
|
|
|
};
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-28 19:54:47 +00:00
|
|
|
public void CreateListener ()
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2011-06-28 19:54:47 +00:00
|
|
|
this.listener = SparkleListenerFactory.CreateListener (Name, Identifier);
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2011-11-13 00:27:25 +00:00
|
|
|
if (this.listener.IsConnected) {
|
|
|
|
this.poll_interval = this.long_interval;
|
|
|
|
|
2011-11-13 00:57:00 +00:00
|
|
|
new Thread (new ThreadStart (delegate {
|
|
|
|
if (!IsSyncing && CheckForRemoteChanges ())
|
|
|
|
SyncDownBase ();
|
|
|
|
})).Start ();
|
2011-11-13 00:27:25 +00:00
|
|
|
}
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
// Stop polling when the connection to the irc channel is succesful
|
|
|
|
this.listener.Connected += delegate {
|
2011-06-12 01:56:01 +00:00
|
|
|
this.poll_interval = this.long_interval;
|
2011-06-09 20:20:57 +00:00
|
|
|
this.last_poll = DateTime.Now;
|
|
|
|
|
2011-11-13 00:27:25 +00:00
|
|
|
if (!IsSyncing) {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2011-11-13 00:27:25 +00:00
|
|
|
// Check for changes manually one more time
|
|
|
|
if (CheckForRemoteChanges ())
|
|
|
|
SyncDownBase ();
|
|
|
|
|
|
|
|
// Push changes that were made since the last disconnect
|
|
|
|
if (HasUnsyncedChanges)
|
|
|
|
SyncUpBase ();
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Start polling when the connection to the irc channel is lost
|
|
|
|
this.listener.Disconnected += delegate {
|
2011-06-12 01:56:01 +00:00
|
|
|
this.poll_interval = this.short_interval;
|
2011-05-22 14:46:15 +00:00
|
|
|
SparkleHelpers.DebugInfo (Name, "Falling back to polling");
|
2011-05-19 16:05:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Fetch changes when there is a message in the irc channel
|
2012-02-01 16:23:49 +00:00
|
|
|
this.listener.Received += delegate (SparkleAnnouncement announcement) {
|
2011-05-22 17:37:36 +00:00
|
|
|
string identifier = Identifier;
|
|
|
|
|
2011-11-04 00:05:25 +00:00
|
|
|
if (announcement.FolderIdentifier.Equals (identifier) &&
|
2011-05-22 00:02:16 +00:00
|
|
|
!announcement.Message.Equals (CurrentRevision)) {
|
2011-11-04 00:05:25 +00:00
|
|
|
|
2011-11-13 00:27:25 +00:00
|
|
|
while (this.IsSyncing)
|
2011-11-07 15:39:33 +00:00
|
|
|
System.Threading.Thread.Sleep (100);
|
2011-11-13 00:27:25 +00:00
|
|
|
|
|
|
|
SparkleHelpers.DebugInfo ("Listener", "Syncing due to announcement");
|
|
|
|
SyncDownBase ();
|
|
|
|
|
2011-11-05 21:09:09 +00:00
|
|
|
} else {
|
|
|
|
if (announcement.FolderIdentifier.Equals (identifier))
|
2011-11-13 00:27:25 +00:00
|
|
|
SparkleHelpers.DebugInfo ("Listener", "Not syncing, message is for current revision");
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
};
|
2011-11-05 21:09:09 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
// Start listening
|
2011-11-13 00:27:25 +00:00
|
|
|
if (!this.listener.IsConnected && !this.listener.IsConnecting)
|
2011-05-22 00:02:16 +00:00
|
|
|
this.listener.Connect ();
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-30 00:44:35 +00:00
|
|
|
public bool IsSyncing {
|
2011-11-13 00:27:25 +00:00
|
|
|
get {
|
|
|
|
return (Status == SyncStatus.SyncUp ||
|
|
|
|
Status == SyncStatus.SyncDown ||
|
|
|
|
this.is_buffering);
|
|
|
|
}
|
2011-11-05 21:09:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
private void CheckForChanges ()
|
|
|
|
{
|
|
|
|
lock (this.change_lock) {
|
|
|
|
if (this.has_changed) {
|
2012-02-01 22:04:58 +00:00
|
|
|
if (this.size_buffer.Count >= 4)
|
|
|
|
this.size_buffer.RemoveAt (0);
|
2011-11-05 21:09:09 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
DirectoryInfo dir_info = new DirectoryInfo (LocalPath);
|
2012-02-01 22:04:58 +00:00
|
|
|
this.size_buffer.Add (CalculateSize (dir_info));
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-02-01 22:04:58 +00:00
|
|
|
if (this.size_buffer.Count >= 4 &&
|
|
|
|
this.size_buffer [0].Equals (this.size_buffer [1]) &&
|
|
|
|
this.size_buffer [1].Equals (this.size_buffer [2]) &&
|
|
|
|
this.size_buffer [2].Equals (this.size_buffer [3])) {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
|
|
SparkleHelpers.DebugInfo ("Local", "[" + Name + "] Changes have settled.");
|
|
|
|
this.is_buffering = false;
|
|
|
|
this.has_changed = false;
|
2011-11-05 21:09:09 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
DisableWatching ();
|
|
|
|
while (AnyDifferences)
|
2011-05-22 14:46:15 +00:00
|
|
|
SyncUpBase ();
|
2011-05-19 16:05:58 +00:00
|
|
|
EnableWatching ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Starts a timer when something changes
|
2011-06-14 22:14:03 +00:00
|
|
|
public void OnFileActivity (FileSystemEventArgs args)
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2011-06-22 15:02:03 +00:00
|
|
|
// Check the watcher for the occasions where this
|
|
|
|
// method is called directly
|
|
|
|
if (!this.watcher.EnableRaisingEvents)
|
|
|
|
return;
|
|
|
|
|
2011-12-29 11:44:18 +00:00
|
|
|
string relative_path = args.FullPath.Replace (LocalPath, "");
|
|
|
|
|
|
|
|
foreach (string exclude_path in ExcludePaths) {
|
|
|
|
if (relative_path.Contains (exclude_path))
|
|
|
|
return;
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2011-05-29 12:53:09 +00:00
|
|
|
WatcherChangeTypes wct = args.ChangeType;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
|
|
if (AnyDifferences) {
|
|
|
|
this.is_buffering = true;
|
|
|
|
|
2011-07-20 23:02:21 +00:00
|
|
|
// We want to disable wathcing temporarily, but
|
|
|
|
// not stop the local timer
|
|
|
|
this.watcher.EnableRaisingEvents = false;
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
// Only fire the event if the timer has been stopped.
|
|
|
|
// This prevents multiple events from being raised whilst "buffering".
|
|
|
|
if (!this.has_changed) {
|
|
|
|
if (ChangesDetected != null)
|
|
|
|
ChangesDetected ();
|
|
|
|
}
|
|
|
|
|
2011-05-29 12:53:09 +00:00
|
|
|
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] " + wct.ToString () + " '" + args.Name + "'");
|
|
|
|
SparkleHelpers.DebugInfo ("Event", "[" + Name + "] Changes found, checking if settled.");
|
2011-07-20 23:02:21 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
this.remote_timer.Stop ();
|
|
|
|
|
|
|
|
lock (this.change_lock) {
|
|
|
|
this.has_changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
public List<SparkleNote> GetNotes (string revision) {
|
|
|
|
List<SparkleNote> notes = new List<SparkleNote> ();
|
|
|
|
|
|
|
|
string notes_path = Path.Combine (LocalPath, ".notes");
|
|
|
|
|
|
|
|
if (!Directory.Exists (notes_path))
|
|
|
|
Directory.CreateDirectory (notes_path);
|
|
|
|
|
|
|
|
Regex regex_notes = new Regex (@"<name>(.+)</name>.*" +
|
|
|
|
"<email>(.+)</email>.*" +
|
|
|
|
"<timestamp>([0-9]+)</timestamp>.*" +
|
|
|
|
"<body>(.+)</body>", RegexOptions.Compiled);
|
|
|
|
|
2011-07-17 00:22:39 +00:00
|
|
|
foreach (string file_path in Directory.GetFiles (notes_path)) {
|
|
|
|
if (Path.GetFileName (file_path).StartsWith (revision)) {
|
|
|
|
string note_xml = String.Join ("", File.ReadAllLines (file_path));
|
2011-07-16 22:15:56 +00:00
|
|
|
|
|
|
|
Match match_notes = regex_notes.Match (note_xml);
|
|
|
|
|
|
|
|
if (match_notes.Success) {
|
|
|
|
SparkleNote note = new SparkleNote () {
|
2011-07-24 01:00:40 +00:00
|
|
|
User = new SparkleUser (match_notes.Groups [1].Value,
|
|
|
|
match_notes.Groups [2].Value),
|
2011-07-16 22:15:56 +00:00
|
|
|
Timestamp = new DateTime (1970, 1, 1).AddSeconds (int.Parse (match_notes.Groups [3].Value)),
|
|
|
|
Body = match_notes.Groups [4].Value
|
|
|
|
};
|
|
|
|
|
|
|
|
notes.Add (note);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return notes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-20 00:55:49 +00:00
|
|
|
private void SyncUpBase ()
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
|
|
|
try {
|
2011-06-14 13:25:04 +00:00
|
|
|
DisableWatching ();
|
2011-05-19 16:05:58 +00:00
|
|
|
this.remote_timer.Stop ();
|
|
|
|
|
|
|
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Initiated");
|
|
|
|
|
2011-05-20 00:55:49 +00:00
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.SyncUp);
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
|
|
if (SyncUp ()) {
|
|
|
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Done");
|
|
|
|
|
|
|
|
HasUnsyncedChanges = false;
|
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Idle);
|
|
|
|
|
2011-05-22 00:02:16 +00:00
|
|
|
this.listener.AnnounceBase (new SparkleAnnouncement (Identifier, CurrentRevision));
|
2011-05-22 16:51:48 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
} else {
|
|
|
|
SparkleHelpers.DebugInfo ("SyncUp", "[" + Name + "] Error");
|
|
|
|
|
|
|
|
HasUnsyncedChanges = true;
|
|
|
|
SyncDownBase ();
|
2011-12-03 11:40:03 +00:00
|
|
|
DisableWatching ();
|
2011-05-22 17:06:28 +00:00
|
|
|
|
2011-12-03 11:40:03 +00:00
|
|
|
if (this.server_online && SyncUp ()) {
|
2011-05-22 17:06:28 +00:00
|
|
|
HasUnsyncedChanges = false;
|
2011-05-22 17:12:40 +00:00
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Idle);
|
|
|
|
|
|
|
|
this.listener.AnnounceBase (new SparkleAnnouncement (Identifier, CurrentRevision));
|
|
|
|
|
|
|
|
} else {
|
2011-12-03 11:40:03 +00:00
|
|
|
this.server_online = false;
|
|
|
|
|
2011-05-22 17:12:40 +00:00
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Error);
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
2011-05-22 16:51:48 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
} finally {
|
|
|
|
this.remote_timer.Start ();
|
2011-06-14 13:25:04 +00:00
|
|
|
EnableWatching ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
|
|
this.progress_percentage = 0.0;
|
|
|
|
this.progress_speed = "";
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void SyncDownBase ()
|
|
|
|
{
|
|
|
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Initiated");
|
|
|
|
this.remote_timer.Stop ();
|
2011-06-14 13:25:04 +00:00
|
|
|
DisableWatching ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.SyncDown);
|
|
|
|
|
2011-11-07 15:39:33 +00:00
|
|
|
string pre_sync_revision = CurrentRevision;
|
2011-11-12 17:47:39 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
if (SyncDown ()) {
|
|
|
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Done");
|
|
|
|
this.server_online = true;
|
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Idle);
|
2011-05-22 14:46:15 +00:00
|
|
|
|
2011-11-12 17:47:39 +00:00
|
|
|
if (!pre_sync_revision.Equals (CurrentRevision)) {
|
2011-11-07 15:39:33 +00:00
|
|
|
List<SparkleChangeSet> change_sets = GetChangeSets (1);
|
2011-11-12 17:47:39 +00:00
|
|
|
|
|
|
|
if (change_sets != null && change_sets.Count > 0) {
|
2011-11-07 15:39:33 +00:00
|
|
|
SparkleChangeSet change_set = change_sets [0];
|
2011-08-26 20:00:33 +00:00
|
|
|
|
2011-11-07 15:39:33 +00:00
|
|
|
bool note_added = false;
|
|
|
|
foreach (string added in change_set.Added) {
|
|
|
|
if (added.Contains (".notes")) {
|
|
|
|
if (NewNote != null)
|
2011-11-18 20:53:53 +00:00
|
|
|
NewNote (change_set.User);
|
2011-08-26 20:00:33 +00:00
|
|
|
|
2011-11-07 15:39:33 +00:00
|
|
|
note_added = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-08-26 20:00:33 +00:00
|
|
|
}
|
2011-07-23 21:23:14 +00:00
|
|
|
|
2011-11-07 15:39:33 +00:00
|
|
|
if (!note_added) {
|
|
|
|
if (NewChangeSet != null)
|
|
|
|
NewChangeSet (change_set);
|
|
|
|
}
|
2011-08-26 20:00:33 +00:00
|
|
|
}
|
2011-07-23 21:23:14 +00:00
|
|
|
}
|
2011-06-03 00:33:41 +00:00
|
|
|
|
2011-07-24 18:22:17 +00:00
|
|
|
// There could be changes from a resolved
|
|
|
|
// conflict. Tries only once, then lets
|
|
|
|
// the timer try again periodically
|
2011-05-22 14:46:15 +00:00
|
|
|
if (HasUnsyncedChanges)
|
|
|
|
SyncUp ();
|
2011-05-22 17:49:04 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
} else {
|
|
|
|
SparkleHelpers.DebugInfo ("SyncDown", "[" + Name + "] Error");
|
|
|
|
this.server_online = false;
|
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SyncStatusChanged != null)
|
|
|
|
SyncStatusChanged (SyncStatus.Idle);
|
|
|
|
|
|
|
|
this.remote_timer.Start ();
|
2011-06-14 13:25:04 +00:00
|
|
|
EnableWatching ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
|
|
|
this.progress_percentage = 0.0;
|
|
|
|
this.progress_speed = "";
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void DisableWatching ()
|
|
|
|
{
|
2011-12-11 20:00:40 +00:00
|
|
|
lock (this.watch_lock) {
|
2011-11-13 18:04:13 +00:00
|
|
|
this.watcher.EnableRaisingEvents = false;
|
|
|
|
this.local_timer.Stop ();
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void EnableWatching ()
|
|
|
|
{
|
2011-12-11 20:00:40 +00:00
|
|
|
lock (this.watch_lock) {
|
2011-11-13 18:04:13 +00:00
|
|
|
this.watcher.EnableRaisingEvents = true;
|
|
|
|
this.local_timer.Start ();
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-28 23:52:46 +00:00
|
|
|
// Create an initial change set when the
|
|
|
|
// user has fetched an empty remote folder
|
|
|
|
public virtual void CreateInitialChangeSet ()
|
|
|
|
{
|
|
|
|
string file_path = Path.Combine (LocalPath, "SparkleShare.txt");
|
|
|
|
TextWriter writer = new StreamWriter (file_path);
|
2012-01-12 00:22:15 +00:00
|
|
|
writer.WriteLine ("Congratulations, you've successfully created a SparkleShare repository!");
|
|
|
|
writer.WriteLine ("");
|
|
|
|
writer.WriteLine ("Any files you add or change in this folder will be automatically synced to ");
|
|
|
|
writer.WriteLine (SparkleConfig.DefaultConfig.GetUrlForFolder (Name) + " and everyone connected to it.");
|
|
|
|
// TODO: Url property? ^
|
|
|
|
|
|
|
|
writer.WriteLine ("");
|
|
|
|
writer.WriteLine ("SparkleShare is a Free and Open Source software program that helps people ");
|
2012-01-12 00:58:19 +00:00
|
|
|
writer.WriteLine ("collaborate and share files. If you like what we do, please consider a small ");
|
2012-01-12 00:22:15 +00:00
|
|
|
writer.WriteLine ("donation to support the project: http://sparkleshare.org/support-us/");
|
|
|
|
writer.WriteLine ("");
|
|
|
|
writer.WriteLine ("Have fun! :)");
|
|
|
|
writer.WriteLine ("");
|
|
|
|
|
2011-05-28 23:52:46 +00:00
|
|
|
writer.Close ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
public void AddNote (string revision, string note)
|
2011-06-20 22:55:18 +00:00
|
|
|
{
|
2011-07-16 22:15:56 +00:00
|
|
|
string notes_path = Path.Combine (LocalPath, ".notes");
|
2011-06-20 22:55:18 +00:00
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
if (!Directory.Exists (notes_path))
|
|
|
|
Directory.CreateDirectory (notes_path);
|
2011-06-20 22:55:18 +00:00
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
// Add a timestamp in seconds since unix epoch
|
|
|
|
int timestamp = (int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds;
|
2011-06-20 22:55:18 +00:00
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
string n = Environment.NewLine;
|
|
|
|
note = "<note>" + n +
|
|
|
|
" <user>" + n +
|
2011-07-24 01:00:40 +00:00
|
|
|
" <name>" + SparkleConfig.DefaultConfig.User.Name + "</name>" + n +
|
|
|
|
" <email>" + SparkleConfig.DefaultConfig.User.Email + "</email>" + n +
|
2011-07-16 22:15:56 +00:00
|
|
|
" </user>" + n +
|
|
|
|
" <timestamp>" + timestamp + "</timestamp>" + n +
|
|
|
|
" <body>" + note + "</body>" + n +
|
|
|
|
"</note>" + n;
|
|
|
|
|
|
|
|
string note_name = revision + SHA1 (timestamp.ToString () + note);
|
|
|
|
string note_path = Path.Combine (notes_path, note_name);
|
2011-06-22 19:38:14 +00:00
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
StreamWriter writer = new StreamWriter (note_path);
|
|
|
|
writer.Write (note);
|
|
|
|
writer.Close ();
|
|
|
|
|
2011-07-17 00:22:39 +00:00
|
|
|
|
|
|
|
// The watcher doesn't like .*/ so we need to trigger
|
|
|
|
// a change manually
|
|
|
|
FileSystemEventArgs args = new FileSystemEventArgs (WatcherChangeTypes.Changed,
|
|
|
|
notes_path, note_name);
|
|
|
|
|
|
|
|
OnFileActivity (args);
|
2011-07-16 22:15:56 +00:00
|
|
|
SparkleHelpers.DebugInfo ("Note", "Added note to " + revision);
|
2011-06-22 19:38:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-15 23:45:17 +00:00
|
|
|
private DateTime progress_last_change = DateTime.Now;
|
|
|
|
private TimeSpan progress_change_interval = new TimeSpan (0, 0, 0, 1);
|
|
|
|
|
2011-12-30 00:44:35 +00:00
|
|
|
protected void OnSyncProgressChanged (double progress_percentage, string progress_speed)
|
|
|
|
{
|
2012-01-15 23:45:17 +00:00
|
|
|
if (DateTime.Compare (this.progress_last_change,
|
|
|
|
DateTime.Now.Subtract (this.progress_change_interval)) < 0) {
|
|
|
|
|
|
|
|
if (SyncProgressChanged != null) {
|
|
|
|
if (progress_percentage == 100.0)
|
|
|
|
progress_percentage = 99.0;
|
2011-12-30 14:00:15 +00:00
|
|
|
|
2012-01-15 23:45:17 +00:00
|
|
|
this.progress_percentage = progress_percentage;
|
|
|
|
this.progress_speed = progress_speed;
|
|
|
|
this.progress_last_change = DateTime.Now;
|
2011-12-30 00:44:35 +00:00
|
|
|
|
2012-01-15 23:45:17 +00:00
|
|
|
SyncProgressChanged (progress_percentage, progress_speed);
|
|
|
|
}
|
|
|
|
}
|
2011-12-30 00:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-16 22:15:56 +00:00
|
|
|
// 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 ("-", "");
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
}
|