diff --git a/SparkleLib/Git/SparkleRepoGit.cs b/SparkleLib/Git/SparkleRepoGit.cs index 9e80cc23..0adff153 100644 --- a/SparkleLib/Git/SparkleRepoGit.cs +++ b/SparkleLib/Git/SparkleRepoGit.cs @@ -771,49 +771,6 @@ namespace SparkleLib.Git { return change_sets; } - private string EnsureSpecialCharacters (string file_path) - { - if (file_path.StartsWith("\"")) - { - System.Diagnostics.Debug.Assert(file_path.Length > 2 && file_path.EndsWith("\""), "unexpected path"); - file_path = ResolveSpecialChars(file_path.Substring(1, file_path.Length - 2)); - } - return file_path; - } - - /// - /// Resolves special characters like \303\244 (ä) to their real character - /// - /// - /// - private string ResolveSpecialChars (string file_path) - { - var builder = new System.Text.StringBuilder(file_path.Length); - var codes = new List(); - for (int i = 0; i < file_path.Length; i++) - { - while (file_path[i] == '\\' - && file_path.Length - i > 3 - && char.IsNumber(file_path[i + 1]) - && char.IsNumber(file_path[i + 2]) - && char.IsNumber(file_path[i + 3])) - { - codes.Add (Convert.ToByte(file_path.Substring(i + 1, 3), 8)); - i += 4; - } - - if (codes.Count > 0) - { - builder.Append(System.Text.Encoding.UTF8.GetString (codes.ToArray ())); - codes.Clear (); - } - - builder.Append(file_path[i]); - } - return builder.ToString(); - //System.Text.Encoding.UTF8. - } - private string EnsureSpecialCharacters (string path) { @@ -825,11 +782,6 @@ namespace SparkleLib.Git { } - /// - /// Resolves special characters like \303\244 (ä) to their real character - /// - /// - /// private string ResolveSpecialChars (string s) { StringBuilder builder = new StringBuilder (s.Length); diff --git a/SparkleLib/SparkleConfig.cs b/SparkleLib/SparkleConfig.cs index 0c441358..6cbcc3f8 100755 --- a/SparkleLib/SparkleConfig.cs +++ b/SparkleLib/SparkleConfig.cs @@ -248,6 +248,15 @@ namespace SparkleLib { } + public void RenameFolder (string identifier, string name) + { + XmlNode node_folder = SelectSingleNode (string.Format ("/sparkleshare/folder[identifier=\"{0}\"]", identifier)); + node_folder ["name"].InnerText = name; + + Save (); + } + + public string GetBackendForFolder (string name) { return GetFolderValue (name, "backend"); @@ -266,6 +275,17 @@ namespace SparkleLib { } + public bool IdentifierExists (string identifier) + { + foreach (XmlNode node_folder in SelectNodes ("/sparkleshare/folder")) { + if (node_folder ["identifier"].InnerText.Equals (identifier)) + return true; + } + + return false; + } + + public bool SetFolderOptionalAttribute (string folder_name, string key, string value) { XmlNode folder = GetFolder (folder_name); diff --git a/SparkleLib/SparkleFetcherBase.cs b/SparkleLib/SparkleFetcherBase.cs index 231ce74d..8f1730dd 100755 --- a/SparkleLib/SparkleFetcherBase.cs +++ b/SparkleLib/SparkleFetcherBase.cs @@ -50,7 +50,7 @@ namespace SparkleLib { public readonly bool FetchPriorHistory = false; public string TargetFolder { get; protected set; } public bool IsActive { get; private set; } - public string Identifier = CreateIdentifier (); + public string Identifier; public string [] Warnings { get { @@ -66,13 +66,34 @@ namespace SparkleLib { protected List warnings = new List (); - protected List errors = new List (); + protected List errors = new List (); + + protected string [] ExcludeRules = new string [] { + "*.autosave", // Various autosaving apps + "*~", // gedit and emacs + ".~lock.*", // LibreOffice + "*.part", "*.crdownload", // Firefox and Chromium temporary download files + ".*.sw[a-z]", "*.un~", "*.swp", "*.swo", // vi(m) + ".directory", // KDE + ".DS_Store", "Icon\r\r", "._*", ".Spotlight-V100", ".Trashes", // Mac OS X + "*(Autosaved).graffle", // Omnigraffle + "Thumbs.db", "Desktop.ini", // Windows + "~*.tmp", "~*.TMP", "*~*.tmp", "*~*.TMP", // MS Office + "~*.ppt", "~*.PPT", "~*.pptx", "~*.PPTX", + "~*.xls", "~*.XLS", "~*.xlsx", "~*.XLSX", + "~*.doc", "~*.DOC", "~*.docx", "~*.DOCX", + "*/CVS/*", ".cvsignore", "*/.cvsignore", // CVS + "/.svn/*", "*/.svn/*", // Subversion + "/.hg/*", "*/.hg/*", "*/.hgignore", // Mercurial + "/.bzr/*", "*/.bzr/*", "*/.bzrignore" // Bazaar + }; + private Thread thread; - public SparkleFetcherBase (string server, string required_fingerprint, string remote_path, - string target_folder, bool fetch_prior_history) + public SparkleFetcherBase (string server, string required_fingerprint, + string remote_path, string target_folder, bool fetch_prior_history) { RequiredFingerprint = required_fingerprint; FetchPriorHistory = fetch_prior_history; @@ -105,7 +126,6 @@ namespace SparkleLib { if (Directory.Exists (TargetFolder)) Directory.Delete (TargetFolder, true); - string host = RemoteUrl.Host; string host_key = GetHostKey (); @@ -116,7 +136,6 @@ namespace SparkleLib { return; } - bool warn = true; if (RequiredFingerprint != null) { string host_fingerprint = GetFingerprint (host_key); @@ -141,30 +160,31 @@ namespace SparkleLib { AcceptHostKey (host_key, warn); + this.thread = new Thread ( + new ThreadStart (delegate { + if (Fetch ()) { + Thread.Sleep (500); + SparkleHelpers.DebugInfo ("Fetcher", "Finished"); - this.thread = new Thread (new ThreadStart (delegate { - if (Fetch ()) { - Thread.Sleep (500); - SparkleHelpers.DebugInfo ("Fetcher", "Finished"); + IsActive = false; - IsActive = false; + // TODO: Find better way to determine if folder should have crypto setup + bool repo_is_encrypted = RemoteUrl.ToString ().Contains ("crypto"); - // TODO: Find better way to determine if folder should have crypto setup - bool repo_is_encrypted = RemoteUrl.ToString ().Contains ("crypto"); + if (Finished != null) + Finished (repo_is_encrypted, IsFetchedRepoEmpty, Warnings); - if (Finished != null) - Finished (repo_is_encrypted, IsFetchedRepoEmpty, Warnings); + } else { + Thread.Sleep (500); + SparkleHelpers.DebugInfo ("Fetcher", "Failed"); - } else { - Thread.Sleep (500); - SparkleHelpers.DebugInfo ("Fetcher", "Failed"); + IsActive = false; - IsActive = false; - - if (Failed != null) - Failed (); - } - })); + if (Failed != null) + Failed (); + } + }) + ); this.thread.Start (); } @@ -174,8 +194,13 @@ namespace SparkleLib { { string identifier_path = Path.Combine (TargetFolder, ".sparkleshare"); - if (!File.Exists (identifier_path)) + if (File.Exists (identifier_path)) { + Identifier = File.ReadAllText (identifier_path).Trim (); + + } else { + Identifier = CreateIdentifier (); File.WriteAllText (identifier_path, Identifier); + } if (IsFetchedRepoEmpty) CreateInitialChangeSet (); @@ -204,6 +229,32 @@ namespace SparkleLib { } + public static string CreateIdentifier () + { + Random random = new Random (); + string number = "" + random.Next () + "" + random.Next () + "" + random.Next (); + + return SparkleHelpers.SHA1 (number); + } + + + public static string GetBackend (string path) + { + string extension = Path.GetExtension (path); + + if (!string.IsNullOrEmpty (extension)) { + extension = extension.Substring (1); + char [] letters = extension.ToCharArray (); + letters [0] = char.ToUpper (letters [0]); + + return new string (letters); + + } else { + return "Git"; + } + } + + public void Dispose () { if (this.thread != null) { @@ -213,15 +264,6 @@ namespace SparkleLib { } - public static string CreateIdentifier () - { - Random random = new Random (); - string number = "" + random.Next () + "" + random.Next () + "" + random.Next (); - - return SparkleHelpers.SHA1 (number); - } - - protected void OnProgressChanged (double percentage) { if (ProgressChanged != null) ProgressChanged (percentage); @@ -242,7 +284,7 @@ namespace SparkleLib { process.StartInfo.RedirectStandardOutput = true; process.StartInfo.CreateNoWindow = true; - process.StartInfo.FileName = "ssh-keyscan"; + process.StartInfo.FileName = "ssh-keyscan"; process.StartInfo.Arguments = "-t rsa " + host; process.Start (); @@ -283,7 +325,7 @@ namespace SparkleLib { // WaitForExit, or it will hang forever on output > 4096 bytes string fingerprint = process.StandardOutput.ReadToEnd ().Trim (); process.WaitForExit (); - + File.Delete (tmp_file_path); try { @@ -329,98 +371,5 @@ namespace SparkleLib { if (warn) this.warnings.Add ("The following host key has been accepted:\n" + GetFingerprint (host_key)); } - - - public static string GetBackend (string path) - { - string extension = Path.GetExtension (path); - - if (!string.IsNullOrEmpty (extension)) { - extension = extension.Substring (1); - char [] letters = extension.ToCharArray (); - letters [0] = char.ToUpper (letters [0]); - - return new string (letters); - - } else { - return "Git"; - } - } - - - protected string [] ExcludeRules = new string [] { - // Various autosaving apps - "*.autosave", - - // gedit and emacs - "*~", - - // LibreOffice - ".~lock.*", - - // Firefox and Chromium temporary download files - "*.part", - "*.crdownload", - - // vi(m) - ".*.sw[a-z]", - "*.un~", - "*.swp", - "*.swo", - - // KDE - ".directory", - - // Mac OS X - ".DS_Store", - "Icon\r\r", - "._*", - ".Spotlight-V100", - ".Trashes", - - // Omnigraffle - "*(Autosaved).graffle", - - // Windows - "Thumbs.db", - "Desktop.ini", - - // MS Office - "~*.tmp", - "~*.TMP", - "*~*.tmp", - "*~*.TMP", - "~*.ppt", - "~*.PPT", - "~*.pptx", - "~*.PPTX", - "~*.xls", - "~*.XLS", - "~*.xlsx", - "~*.XLSX", - "~*.doc", - "~*.DOC", - "~*.docx", - "~*.DOCX", - - // CVS - "*/CVS/*", - ".cvsignore", - "*/.cvsignore", - - // Subversion - "/.svn/*", - "*/.svn/*", - - // Mercurial - "/.hg/*", - "*/.hg/*", - "*/.hgignore", - - // Bazaar - "/.bzr/*", - "*/.bzr/*", - "*/.bzrignore" - }; } } diff --git a/SparkleShare/SparkleControllerBase.cs b/SparkleShare/SparkleControllerBase.cs index b79fb370..674a4385 100644 --- a/SparkleShare/SparkleControllerBase.cs +++ b/SparkleShare/SparkleControllerBase.cs @@ -34,12 +34,8 @@ namespace SparkleShare { public SparkleRepoBase [] Repositories { get { - lock (this.repo_lock) { - SparkleRepoBase [] repositories = - this.repositories.GetRange (0, this.repositories.Count).ToArray (); - - return repositories; - } + lock (this.repo_lock) + return this.repositories.GetRange (0, this.repositories.Count).ToArray (); } } @@ -170,8 +166,8 @@ namespace SparkleShare { private SparkleFetcherBase fetcher; - private Object repo_lock = new Object (); - private Object delete_watcher_lock = new Object (); + private Object repo_lock = new Object (); + private Object check_repos_lock = new Object (); // Short alias for the translations @@ -186,6 +182,110 @@ namespace SparkleShare { } + public void HandleInvite (FileSystemEventArgs args) + { + if (this.fetcher != null && + this.fetcher.IsActive) { + + if (AlertNotificationRaised != null) + AlertNotificationRaised ("SparkleShare Setup seems busy", + "Please wait for it to finish"); + + } else { + if (InviteReceived != null) { + SparkleInvite invite = new SparkleInvite (args.FullPath); + + // It may be that the invite we received a path to isn't + // fully downloaded yet, so we try to read it several times + int tries = 0; + while (!invite.IsValid) { + Thread.Sleep (1 * 250); + invite = new SparkleInvite (args.FullPath); + tries++; + if (tries > 20) + break; + } + + if (invite.IsValid) { + InviteReceived (invite); + + } else { + if (AlertNotificationRaised != null) + AlertNotificationRaised ("Oh noes!", + "This invite seems screwed up..."); + } + + File.Delete (args.FullPath); + } + } + } + + + public void CheckRepositories () + { + lock (this.check_repos_lock) { + string path = SparkleConfig.DefaultConfig.FoldersPath; + + foreach (string folder_path in Directory.GetDirectories (path)) { + string folder_name = Path.GetFileName (folder_path); + + if (folder_name.Equals (".tmp")) + continue; + + if (SparkleConfig.DefaultConfig.GetIdentifierForFolder (folder_name) == null) { + string identifier_file_path = Path.Combine (folder_path, ".sparkleshare"); + + if (!File.Exists (identifier_file_path)) + continue; + + string identifier = File.ReadAllText (identifier_file_path).Trim (); + + if (SparkleConfig.DefaultConfig.IdentifierExists (identifier)) { + RemoveRepository (folder_path); + SparkleConfig.DefaultConfig.RenameFolder (identifier, folder_name); + + string new_folder_path = Path.Combine (path, folder_name); + AddRepository (new_folder_path); + + SparkleHelpers.DebugInfo ("Controller", + "Renamed folder with identifier " + identifier + " to '" + folder_name + "'"); + } + } + } + + foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) { + string folder_path = new SparkleFolder (folder_name).FullPath; + + if (!Directory.Exists (folder_path)) { + SparkleConfig.DefaultConfig.RemoveFolder (folder_name); + RemoveRepository (folder_path); + + SparkleHelpers.DebugInfo ("Controller", + "Removed folder '" + folder_name + "' from config"); + } + } + + if (FolderListChanged != null) + FolderListChanged (); + } + } + + + public void OnFolderActivity (object o, FileSystemEventArgs args) + { + if (args != null && + args.ChangeType == WatcherChangeTypes.Created && + args.FullPath.EndsWith (".xml")) { + + HandleInvite (args); + return; + + } else { + CheckRepositories (); + } + } + + public virtual void Initialize () { SparklePlugin.PluginsPath = PluginsPath; @@ -201,69 +301,17 @@ namespace SparkleShare { ImportPrivateKey (); // Watch the SparkleShare folder - FileSystemWatcher watcher = new FileSystemWatcher (SparkleConfig.DefaultConfig.FoldersPath) { + FileSystemWatcher watcher = new FileSystemWatcher () { + Filter = "*", IncludeSubdirectories = false, - EnableRaisingEvents = true, - Filter = "*" + Path = SparkleConfig.DefaultConfig.FoldersPath }; - watcher.Deleted += delegate (object o, FileSystemEventArgs args) { - lock (this.delete_watcher_lock) { - foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) { - string folder_path = new SparkleFolder (folder_name).FullPath; + watcher.Deleted += OnFolderActivity; + watcher.Created += OnFolderActivity; + watcher.Renamed += OnFolderActivity; - if (!Directory.Exists (folder_path)) { - SparkleConfig.DefaultConfig.RemoveFolder (folder_name); - RemoveRepository (folder_path); - } - } - - if (FolderListChanged != null) - FolderListChanged (); - } - }; - - watcher.Created += delegate (object o, FileSystemEventArgs args) { - if (!args.FullPath.EndsWith (".xml")) - return; - - if (this.fetcher != null && - this.fetcher.IsActive) { - - if (AlertNotificationRaised != null) - AlertNotificationRaised ("SparkleShare Setup seems busy", - "Please wait for it to finish"); - - } else { - if (InviteReceived != null) { - SparkleInvite invite = new SparkleInvite (args.FullPath); - - // It may be that the invite we received a path to isn't - // fully downloaded yet, so we try to read it several times - int tries = 0; - while (!invite.IsValid) { - Thread.Sleep (1 * 250); - invite = new SparkleInvite (args.FullPath); - tries++; - if (tries > 20) - break; - } - - if (invite.IsValid) { - InviteReceived (invite); - - } else { - invite = null; - - if (AlertNotificationRaised != null) - AlertNotificationRaised ("Oh noes!", - "This invite seems screwed up..."); - } - - File.Delete (args.FullPath); - } - } - }; + watcher.EnableRaisingEvents = true; } @@ -327,11 +375,9 @@ namespace SparkleShare { string path = new SparkleFolder (name).FullPath; - lock (this.repo_lock) { - foreach (SparkleRepoBase repo in Repositories) { - if (repo.LocalPath.Equals (path)) - return repo.ChangeSets; - } + foreach (SparkleRepoBase repo in Repositories) { + if (repo.LocalPath.Equals (path)) + return repo.ChangeSets; } return null; @@ -511,7 +557,6 @@ namespace SparkleShare { } - // Adds a repository to the list of repositories private void AddRepository (string folder_path) { SparkleRepoBase repo = null; @@ -531,16 +576,8 @@ namespace SparkleShare { return; } - - repo.NewChangeSet += delegate (SparkleChangeSet change_set) { - if (NotificationRaised != null) - NotificationRaised (change_set); - }; - - repo.ConflictResolved += delegate { - if (AlertNotificationRaised != null) - AlertNotificationRaised ("Conflict detected", - "Don't worry, SparkleShare made a copy of each conflicting file."); + repo.ChangesDetected += delegate { + UpdateState (); }; repo.SyncStatusChanged += delegate (SyncStatus status) { @@ -565,8 +602,15 @@ namespace SparkleShare { UpdateState (); }; - repo.ChangesDetected += delegate { - UpdateState (); + repo.NewChangeSet += delegate (SparkleChangeSet change_set) { + if (NotificationRaised != null) + NotificationRaised (change_set); + }; + + repo.ConflictResolved += delegate { + if (AlertNotificationRaised != null) + AlertNotificationRaised ("Conflict detected", + "Don't worry, SparkleShare made a copy of each conflicting file."); }; this.repositories.Add (repo); @@ -574,45 +618,25 @@ namespace SparkleShare { } - // Removes a repository from the list of repositories and - // updates the statusicon menu private void RemoveRepository (string folder_path) { - string folder_name = Path.GetFileName (folder_path); + for (int i = 0; i < this.repositories.Count; i++) { + SparkleRepoBase repo = this.repositories [i]; - for (int i = 0; i < Repositories.Length; i++) { - SparkleRepoBase repo = Repositories [i]; - - if (repo.Name.Equals (folder_name)) { + if (repo.LocalPath.Equals (folder_path)) { repo.Dispose (); - - lock (this.repo_lock) { - this.repositories.Remove (repo); - } + this.repositories.Remove (repo); repo = null; - break; + return; } } - } - // Updates the list of repositories with all the - // folders in the SparkleShare folder private void PopulateRepositories () { - lock (this.repo_lock) { - foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) { - string folder_path = new SparkleFolder (folder_name).FullPath; - - if (Directory.Exists (folder_path)) - AddRepository (folder_path); - else - SparkleConfig.DefaultConfig.RemoveFolder (folder_name); - } - } - + CheckRepositories (); RepositoriesLoaded = true; if (FolderListChanged != null) @@ -922,9 +946,7 @@ namespace SparkleShare { target_folder_name, "announcements_url", announcements_url); */ - lock (this.repo_lock) { - AddRepository (target_folder_path); - } + AddRepository (target_folder_path); if (FolderListChanged != null) FolderListChanged ();