2011-06-01 23:08:05 +00:00
|
|
|
// SparkleShare, a collaboration and sharing tool.
|
2017-07-23 12:47:54 +00:00
|
|
|
// Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
|
2011-05-19 16:05:58 +00:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2013-10-11 15:13:46 +00:00
|
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
// License, or (at your option) any later version.
|
2011-05-19 16:05:58 +00:00
|
|
|
//
|
|
|
|
// 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-11-13 00:57:00 +00:00
|
|
|
using System.Threading;
|
2012-06-10 10:57:31 +00:00
|
|
|
|
|
|
|
using Timers = System.Timers;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2016-03-31 08:35:26 +00:00
|
|
|
namespace Sparkles {
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2016-05-31 17:59:35 +00:00
|
|
|
public enum StorageType {
|
|
|
|
Unknown,
|
2016-05-30 03:14:40 +00:00
|
|
|
Plain,
|
2016-06-20 20:19:20 +00:00
|
|
|
LargeFiles,
|
2016-05-31 17:59:35 +00:00
|
|
|
Encrypted
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class StorageTypeInfo {
|
|
|
|
|
|
|
|
public readonly StorageType Type;
|
|
|
|
|
|
|
|
public readonly string Name;
|
|
|
|
public readonly string Description;
|
|
|
|
|
|
|
|
|
|
|
|
public StorageTypeInfo (StorageType storage_type, string name, string description)
|
|
|
|
{
|
|
|
|
Type = storage_type;
|
|
|
|
|
|
|
|
Name = name;
|
|
|
|
Description = description;
|
|
|
|
}
|
2016-05-30 03:14:40 +00:00
|
|
|
}
|
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
public enum SyncStatus {
|
|
|
|
Idle,
|
2014-10-28 15:02:23 +00:00
|
|
|
Paused,
|
2011-05-19 16:05:58 +00:00
|
|
|
SyncUp,
|
|
|
|
SyncDown,
|
|
|
|
Error
|
|
|
|
}
|
|
|
|
|
2012-09-18 18:40:06 +00:00
|
|
|
public enum ErrorStatus {
|
|
|
|
None,
|
|
|
|
HostUnreachable,
|
|
|
|
HostIdentityChanged,
|
|
|
|
AuthenticationFailed,
|
2012-11-23 10:08:50 +00:00
|
|
|
DiskSpaceExceeded,
|
2013-03-11 17:02:30 +00:00
|
|
|
UnreadableFiles,
|
2014-04-22 09:28:50 +00:00
|
|
|
NotFound,
|
2016-06-29 13:44:15 +00:00
|
|
|
IncompatibleClientServer,
|
|
|
|
Unknown
|
2012-09-18 18:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
public abstract class BaseRepository {
|
2012-06-10 10:57:31 +00:00
|
|
|
|
2013-02-09 23:42:32 +00:00
|
|
|
public abstract bool SyncUp ();
|
|
|
|
public abstract bool SyncDown ();
|
|
|
|
public abstract void RestoreFile (string path, string revision, string target_file_path);
|
|
|
|
public abstract bool HasUnsyncedChanges { get; set; }
|
|
|
|
public abstract bool HasLocalChanges { get; }
|
|
|
|
public abstract bool HasRemoteChanges { get; }
|
2012-08-19 10:43:41 +00:00
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
public abstract string CurrentRevision { get; }
|
|
|
|
public abstract double Size { get; }
|
|
|
|
public abstract double HistorySize { get; }
|
2013-02-09 23:42:32 +00:00
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
public abstract List<string> ExcludePaths { get; }
|
2016-03-30 23:56:48 +00:00
|
|
|
public abstract List<Change> UnsyncedChanges { get; }
|
2016-03-30 23:36:31 +00:00
|
|
|
public abstract List<ChangeSet> GetChangeSets ();
|
|
|
|
public abstract List<ChangeSet> GetChangeSets (string path);
|
2013-02-09 23:42:32 +00:00
|
|
|
|
2016-05-31 17:59:35 +00:00
|
|
|
protected StorageType StorageType = StorageType.Plain;
|
2016-05-30 03:14:40 +00:00
|
|
|
|
2013-02-09 23:42:32 +00:00
|
|
|
public static bool UseCustomWatcher = false;
|
|
|
|
|
2011-12-15 15:15:29 +00:00
|
|
|
|
2012-07-18 10:35:22 +00:00
|
|
|
public event SyncStatusChangedEventHandler SyncStatusChanged = delegate { };
|
2011-05-19 16:05:58 +00:00
|
|
|
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
|
|
|
|
|
2012-07-18 10:35:22 +00:00
|
|
|
public event ProgressChangedEventHandler ProgressChanged = delegate { };
|
2012-11-17 23:05:29 +00:00
|
|
|
public delegate void ProgressChangedEventHandler ();
|
2011-12-30 00:44:35 +00:00
|
|
|
|
2012-07-18 10:35:22 +00:00
|
|
|
public event NewChangeSetEventHandler NewChangeSet = delegate { };
|
2016-03-30 23:36:31 +00:00
|
|
|
public delegate void NewChangeSetEventHandler (ChangeSet change_set);
|
2011-05-22 14:46:15 +00:00
|
|
|
|
2012-07-18 10:35:22 +00:00
|
|
|
public event Action ConflictResolved = delegate { };
|
|
|
|
public event Action ChangesDetected = delegate { };
|
2011-05-22 14:46:15 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-02-09 01:46:25 +00:00
|
|
|
public readonly string LocalPath;
|
|
|
|
public readonly string Name;
|
2012-04-29 14:19:36 +00:00
|
|
|
public readonly Uri RemoteUrl;
|
2016-04-04 09:27:20 +00:00
|
|
|
public List<ChangeSet> ChangeSets { get; set; }
|
|
|
|
public SyncStatus Status { get; set; }
|
2012-09-18 18:40:06 +00:00
|
|
|
public ErrorStatus Error { get; protected set; }
|
2016-04-04 09:27:20 +00:00
|
|
|
public bool IsBuffering { get; set; }
|
2016-06-29 13:44:15 +00:00
|
|
|
|
|
|
|
public double ProgressPercentage { get; private set; }
|
|
|
|
public double ProgressSpeed { get; private set; }
|
|
|
|
public string ProgressInformation { get; private set; }
|
2014-11-07 16:55:25 +00:00
|
|
|
|
|
|
|
public DateTime LastSync {
|
|
|
|
get {
|
|
|
|
if (ChangeSets != null && ChangeSets.Count > 0)
|
|
|
|
return ChangeSets [0].Timestamp;
|
|
|
|
else
|
|
|
|
return DateTime.MinValue;
|
|
|
|
}
|
|
|
|
}
|
2012-02-09 01:46:25 +00:00
|
|
|
|
2013-01-08 20:35:01 +00:00
|
|
|
public virtual string Identifier {
|
2012-05-19 15:17:09 +00:00
|
|
|
get {
|
|
|
|
if (this.identifier != null)
|
2012-05-19 15:15:20 +00:00
|
|
|
return this.identifier;
|
|
|
|
|
2012-05-19 15:17:09 +00:00
|
|
|
string id_path = Path.Combine (LocalPath, ".sparkleshare");
|
2012-06-10 10:57:31 +00:00
|
|
|
|
2017-03-01 19:59:44 +00:00
|
|
|
if (File.Exists (id_path)) {
|
|
|
|
File.SetAttributes (id_path, FileAttributes.Hidden);
|
2012-05-19 15:17:09 +00:00
|
|
|
this.identifier = File.ReadAllText (id_path).Trim ();
|
2017-03-01 19:59:44 +00:00
|
|
|
}
|
2012-05-19 15:15:20 +00:00
|
|
|
|
2012-07-05 11:33:32 +00:00
|
|
|
if (!string.IsNullOrEmpty (this.identifier)) {
|
2012-05-19 15:15:20 +00:00
|
|
|
return this.identifier;
|
|
|
|
|
|
|
|
} else {
|
2016-04-06 09:12:49 +00:00
|
|
|
string config_identifier = this.local_config.IdentifierByName (Name);
|
2012-07-05 11:33:32 +00:00
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty (config_identifier))
|
|
|
|
this.identifier = config_identifier;
|
|
|
|
else
|
2016-06-19 06:39:33 +00:00
|
|
|
this.identifier = Path.GetRandomFileName ().SHA256 ();
|
2012-06-10 22:56:39 +00:00
|
|
|
|
2012-05-19 15:17:09 +00:00
|
|
|
File.WriteAllText (id_path, this.identifier);
|
|
|
|
File.SetAttributes (id_path, FileAttributes.Hidden);
|
2012-05-19 15:15:20 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("Local", Name + " | Assigned identifier: " + this.identifier);
|
2012-06-10 22:56:39 +00:00
|
|
|
|
2012-05-19 15:17:09 +00:00
|
|
|
return this.identifier;
|
2012-05-19 15:15:20 +00:00
|
|
|
}
|
2012-05-19 15:17:09 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
protected Configuration local_config;
|
2012-06-10 10:57:31 +00:00
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
string identifier;
|
2017-09-09 11:28:29 +00:00
|
|
|
BaseListener listener = null;
|
2016-04-04 09:27:20 +00:00
|
|
|
Watcher watcher;
|
|
|
|
TimeSpan poll_interval = PollInterval.Short;
|
|
|
|
DateTime last_poll = DateTime.Now;
|
|
|
|
Timers.Timer remote_timer = new Timers.Timer () { Interval = 5000 };
|
|
|
|
DisconnectReason last_disconnect_reason = DisconnectReason.None;
|
2012-07-14 15:18:29 +00:00
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
bool is_syncing {
|
2013-02-09 23:42:32 +00:00
|
|
|
get { return (Status == SyncStatus.SyncUp || Status == SyncStatus.SyncDown || IsBuffering); }
|
2012-06-10 10:57:31 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
static class PollInterval {
|
2012-07-14 15:18:29 +00:00
|
|
|
public static readonly TimeSpan Short = new TimeSpan (0, 0, 5, 0);
|
|
|
|
public static readonly TimeSpan Long = new TimeSpan (0, 0, 15, 0);
|
2012-06-10 22:56:39 +00:00
|
|
|
}
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
public BaseRepository (string path, Configuration config)
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo (path, "Initializing...");
|
2012-11-28 20:17:39 +00:00
|
|
|
|
2012-09-18 18:40:06 +00:00
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
Error = ErrorStatus.None;
|
2012-07-14 10:45:54 +00:00
|
|
|
this.local_config = config;
|
2012-07-17 13:09:57 +00:00
|
|
|
LocalPath = path;
|
|
|
|
Name = Path.GetFileName (LocalPath);
|
2016-04-06 09:12:49 +00:00
|
|
|
RemoteUrl = new Uri (this.local_config.UrlByName (Name));
|
2012-07-17 13:09:57 +00:00
|
|
|
IsBuffering = false;
|
|
|
|
this.identifier = Identifier;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2016-06-18 00:03:40 +00:00
|
|
|
string storage_type = this.local_config.GetFolderOptionalAttribute (Name, "storage_type");
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty (storage_type))
|
2017-02-03 01:06:20 +00:00
|
|
|
StorageType = (StorageType) Enum.Parse (typeof (StorageType), storage_type);
|
2016-06-18 00:03:40 +00:00
|
|
|
|
2014-10-30 18:42:57 +00:00
|
|
|
string is_paused = this.local_config.GetFolderOptionalAttribute (Name, "paused");
|
|
|
|
if (is_paused != null && is_paused.Equals (bool.TrueString))
|
|
|
|
Status = SyncStatus.Paused;
|
|
|
|
|
|
|
|
string identifier_file_path = Path.Combine (LocalPath, ".sparkleshare");
|
|
|
|
File.SetAttributes (identifier_file_path, FileAttributes.Hidden);
|
2012-10-21 20:16:38 +00:00
|
|
|
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
2016-03-30 23:36:31 +00:00
|
|
|
this.watcher = new Watcher (LocalPath);
|
2012-08-19 10:43:41 +00:00
|
|
|
|
2012-07-15 13:59:04 +00:00
|
|
|
new Thread (() => CreateListener ()).Start ();
|
2011-06-28 19:06:04 +00:00
|
|
|
|
2014-04-18 13:06:46 +00:00
|
|
|
this.remote_timer.Elapsed += RemoteTimerElapsedDelegate;
|
|
|
|
}
|
2013-02-09 23:42:32 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void RemoteTimerElapsedDelegate (object sender, EventArgs args)
|
2014-04-18 13:06:46 +00:00
|
|
|
{
|
2014-10-28 15:02:23 +00:00
|
|
|
if (this.is_syncing || IsBuffering || Status == SyncStatus.Paused)
|
2014-04-18 13:06:46 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
int time_comparison = DateTime.Compare (this.last_poll, DateTime.Now.Subtract (this.poll_interval));
|
|
|
|
|
|
|
|
if (time_comparison < 0) {
|
|
|
|
if (HasUnsyncedChanges && !this.is_syncing)
|
2011-05-19 16:05:58 +00:00
|
|
|
SyncUpBase ();
|
2014-04-18 13:06:46 +00:00
|
|
|
|
|
|
|
this.last_poll = DateTime.Now;
|
|
|
|
|
|
|
|
if (HasRemoteChanges && !this.is_syncing)
|
|
|
|
SyncDownBase ();
|
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// if (this.listener.IsConnected)
|
|
|
|
// this.poll_interval = PollInterval.Long;
|
2014-04-18 13:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// In the unlikely case that we haven't synced up our
|
|
|
|
// changes or the server was down, sync up again
|
|
|
|
if (HasUnsyncedChanges && !this.is_syncing && Error == ErrorStatus.None)
|
|
|
|
SyncUpBase ();
|
|
|
|
|
|
|
|
if (Status != SyncStatus.Idle && Status != SyncStatus.Error) {
|
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
|
|
|
}
|
2012-01-18 16:59:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Initialize ()
|
|
|
|
{
|
2017-02-03 01:06:20 +00:00
|
|
|
ChangeSets = GetChangeSets ();
|
|
|
|
|
2013-02-09 23:42:32 +00:00
|
|
|
// Sync up everything that changed since we've been offline
|
2012-10-16 11:21:44 +00:00
|
|
|
new Thread (() => {
|
2014-10-31 11:01:03 +00:00
|
|
|
if (Status != SyncStatus.Paused) {
|
|
|
|
if (HasRemoteChanges)
|
|
|
|
SyncDownBase ();
|
2013-02-09 23:42:32 +00:00
|
|
|
|
2014-10-31 11:01:03 +00:00
|
|
|
if (HasUnsyncedChanges || HasLocalChanges) {
|
|
|
|
do {
|
|
|
|
SyncUpBase ();
|
2013-01-05 13:15:22 +00:00
|
|
|
|
2014-10-31 11:01:03 +00:00
|
|
|
} while (HasLocalChanges);
|
|
|
|
}
|
2012-10-16 11:21:44 +00:00
|
|
|
}
|
2013-02-09 23:42:32 +00:00
|
|
|
|
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.ChangeEvent += OnFileActivity;
|
2012-10-16 11:21:44 +00:00
|
|
|
|
|
|
|
this.remote_timer.Start ();
|
|
|
|
|
|
|
|
}).Start ();
|
2011-05-20 00:55:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
Object buffer_lock = new Object ();
|
2012-09-22 22:09:36 +00:00
|
|
|
|
2011-06-14 22:14:03 +00:00
|
|
|
public void OnFileActivity (FileSystemEventArgs args)
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2014-10-28 21:59:57 +00:00
|
|
|
if (IsBuffering || this.is_syncing)
|
2012-07-14 12:35:55 +00:00
|
|
|
return;
|
2012-06-28 13:53:30 +00:00
|
|
|
|
2012-10-14 18:57:13 +00:00
|
|
|
if (args != null) {
|
|
|
|
foreach (string exclude_path in ExcludePaths) {
|
2012-11-04 18:37:38 +00:00
|
|
|
if (args.FullPath.Contains (Path.DirectorySeparatorChar + exclude_path))
|
2012-10-14 18:57:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-07-14 12:35:55 +00:00
|
|
|
}
|
2014-10-28 21:59:57 +00:00
|
|
|
|
|
|
|
if (Status == SyncStatus.Paused) {
|
|
|
|
ChangesDetected ();
|
|
|
|
return;
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-09-22 22:09:36 +00:00
|
|
|
lock (this.buffer_lock) {
|
2012-10-26 12:43:54 +00:00
|
|
|
if (IsBuffering || this.is_syncing || !HasLocalChanges)
|
2012-09-22 22:09:36 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
IsBuffering = true;
|
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-09-22 22:09:36 +00:00
|
|
|
ChangesDetected ();
|
2012-08-19 10:43:41 +00:00
|
|
|
|
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Disable ();
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("Local", Name + " | Activity detected, waiting for it to settle...");
|
2011-07-20 23:02:21 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
List<double> size_buffer = new List<double> ();
|
2013-01-06 11:21:54 +00:00
|
|
|
DirectoryInfo info = new DirectoryInfo (LocalPath);
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
do {
|
|
|
|
if (size_buffer.Count >= 4)
|
|
|
|
size_buffer.RemoveAt (0);
|
2012-06-10 00:16:28 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
size_buffer.Add (CalculateSize (info));
|
2012-06-10 00:16:28 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
if (size_buffer.Count >= 4 &&
|
|
|
|
size_buffer [0].Equals (size_buffer [1]) &&
|
|
|
|
size_buffer [1].Equals (size_buffer [2]) &&
|
|
|
|
size_buffer [2].Equals (size_buffer [3])) {
|
2012-06-10 00:16:28 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("Local", Name + " | Activity has settled");
|
2012-07-14 12:35:55 +00:00
|
|
|
IsBuffering = false;
|
2012-07-13 12:19:46 +00:00
|
|
|
|
2013-05-29 11:09:39 +00:00
|
|
|
bool first_sync = true;
|
|
|
|
|
|
|
|
if (HasLocalChanges && Status == SyncStatus.Idle) {
|
2012-07-14 12:35:55 +00:00
|
|
|
do {
|
2013-05-29 11:09:39 +00:00
|
|
|
if (!first_sync)
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("Local", Name + " | More changes found");
|
2013-05-29 11:09:39 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
SyncUpBase ();
|
2012-07-13 12:19:46 +00:00
|
|
|
|
2013-03-11 17:02:30 +00:00
|
|
|
if (Error == ErrorStatus.UnreadableFiles)
|
|
|
|
return;
|
|
|
|
|
2013-05-29 11:09:39 +00:00
|
|
|
first_sync = false;
|
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
} while (HasLocalChanges);
|
2013-06-13 16:51:24 +00:00
|
|
|
}
|
2012-06-10 00:16:28 +00:00
|
|
|
|
2013-06-13 17:17:04 +00:00
|
|
|
if (Status != SyncStatus.Idle && Status != SyncStatus.Error) {
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
2012-07-14 12:35:55 +00:00
|
|
|
}
|
2012-06-10 00:16:28 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
} else {
|
|
|
|
Thread.Sleep (500);
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
2012-06-28 13:53:30 +00:00
|
|
|
|
2012-07-14 12:35:55 +00:00
|
|
|
} while (IsBuffering);
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Enable ();
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-17 19:03:37 +00:00
|
|
|
public void ForceRetry ()
|
|
|
|
{
|
2014-10-31 11:01:03 +00:00
|
|
|
if (Error != ErrorStatus.None && !this.is_syncing)
|
|
|
|
SyncUpBase ();
|
2012-11-17 19:03:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
protected void OnConflictResolved ()
|
|
|
|
{
|
2012-07-18 10:35:22 +00:00
|
|
|
ConflictResolved ();
|
2012-06-10 10:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
DateTime progress_last_change = DateTime.Now;
|
|
|
|
|
|
|
|
protected void OnProgressChanged (double percentage, double speed, string information)
|
2012-07-17 13:09:57 +00:00
|
|
|
{
|
2016-06-29 13:44:15 +00:00
|
|
|
if (percentage < 1)
|
2012-09-19 18:57:57 +00:00
|
|
|
return;
|
|
|
|
|
2012-07-17 13:09:57 +00:00
|
|
|
// Only trigger the ProgressChanged event once per second
|
2013-02-09 23:42:32 +00:00
|
|
|
if (DateTime.Compare (this.progress_last_change, DateTime.Now.Subtract (new TimeSpan (0, 0, 0, 1))) >= 0)
|
2012-07-17 13:09:57 +00:00
|
|
|
return;
|
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
if (percentage == 100.0)
|
|
|
|
percentage = 99.0;
|
|
|
|
|
|
|
|
progress_last_change = DateTime.Now;
|
2012-07-17 13:09:57 +00:00
|
|
|
|
2016-06-29 13:44:15 +00:00
|
|
|
ProgressPercentage = percentage;
|
|
|
|
ProgressSpeed = speed;
|
|
|
|
ProgressInformation = information;
|
2012-07-17 13:09:57 +00:00
|
|
|
|
2012-11-17 23:05:29 +00:00
|
|
|
ProgressChanged ();
|
2012-07-17 13:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void SyncUpBase ()
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Disable ();
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncUp", Name + " | Initiated");
|
2012-07-14 15:18:29 +00:00
|
|
|
HasUnsyncedChanges = true;
|
|
|
|
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.SyncUp;
|
|
|
|
SyncStatusChanged (Status);
|
2012-07-14 15:18:29 +00:00
|
|
|
|
|
|
|
if (SyncUp ()) {
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncUp", Name + " | Done");
|
2012-07-19 20:21:38 +00:00
|
|
|
ChangeSets = GetChangeSets ();
|
|
|
|
|
2012-07-25 09:25:52 +00:00
|
|
|
HasUnsyncedChanges = false;
|
2012-09-29 10:35:36 +00:00
|
|
|
this.poll_interval = PollInterval.Long;
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener.Announce (new Announcement (Identifier, CurrentRevision));
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
|
|
|
|
2012-07-14 15:18:29 +00:00
|
|
|
} else {
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncUp", Name + " | Error");
|
2012-07-14 15:18:29 +00:00
|
|
|
SyncDownBase ();
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Disable ();
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2012-09-18 18:40:06 +00:00
|
|
|
if (Error == ErrorStatus.None && SyncUp ()) {
|
2011-05-19 16:05:58 +00:00
|
|
|
HasUnsyncedChanges = false;
|
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener.Announce (new Announcement (Identifier, CurrentRevision));
|
2013-05-30 12:05:33 +00:00
|
|
|
|
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
2011-05-22 16:51:48 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
} else {
|
2012-09-29 10:35:36 +00:00
|
|
|
this.poll_interval = PollInterval.Short;
|
2013-05-30 12:05:33 +00:00
|
|
|
|
|
|
|
Status = SyncStatus.Error;
|
|
|
|
SyncStatusChanged (Status);
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
2012-07-14 15:18:29 +00:00
|
|
|
}
|
2011-05-22 16:51:48 +00:00
|
|
|
|
2012-07-14 15:18:29 +00:00
|
|
|
ProgressPercentage = 0.0;
|
2012-11-17 23:05:29 +00:00
|
|
|
ProgressSpeed = 0.0;
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Enable ();
|
2014-10-28 15:12:37 +00:00
|
|
|
|
|
|
|
this.status_message = "";
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void SyncDownBase ()
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Disable ();
|
2012-07-25 09:25:52 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncDown", Name + " | Initiated");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.SyncDown;
|
|
|
|
SyncStatusChanged (Status);
|
|
|
|
|
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 ()) {
|
2012-09-18 18:40:06 +00:00
|
|
|
Error = ErrorStatus.None;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2013-06-13 16:51:24 +00:00
|
|
|
string identifier_file_path = Path.Combine (LocalPath, ".sparkleshare");
|
|
|
|
File.SetAttributes (identifier_file_path, FileAttributes.Hidden);
|
2012-10-21 20:16:38 +00:00
|
|
|
|
2012-07-19 20:21:38 +00:00
|
|
|
ChangeSets = GetChangeSets ();
|
2012-05-27 18:24:12 +00:00
|
|
|
|
2013-01-20 12:40:24 +00:00
|
|
|
if (!pre_sync_revision.Equals (CurrentRevision) &&
|
|
|
|
ChangeSets != null && ChangeSets.Count > 0 &&
|
|
|
|
!ChangeSets [0].User.Name.Equals (this.local_config.User.Name)) {
|
|
|
|
|
2012-07-19 20:21:38 +00:00
|
|
|
bool emit_change_event = true;
|
|
|
|
|
2016-03-30 23:56:48 +00:00
|
|
|
foreach (Change change in ChangeSets [0].Changes) {
|
2012-07-19 20:21:38 +00:00
|
|
|
if (change.Path.EndsWith (".sparkleshare")) {
|
|
|
|
emit_change_event = false;
|
|
|
|
break;
|
2012-07-18 10:35:22 +00:00
|
|
|
}
|
2011-08-26 20:00:33 +00:00
|
|
|
}
|
2012-07-19 20:21:38 +00:00
|
|
|
|
|
|
|
if (emit_change_event)
|
|
|
|
NewChangeSet (ChangeSets [0]);
|
2011-07-23 21:23:14 +00:00
|
|
|
}
|
2011-06-03 00:33:41 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncDown", Name + " | Done");
|
2013-06-13 16:51:24 +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
|
2012-03-16 01:53:23 +00:00
|
|
|
if (HasUnsyncedChanges) {
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.SyncUp;
|
|
|
|
SyncStatusChanged (Status);
|
2012-05-19 15:17:09 +00:00
|
|
|
|
2013-02-09 23:42:32 +00:00
|
|
|
if (SyncUp ())
|
|
|
|
HasUnsyncedChanges = false;
|
2012-05-19 15:17:09 +00:00
|
|
|
}
|
2012-07-18 10:35:22 +00:00
|
|
|
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
2011-05-22 17:49:04 +00:00
|
|
|
|
2011-05-19 16:05:58 +00:00
|
|
|
} else {
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("SyncDown", Name + " | Error");
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2012-07-19 20:21:38 +00:00
|
|
|
ChangeSets = GetChangeSets ();
|
2013-05-30 12:05:33 +00:00
|
|
|
|
|
|
|
Status = SyncStatus.Error;
|
|
|
|
SyncStatusChanged (Status);
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
ProgressPercentage = 0.0;
|
2012-11-17 23:05:29 +00:00
|
|
|
ProgressSpeed = 0.0;
|
2012-03-29 00:44:26 +00:00
|
|
|
|
2013-05-30 12:05:33 +00:00
|
|
|
Status = SyncStatus.Idle;
|
|
|
|
SyncStatusChanged (Status);
|
2013-02-09 23:42:32 +00:00
|
|
|
|
2012-08-19 10:43:41 +00:00
|
|
|
if (!UseCustomWatcher)
|
|
|
|
this.watcher.Enable ();
|
2012-06-10 10:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void CreateListener ()
|
2011-05-19 16:05:58 +00:00
|
|
|
{
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener = ListenerFactory.CreateListener (Name, Identifier);
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// if (this.listener.IsConnected)
|
|
|
|
// this.poll_interval = PollInterval.Long;
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener.Connected += ListenerConnectedDelegate;
|
|
|
|
// this.listener.Disconnected += ListenerDisconnectedDelegate;
|
|
|
|
// this.listener.AnnouncementReceived += ListenerAnnouncementReceivedDelegate;
|
2011-05-19 16:05:58 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// if (!this.listener.IsConnected && !this.listener.IsConnecting)
|
|
|
|
// this.listener.Connect ();
|
2012-07-05 11:33:32 +00:00
|
|
|
}
|
2012-01-12 00:22:15 +00:00
|
|
|
|
2014-04-18 13:06:46 +00:00
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void ListenerConnectedDelegate ()
|
2014-04-18 13:06:46 +00:00
|
|
|
{
|
|
|
|
if (this.last_disconnect_reason == DisconnectReason.SystemSleep) {
|
|
|
|
this.last_disconnect_reason = DisconnectReason.None;
|
|
|
|
|
|
|
|
if (HasRemoteChanges && !this.is_syncing)
|
|
|
|
SyncDownBase ();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.poll_interval = PollInterval.Long;
|
|
|
|
}
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void ListenerDisconnectedDelegate (DisconnectReason reason)
|
2012-07-05 11:33:32 +00:00
|
|
|
{
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo (Name, "Falling back to regular polling");
|
2014-04-18 13:06:46 +00:00
|
|
|
this.poll_interval = PollInterval.Short;
|
|
|
|
|
|
|
|
this.last_disconnect_reason = reason;
|
|
|
|
|
|
|
|
if (reason == DisconnectReason.SystemSleep) {
|
|
|
|
this.remote_timer.Stop ();
|
|
|
|
|
|
|
|
int backoff_time = 2;
|
|
|
|
|
|
|
|
do {
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo (Name, "Next reconnect attempt in " + backoff_time + " seconds");
|
2014-04-18 13:06:46 +00:00
|
|
|
Thread.Sleep (backoff_time * 1000);
|
|
|
|
this.listener.Connect ();
|
|
|
|
backoff_time *= 2;
|
|
|
|
|
|
|
|
} while (backoff_time < 64 && !this.listener.IsConnected);
|
|
|
|
|
|
|
|
this.remote_timer.Start ();
|
|
|
|
}
|
2012-07-05 11:33:32 +00:00
|
|
|
}
|
2012-02-08 19:42:29 +00:00
|
|
|
|
|
|
|
|
2016-04-04 09:27:20 +00:00
|
|
|
void ListenerAnnouncementReceivedDelegate (Announcement announcement)
|
2012-07-05 11:33:32 +00:00
|
|
|
{
|
|
|
|
string identifier = Identifier;
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2013-02-09 23:42:32 +00:00
|
|
|
if (!announcement.FolderIdentifier.Equals (identifier))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!announcement.Message.Equals (CurrentRevision)) {
|
2012-07-05 11:33:32 +00:00
|
|
|
while (this.is_syncing)
|
2012-07-08 21:53:44 +00:00
|
|
|
Thread.Sleep (100);
|
2012-07-05 11:33:32 +00:00
|
|
|
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo (Name, "Syncing due to announcement");
|
2014-10-28 15:02:23 +00:00
|
|
|
|
|
|
|
if (Status == SyncStatus.Paused)
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo (Name, "We're paused, skipping sync");
|
2014-10-28 15:02:23 +00:00
|
|
|
else
|
|
|
|
SyncDownBase ();
|
2012-07-05 11:33:32 +00:00
|
|
|
}
|
2011-05-28 23:52:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-08 19:42:29 +00:00
|
|
|
// Recursively gets a folder's size in bytes
|
2016-04-04 09:27:20 +00:00
|
|
|
long CalculateSize (DirectoryInfo parent)
|
2012-02-08 19:42:29 +00:00
|
|
|
{
|
|
|
|
if (ExcludePaths.Contains (parent.Name))
|
|
|
|
return 0;
|
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
long size = 0;
|
2012-02-08 19:42:29 +00:00
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
try {
|
2012-02-08 19:42:29 +00:00
|
|
|
foreach (DirectoryInfo directory in parent.GetDirectories ())
|
|
|
|
size += CalculateSize (directory);
|
|
|
|
|
2013-01-11 18:58:14 +00:00
|
|
|
foreach (FileInfo file in parent.GetFiles ())
|
|
|
|
size += file.Length;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
2016-03-30 23:36:31 +00:00
|
|
|
Logger.LogInfo ("Local", "Error calculating directory size", e);
|
2012-02-08 19:42:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
2012-06-10 10:57:31 +00:00
|
|
|
|
|
|
|
|
2014-10-28 15:12:37 +00:00
|
|
|
public void Pause ()
|
|
|
|
{
|
2014-10-30 18:42:57 +00:00
|
|
|
if (Status == SyncStatus.Idle) {
|
|
|
|
this.local_config.SetFolderOptionalAttribute (Name, "paused", bool.TrueString);
|
2014-10-28 15:12:37 +00:00
|
|
|
Status = SyncStatus.Paused;
|
2014-10-30 18:42:57 +00:00
|
|
|
}
|
2014-10-28 15:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected string status_message = "";
|
|
|
|
|
|
|
|
public void Resume (string message)
|
|
|
|
{
|
|
|
|
this.status_message = message;
|
|
|
|
|
2014-10-30 18:42:57 +00:00
|
|
|
if (Status == SyncStatus.Paused) {
|
|
|
|
this.local_config.SetFolderOptionalAttribute (Name, "paused", bool.FalseString);
|
2014-10-28 15:12:37 +00:00
|
|
|
Status = SyncStatus.Idle;
|
2014-10-31 11:01:03 +00:00
|
|
|
|
2014-10-31 16:37:13 +00:00
|
|
|
if (HasUnsyncedChanges || HasLocalChanges) {
|
|
|
|
do {
|
|
|
|
SyncUpBase ();
|
|
|
|
|
|
|
|
} while (HasLocalChanges);
|
|
|
|
}
|
2014-10-30 18:42:57 +00:00
|
|
|
}
|
2014-10-28 15:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-10 10:57:31 +00:00
|
|
|
public void Dispose ()
|
|
|
|
{
|
2015-10-13 15:02:22 +00:00
|
|
|
if (remote_timer != null) {
|
|
|
|
this.remote_timer.Elapsed -= RemoteTimerElapsedDelegate;
|
|
|
|
this.remote_timer.Stop ();
|
|
|
|
this.remote_timer.Dispose ();
|
|
|
|
this.remote_timer = null;
|
|
|
|
}
|
2012-07-05 11:33:32 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener.Disconnected -= ListenerDisconnectedDelegate;
|
|
|
|
// this.listener.AnnouncementReceived -= ListenerAnnouncementReceivedDelegate;
|
2012-07-15 10:05:58 +00:00
|
|
|
|
2017-09-09 11:28:29 +00:00
|
|
|
// this.listener.Dispose ();
|
2015-01-13 22:18:47 +00:00
|
|
|
|
2018-03-08 10:29:48 +00:00
|
|
|
if (!UseCustomWatcher && this.watcher != null)
|
2012-08-19 10:43:41 +00:00
|
|
|
this.watcher.Dispose ();
|
2012-06-10 10:57:31 +00:00
|
|
|
}
|
2011-05-19 16:05:58 +00:00
|
|
|
}
|
|
|
|
}
|