From d661c184e2271c5528f2193180570d6f785690d0 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Sun, 25 Sep 2011 19:22:35 +0200 Subject: [PATCH] Remove OS detection code, rely on an abstract base controller with similarly named custom controller subclasses for each OS instead. --- SparkleLib/SparkleListenerBase.cs | 4 +- SparkleLib/SparkleRepoBase.cs | 2 + ...eMacController.cs => SparkleController.cs} | 8 +- SparkleShare/Mac/SparkleShare.csproj | 6 +- SparkleShare/Makefile.am | 2 +- SparkleShare/Program.cs | 15 +- SparkleShare/SparkleController.cs | 1243 ++--------------- SparkleShare/SparkleControllerBase.cs | 1169 ++++++++++++++++ SparkleShare/SparkleLinController.cs | 214 --- SparkleShare/SparkleShare.csproj | 13 +- SparkleShare/SparkleShare.sln | 20 + 11 files changed, 1353 insertions(+), 1343 deletions(-) rename SparkleShare/Mac/{SparkleMacController.cs => SparkleController.cs} (97%) create mode 100755 SparkleShare/SparkleControllerBase.cs delete mode 100755 SparkleShare/SparkleLinController.cs create mode 100644 SparkleShare/SparkleShare.sln diff --git a/SparkleLib/SparkleListenerBase.cs b/SparkleLib/SparkleListenerBase.cs index b9293841..56758ecd 100755 --- a/SparkleLib/SparkleListenerBase.cs +++ b/SparkleLib/SparkleListenerBase.cs @@ -50,7 +50,7 @@ namespace SparkleLib { // don't have your own. All data needed to connect is hashed and // we don't store any personal information ever - uri = "irc://204.62.14.135/"; + uri = "tcp://204.62.14.135:1986"; } Uri announce_uri = new Uri (uri); @@ -77,7 +77,7 @@ namespace SparkleLib { listeners.Add (new SparkleListenerIrc (announce_uri, folder_identifier)); break; default: - listeners.Add (new SparkleListenerIrc (announce_uri, folder_identifier)); + listeners.Add (new SparkleListenerTcp (announce_uri, folder_identifier)); break; } diff --git a/SparkleLib/SparkleRepoBase.cs b/SparkleLib/SparkleRepoBase.cs index 6d724859..9be8e97a 100755 --- a/SparkleLib/SparkleRepoBase.cs +++ b/SparkleLib/SparkleRepoBase.cs @@ -256,6 +256,8 @@ namespace SparkleLib { this.listener.Announcement += delegate (SparkleAnnouncement announcement) { string identifier = Identifier; + Console.WriteLine (announcement.Message + " ! " + CurrentRevision); + if (announcement.FolderIdentifier == identifier && !announcement.Message.Equals (CurrentRevision)) { if ((Status != SyncStatus.SyncUp) && diff --git a/SparkleShare/Mac/SparkleMacController.cs b/SparkleShare/Mac/SparkleController.cs similarity index 97% rename from SparkleShare/Mac/SparkleMacController.cs rename to SparkleShare/Mac/SparkleController.cs index 761c7953..15fa3da1 100755 --- a/SparkleShare/Mac/SparkleMacController.cs +++ b/SparkleShare/Mac/SparkleController.cs @@ -26,13 +26,17 @@ using SparkleLib; namespace SparkleShare { - public class SparkleMacController : SparkleController { + public class SparkleController : SparkleControllerBase { // We have to use our own custom made folder watcher, as // System.IO.FileSystemWatcher fails watching subfolders on Mac private SparkleMacWatcher watcher; - public SparkleMacController () : base () { } + + public SparkleController () : base () + { + } + public override void Initialize () { diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 5f99df54..078dfbf0 100755 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -74,10 +74,9 @@ MainMenu.xib - - SparkleController.cs + + SparkleControllerBase.cs - @@ -109,6 +108,7 @@ SparkleExtensions.cs + diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index 96350ab7..802cbdbd 100755 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -18,11 +18,11 @@ SOURCES = \ SparkleBubbles.cs \ SparkleBubblesController.cs \ SparkleController.cs \ + SparkleControllerBase.cs \ SparkleEntry.cs \ SparkleEventLog.cs \ SparkleEventLogController.cs \ SparkleExtensions.cs \ - SparkleLinController.cs \ SparkleSetup.cs \ SparkleSetupController.cs \ SparkleSetupWindow.cs \ diff --git a/SparkleShare/Program.cs b/SparkleShare/Program.cs index 4291d3bb..8d5f7697 100755 --- a/SparkleShare/Program.cs +++ b/SparkleShare/Program.cs @@ -64,23 +64,10 @@ namespace SparkleShare { if (show_help) ShowHelp (option_set); - // Load the right controller for the OS - string controller_name = "Lin"; - switch (SparkleBackend.Platform) { - case PlatformID.Unix: - SetProcessName ("sparkleshare"); - break; - case PlatformID.MacOSX: - controller_name = "Mac"; - break; - case PlatformID.Win32NT: - controller_name = "Win"; - break; - } // Initialize the controller this way so that // there aren't any exceptions in the OS specific UI's - Controller = new SparkleMacController (); + Controller = new SparkleController (); Controller.Initialize (); if (Controller != null) { diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index d003fac1..d4ceda9e 100755 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -19,1148 +19,195 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Net; -using System.Security.Cryptography; +using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; using System.Threading; -using System.Xml; using Mono.Unix; using SparkleLib; namespace SparkleShare { - public abstract class SparkleController { + public class SparkleController : SparkleControllerBase { - public List Repositories; - public string FolderSize; - public readonly string SparklePath = SparkleConfig.DefaultConfig.FoldersPath; - - public event OnQuitWhileSyncingEventHandler OnQuitWhileSyncing; - public delegate void OnQuitWhileSyncingEventHandler (); - - public event FolderFetchedEventHandler FolderFetched; - public delegate void FolderFetchedEventHandler (); - - public event FolderFetchErrorEventHandler FolderFetchError; - public delegate void FolderFetchErrorEventHandler (string remote_url); - - public event FolderFetchingEventHandler FolderFetching; - public delegate void FolderFetchingEventHandler (double percentage); - - public event FolderListChangedEventHandler FolderListChanged; - public delegate void FolderListChangedEventHandler (); - - public event FolderSizeChangedEventHandler FolderSizeChanged; - public delegate void FolderSizeChangedEventHandler (string folder_size); - - public event AvatarFetchedEventHandler AvatarFetched; - public delegate void AvatarFetchedEventHandler (); - - public event OnIdleEventHandler OnIdle; - public delegate void OnIdleEventHandler (); - - public event OnSyncingEventHandler OnSyncing; - public delegate void OnSyncingEventHandler (); - - public event OnErrorEventHandler OnError; - public delegate void OnErrorEventHandler (); - - public event OnInvitationEventHandler OnInvitation; - public delegate void OnInvitationEventHandler (string server, string folder, string token); - - public event ConflictNotificationRaisedEventHandler ConflictNotificationRaised; - public delegate void ConflictNotificationRaisedEventHandler (); - - public event NotificationRaisedEventHandler NotificationRaised; - public delegate void NotificationRaisedEventHandler (string user_name, string user_email, - string message, string repository_path); - - private SparkleFetcherBase fetcher; - - - // Short alias for the translations - public static string _ (string s) + public SparkleController () : base () { - return Catalog.GetString (s); - } - - - public SparkleController () { } - - public virtual void Initialize () - { - InstallLauncher (); - EnableSystemAutostart (); - - // Create the SparkleShare folder and add it to the bookmarks - if (CreateSparkleShareFolder ()) - AddToBookmarks (); - - FolderSize = GetFolderSize (); - - if (FirstRun) - SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); - else - AddKey (); - - // Watch the SparkleShare folder - FileSystemWatcher watcher = new FileSystemWatcher (SparkleConfig.DefaultConfig.FoldersPath) { - IncludeSubdirectories = false, - EnableRaisingEvents = true, - Filter = "*" - }; - - // Remove the repository when a delete event occurs - watcher.Deleted += delegate (object o, FileSystemEventArgs args) { - RemoveRepository (args.FullPath); - SparkleConfig.DefaultConfig.RemoveFolder (Path.GetFileName (args.Name)); - - if (FolderListChanged != null) - FolderListChanged (); - - FolderSize = GetFolderSize (); - - if (FolderSizeChanged != null) - FolderSizeChanged (FolderSize); - }; - - - watcher.Created += delegate (object o, FileSystemEventArgs args) { - - // Handle invitations when the user saves an - // invitation into the SparkleShare folder - if (args.Name.EndsWith (".sparkle") && !FirstRun) { - XmlDocument xml_doc = new XmlDocument (); - xml_doc.Load (args.Name); - - string server = xml_doc.GetElementsByTagName ("server") [0].InnerText; - string folder = xml_doc.GetElementsByTagName ("folder") [0].InnerText; - string token = xml_doc.GetElementsByTagName ("token") [0].InnerText; - - // FIXME: this is broken :\ - if (OnInvitation != null) - OnInvitation (server, folder, token); - } - }; - - new Thread (new ThreadStart (PopulateRepositories)).Start (); - } - - - public bool FirstRun { - get { - return SparkleConfig.DefaultConfig.User.Email.Equals ("Unknown"); - } - } - - - // Uploads the user's public key to the server - public bool AcceptInvitation (string server, string folder, string token) - { - // The location of the user's public key for SparkleShare - string public_key_file_path = SparkleHelpers.CombineMore (SparkleConfig.DefaultConfig.HomePath, ".ssh", - "sparkleshare." + UserEmail + ".key.pub"); - - if (!File.Exists (public_key_file_path)) - return false; - - StreamReader reader = new StreamReader (public_key_file_path); - string public_key = reader.ReadToEnd (); - reader.Close (); - - string url = "https://" + server + "/?folder=" + folder + - "&token=" + token + "&pubkey=" + public_key; - - SparkleHelpers.DebugInfo ("WebRequest", url); - - HttpWebRequest request = (HttpWebRequest) WebRequest.Create (url); - HttpWebResponse response = (HttpWebResponse) request.GetResponse(); - - if (response.StatusCode == HttpStatusCode.OK) { - response.Close (); - return true; - - } else { - response.Close (); - return false; - } - } - - - public List Folders { - get { - List folders = SparkleConfig.DefaultConfig.Folders; - folders.Sort (); - return folders; - } - } - - - public List PreviousHosts { - get { - List hosts = SparkleConfig.DefaultConfig.HostsWithUsername; - hosts.AddRange(SparkleConfig.DefaultConfig.Hosts); - hosts.Sort (); - return hosts; - } - } - - - public List UnsyncedFolders { - get { - List unsynced_folders = new List (); - - foreach (SparkleRepoBase repo in Repositories) { - if (repo.HasUnsyncedChanges) - unsynced_folders.Add (repo.Name); - } - - return unsynced_folders; - } - } - - - public List GetLog () - { - List list = new List (); - - foreach (SparkleRepoBase repo in Repositories) { - List change_sets = repo.GetChangeSets (50); - - if (change_sets != null) - list.AddRange (change_sets); - else - SparkleHelpers.DebugInfo ("Log", "Could not create log for " + repo.Name); - } - - list.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); - list.Reverse (); - - if (list.Count > 100) - return list.GetRange (0, 100); - else - return list.GetRange (0, list.Count); - } - - - public List GetLog (string name) - { - if (name == null) - return GetLog (); - - string path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, name}.Combine (); - int log_size = 50; - - foreach (SparkleRepoBase repo in Repositories) { - if (repo.LocalPath.Equals (path)) - return repo.GetChangeSets (log_size); - } - - return null; - } - - - public abstract string EventLogHTML { get; } - public abstract string DayEntryHTML { get; } - public abstract string EventEntryHTML { get; } - - - public string GetHTMLLog (List change_sets) - { - List activity_days = new List (); - List emails = new List (); - - change_sets.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); - change_sets.Reverse (); - - if (change_sets.Count == 0) - return null; - - foreach (SparkleChangeSet change_set in change_sets) { - if (!emails.Contains (change_set.User.Email)) - emails.Add (change_set.User.Email); - - bool change_set_inserted = false; - foreach (ActivityDay stored_activity_day in activity_days) { - if (stored_activity_day.DateTime.Year == change_set.Timestamp.Year && - stored_activity_day.DateTime.Month == change_set.Timestamp.Month && - stored_activity_day.DateTime.Day == change_set.Timestamp.Day) { - - bool squash = false; - foreach (SparkleChangeSet existing_set in stored_activity_day) { - if (change_set.User.Name.Equals (existing_set.User.Name) && - change_set.User.Email.Equals (existing_set.User.Email) && - change_set.Folder.Equals (existing_set.Folder)) { - - existing_set.Added.AddRange (change_set.Added); - existing_set.Edited.AddRange (change_set.Edited); - existing_set.Deleted.AddRange (change_set.Deleted); - existing_set.MovedFrom.AddRange (change_set.MovedFrom); - existing_set.MovedTo.AddRange (change_set.MovedTo); - existing_set.Notes.AddRange (change_set.Notes); - - existing_set.Added = existing_set.Added.Distinct ().ToList (); - existing_set.Edited = existing_set.Edited.Distinct ().ToList (); - existing_set.Deleted = existing_set.Deleted.Distinct ().ToList (); - - if (DateTime.Compare (existing_set.Timestamp, change_set.Timestamp) < 1) { - existing_set.FirstTimestamp = existing_set.Timestamp; - existing_set.Timestamp = change_set.Timestamp; - existing_set.Revision = change_set.Revision; - - } else { - existing_set.FirstTimestamp = change_set.Timestamp; - } - - squash = true; - } - } - - if (!squash) - stored_activity_day.Add (change_set); - - change_set_inserted = true; - break; - } - } - - if (!change_set_inserted) { - ActivityDay activity_day = new ActivityDay (change_set.Timestamp); - activity_day.Add (change_set); - activity_days.Add (activity_day); - } - } - - new Thread (new ThreadStart (delegate { - FetchAvatars (emails, 48); - })).Start (); - - string event_log_html = EventLogHTML; - string day_entry_html = DayEntryHTML; - string event_entry_html = EventEntryHTML; - string event_log = ""; - - foreach (ActivityDay activity_day in activity_days) { - string event_entries = ""; - - foreach (SparkleChangeSet change_set in activity_day) { - string event_entry = "
"; - - if (change_set.IsMagical) { - event_entry += "
Did something magical
"; - - } else { - if (change_set.Edited.Count > 0) { - foreach (string file_path in change_set.Edited) { - string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, - change_set.Folder, file_path}.Combine (); - - if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; - else - event_entry += "
" + file_path + "
"; - } - } - - if (change_set.Added.Count > 0) { - foreach (string file_path in change_set.Added) { - string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, - change_set.Folder, file_path}.Combine (); - - if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; - else - event_entry += "
" + file_path + "
"; - } - } - - if (change_set.Deleted.Count > 0) { - foreach (string file_path in change_set.Deleted) { - string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, - change_set.Folder, file_path}.Combine (); - - if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; - else - event_entry += "
" + file_path + "
"; - } - } - - if (change_set.MovedFrom.Count > 0) { - int i = 0; - foreach (string file_path in change_set.MovedFrom) { - string to_file_path = change_set.MovedTo [i]; - - string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, - change_set.Folder, file_path}.Combine (); - - string absolute_to_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, - change_set.Folder, to_file_path}.Combine (); - - if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; - else - event_entry += "
" + file_path + "
"; - - if (File.Exists (absolute_to_file_path)) - event_entry += "" + to_file_path + "
"; - else - event_entry += to_file_path + ""; - - i++; - } - } - } - - string comments = ""; - comments = "
"; - - if (change_set.Notes != null) { - change_set.Notes.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); - - foreach (SparkleNote note in change_set.Notes) { - - string note_avatar = GetAvatar (note.User.Email, 48); - if (File.Exists (note_avatar)) - note_avatar = "file://" + note_avatar; - else - note_avatar = ""; - - comments += "
" + - "

" + - note.User.Name + "

" + - note.Body + - "
"; - } - } - - comments += "
"; - - string change_set_avatar = GetAvatar (change_set.User.Email, 48); - if (File.Exists (change_set_avatar)) - change_set_avatar = "file://" + change_set_avatar; - else - change_set_avatar = ""; - - event_entry += "
"; - - string timestamp = change_set.Timestamp.ToString ("H:mm"); - - if (!change_set.FirstTimestamp.Equals (new DateTime ())) - timestamp = change_set.FirstTimestamp.ToString ("H:mm") + - " – " + timestamp; - - event_entries += event_entry_html.Replace ("", event_entry) - .Replace ("", change_set.User.Name) - .Replace ("", change_set_avatar) - .Replace ("", timestamp) - .Replace ("", change_set.Folder) - .Replace ("", change_set.Revision) - .Replace ("", AssignColor (change_set.Folder)) - .Replace ("", comments); - } - - string day_entry = ""; - DateTime today = DateTime.Now; - DateTime yesterday = DateTime.Now.AddDays (-1); - - if (today.Day == activity_day.DateTime.Day && - today.Month == activity_day.DateTime.Month && - today.Year == activity_day.DateTime.Year) { - - day_entry = day_entry_html.Replace ("", "Today"); - - } else if (yesterday.Day == activity_day.DateTime.Day && - yesterday.Month == activity_day.DateTime.Month && - yesterday.Year == activity_day.DateTime.Year) { - - day_entry = day_entry_html.Replace ("", "Yesterday"); - - } else { - if (activity_day.DateTime.Year != DateTime.Now.Year) { - - // TRANSLATORS: This is the date in the event logs - day_entry = day_entry_html.Replace ("", - activity_day.DateTime.ToString (_("dddd, MMMM d, yyyy"))); - - } else { - - // TRANSLATORS: This is the date in the event logs, without the year - day_entry = day_entry_html.Replace ("", - activity_day.DateTime.ToString (_("dddd, MMMM d"))); - } - } - - event_log += day_entry.Replace ("", event_entries); - } - - string html = event_log_html.Replace ("", event_log) - .Replace ("", UserName) - .Replace ("", "file://" + GetAvatar (UserEmail, 48)); - - return html; } // Creates a .desktop entry in autostart folder to // start SparkleShare automatically at login - public abstract void EnableSystemAutostart (); + public override void EnableSystemAutostart () + { + string autostart_path = Path.Combine (Environment.GetFolderPath ( + Environment.SpecialFolder.ApplicationData), "autostart"); + + string desktopfile_path = Path.Combine (autostart_path, "sparkleshare.desktop"); + + if (!Directory.Exists (autostart_path)) + Directory.CreateDirectory (autostart_path); + + if (!File.Exists (desktopfile_path)) { + TextWriter writer = new StreamWriter (desktopfile_path); + writer.WriteLine ("[Desktop Entry]\n" + + "Type=Application\n" + + "Name=SparkleShare\n" + + "Exec=sparkleshare start\n" + + "Icon=folder-sparkleshare\n" + + "Terminal=false\n" + + "X-GNOME-Autostart-enabled=true\n" + + "Categories=Network"); + writer.Close (); + + // Give the launcher the right permissions so it can be launched by the user + UnixFileInfo file_info = new UnixFileInfo (desktopfile_path); + file_info.Create (FileAccessPermissions.UserReadWriteExecute); + + SparkleHelpers.DebugInfo ("Controller", "Enabled autostart on login"); + } + } + // Installs a launcher so the user can launch SparkleShare // from the Internet category if needed - public abstract void InstallLauncher (); + public override void InstallLauncher () + { + string apps_path = + new string [] {SparkleConfig.DefaultConfig.HomePath, + ".local", "share", "applications"}.Combine (); + + string desktopfile_path = Path.Combine (apps_path, "sparkleshare.desktop"); + + if (!File.Exists (desktopfile_path)) { + if (!Directory.Exists (apps_path)) + Directory.CreateDirectory (apps_path); + + TextWriter writer = new StreamWriter (desktopfile_path); + writer.WriteLine ("[Desktop Entry]\n" + + "Type=Application\n" + + "Name=SparkleShare\n" + + "Comment=Share documents\n" + + "Exec=sparkleshare start\n" + + "Icon=folder-sparkleshare\n" + + "Terminal=false\n" + + "Categories=Network;"); + writer.Close (); + + // Give the launcher the right permissions so it can be launched by the user + UnixFileInfo file_info = new UnixFileInfo (desktopfile_path); + file_info.FileAccessPermissions = FileAccessPermissions.UserReadWriteExecute; + + SparkleHelpers.DebugInfo ("Controller", "Created '" + desktopfile_path + "'"); + } + } + // Adds the SparkleShare folder to the user's // list of bookmarked places - public abstract void AddToBookmarks (); + public override void AddToBookmarks () + { + string bookmarks_file_path = Path.Combine (SparkleConfig.DefaultConfig.HomePath, ".gtk-bookmarks"); + string sparkleshare_bookmark = "file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"; + + if (File.Exists (bookmarks_file_path)) { + StreamReader reader = new StreamReader (bookmarks_file_path); + string bookmarks = reader.ReadToEnd (); + reader.Close (); + + if (!bookmarks.Contains (sparkleshare_bookmark)) { + TextWriter writer = File.AppendText (bookmarks_file_path); + writer.WriteLine ("file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"); + writer.Close (); + } + } else { + StreamWriter writer = new StreamWriter (bookmarks_file_path); + writer.WriteLine ("file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"); + writer.Close (); + } + } + // Creates the SparkleShare folder in the user's home folder - public abstract bool CreateSparkleShareFolder (); - - // Opens the SparkleShare folder or an (optional) subfolder - public abstract void OpenSparkleShareFolder (string subfolder); - - - // Fires events for the current syncing state - public void UpdateState () + public override bool CreateSparkleShareFolder () { - foreach (SparkleRepoBase repo in Repositories) { - if (repo.Status == SyncStatus.SyncDown || - repo.Status == SyncStatus.SyncUp || - repo.IsBuffering) { + if (!Directory.Exists (SparkleConfig.DefaultConfig.FoldersPath)) { + + Directory.CreateDirectory (SparkleConfig.DefaultConfig.FoldersPath); + SparkleHelpers.DebugInfo ("Controller", "Created '" + SparkleConfig.DefaultConfig.FoldersPath + "'"); - if (OnSyncing != null) - OnSyncing (); + string gvfs_command_path = + new string [] {Path.VolumeSeparatorChar.ToString (), + "usr", "bin", "gvfs-set-attribute"}.Combine (); - return; + // Add a special icon to the SparkleShare folder + if (File.Exists (gvfs_command_path)) { + Process process = new Process (); - } else if (repo.HasUnsyncedChanges) { - if (OnError != null) - OnError (); + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = "gvfs-set-attribute"; - return; + // Clear the custom (legacy) icon path + process.StartInfo.Arguments = "-t unset " + SparkleConfig.DefaultConfig.FoldersPath + " metadata::custom-icon"; + process.Start (); + process.WaitForExit (); + + // Give the SparkleShare folder an icon name, so that it scales + process.StartInfo.Arguments = SparkleConfig.DefaultConfig.FoldersPath + " metadata::custom-icon-name 'folder-sparkleshare'"; + process.Start (); + process.WaitForExit (); } + + return true; } - if (OnIdle != null) - OnIdle (); - - FolderSize = GetFolderSize (); - - if (FolderSizeChanged != null) - FolderSizeChanged (FolderSize); + return false; } + - - // Adds a repository to the list of repositories - private void AddRepository (string folder_path) - { - if (folder_path.Equals (SparkleConfig.DefaultConfig.TmpPath)) - return; - - string folder_name = Path.GetFileName (folder_path); - string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name); - - if (backend == null) - return; - - SparkleRepoBase repo = new SparkleRepoGit (folder_path, SparkleBackend.DefaultBackend); - - repo.NewChangeSet += delegate (SparkleChangeSet change_set) { - string message = FormatMessage (change_set); - - if (NotificationRaised != null) - NotificationRaised (change_set.User.Name, change_set.User.Email, message, repo.LocalPath); - }; - - repo.NewNote += delegate (string user_name, string user_email) { - if (NotificationRaised != null) - NotificationRaised (user_name, user_email, - "added a note to " + Path.GetFileName (repo.LocalPath), repo.LocalPath); - }; - - repo.ConflictResolved += delegate { - if (ConflictNotificationRaised != null) - ConflictNotificationRaised (); - }; - - repo.SyncStatusChanged += delegate (SyncStatus status) { -/* if (status == SyncStatus.SyncUp) { - foreach (string path in repo.UnsyncedFilePaths) - Console.WriteLine (path); - } -*/ - if (status == SyncStatus.Idle || - status == SyncStatus.SyncUp || - status == SyncStatus.SyncDown || - status == SyncStatus.Error) { - - UpdateState (); - } - }; - - repo.ChangesDetected += delegate { - UpdateState (); - }; - - Repositories.Add (repo); - } - - - // 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 < Repositories.Count; i++) { - SparkleRepoBase repo = Repositories [i]; - - if (repo.Name.Equals (folder_name)) { - repo.Dispose (); - Repositories.Remove (repo); - repo = null; - break; - } - } - } - - - // Updates the list of repositories with all the - // folders in the SparkleShare folder - private void PopulateRepositories () - { - Repositories = new List (); - - 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); - } - - if (FolderListChanged != null) - FolderListChanged (); - - FolderSize = GetFolderSize (); - - if (FolderSizeChanged != null) - FolderSizeChanged (FolderSize); - } - - - public bool NotificationsEnabled { + public override string EventLogHTML { get { - string notifications_enabled = - SparkleConfig.DefaultConfig.GetConfigOption ("notifications"); + string path = new string [] {Defines.PREFIX, + "share", "sparkleshare", "html", "event-log.html"}.Combine (); - if (String.IsNullOrEmpty (notifications_enabled)) { - SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); - return true; + string html = String.Join (Environment.NewLine, File.ReadAllLines (path)); - } else { - return notifications_enabled.Equals (bool.TrueString); - } + html = html.Replace ("", "file://" + + new string [] {Defines.PREFIX, "share", "sparkleshare", "html", "jquery.js"}.Combine ()); + + return html; } - } - - - public void ToggleNotifications () { - bool notifications_enabled = - SparkleConfig.DefaultConfig.GetConfigOption ("notifications") - .Equals (bool.TrueString); - - if (notifications_enabled) - SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.FalseString); - else - SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); - } - - - private string GetFolderSize () - { - double folder_size = CalculateFolderSize ( - new DirectoryInfo (SparkleConfig.DefaultConfig.FoldersPath)); - - return FormatFolderSize (folder_size); - } - - - private string FormatMessage (SparkleChangeSet change_set) - { - string file_name = ""; - string message = ""; - - if (change_set.Added.Count > 0) { - file_name = change_set.Added [0]; - message = String.Format (_("added ‘{0}’"), file_name); - } - - if (change_set.MovedFrom.Count > 0) { - file_name = change_set.MovedFrom [0]; - message = String.Format (_("moved ‘{0}’"), file_name); - } - - if (change_set.Edited.Count > 0) { - file_name = change_set.Edited [0]; - message = String.Format (_("edited ‘{0}’"), file_name); - } - - if (change_set.Deleted.Count > 0) { - file_name = change_set.Deleted [0]; - message = String.Format (_("deleted ‘{0}’"), file_name); - } - - int changes_count = (change_set.Added.Count + - change_set.Edited.Count + - change_set.Deleted.Count + - change_set.MovedFrom.Count) - 1; - - if (changes_count > 0) { - string msg = Catalog.GetPluralString ("and {0} more", "and {0} more", changes_count); - message += " " + String.Format (msg, changes_count); - - } else if (changes_count < 0) { - message += _("did something magical"); - } - - return message; - } // TODO: move to bubbles controller - - - // Recursively gets a folder's size in bytes - private double CalculateFolderSize (DirectoryInfo parent) - { - if (!Directory.Exists (parent.ToString ())) - return 0; - - double size = 0; - - // Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential - // crashes when files are being queried whilst the files have already been deleted. - if (parent.Name.Equals ("rebase-apply") || - parent.Name.Equals (".tmp")) - return 0; - - try { - foreach (FileInfo file in parent.GetFiles()) { - if (!file.Exists) - return 0; - - size += file.Length; - } - - foreach (DirectoryInfo directory in parent.GetDirectories()) - size += CalculateFolderSize (directory); - - } catch (Exception) { - return 0; - } - - return size; - } - - - // Format a file size nicely with small caps. - // Example: 1048576 becomes "1 ᴍʙ" - private string FormatFolderSize (double byte_count) - { - if (byte_count >= 1099511627776) - return String.Format ("{0:##.##} ᴛʙ", Math.Round (byte_count / 1099511627776, 1)); - else if (byte_count >= 1073741824) - return String.Format ("{0:##.##} ɢʙ", Math.Round (byte_count / 1073741824, 1)); - else if (byte_count >= 1048576) - return String.Format ("{0:##.##} ᴍʙ", Math.Round (byte_count / 1048576, 1)); - else if (byte_count >= 1024) - return String.Format ("{0:##.##} ᴋʙ", Math.Round (byte_count / 1024, 1)); - else - return byte_count.ToString () + " bytes"; - } - - - public void OpenSparkleShareFolder () - { - OpenSparkleShareFolder (""); } - // Adds the user's SparkleShare key to the ssh-agent, - // so all activity is done with this key - public void AddKey () + public override string DayEntryHTML { + get { + string path = new string [] {Defines.PREFIX, + "share", "sparkleshare", "html", "day-entry.html"}.Combine (); + + return String.Join (Environment.NewLine, File.ReadAllLines (path)); + } + } + + + public override string EventEntryHTML { + get { + string path = new string [] {Defines.PREFIX, + "share", "sparkleshare", "html", "event-entry.html"}.Combine (); + + return String.Join (Environment.NewLine, File.ReadAllLines (path)); + } + } + + + public override void OpenSparkleShareFolder (string subfolder) { - string keys_path = Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath); - string key_file_name = "sparkleshare." + UserEmail + ".key"; + string folder = Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, subfolder); Process process = new Process (); - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.FileName = "ssh-add"; - process.StartInfo.Arguments = "\"" + Path.Combine (keys_path, key_file_name) + "\""; + process.StartInfo.FileName = "xdg-open"; + process.StartInfo.Arguments = "\"" + folder + "\""; process.Start (); - process.WaitForExit (); - } - - - public bool BackendIsPresent { - get { - return SparkleBackend.DefaultBackend.IsPresent; - } - } - - - // Looks up the user's name from the global configuration - public string UserName - { - get { - return SparkleConfig.DefaultConfig.User.Name; - } - - set { - SparkleConfig.DefaultConfig.User = new SparkleUser (value, UserEmail); - } - } - - - // Looks up the user's email from the global configuration - public string UserEmail - { - get { - return SparkleConfig.DefaultConfig.User.Email; - } - - set { - SparkleConfig.DefaultConfig.User = new SparkleUser (UserName, value); - } - } - - - // Generates and installs an RSA keypair to identify this system - public void GenerateKeyPair () - { - string keys_path = Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath); - string key_file_name = "sparkleshare." + UserEmail + ".key"; - string key_file_path = Path.Combine (keys_path, key_file_name); - - if (File.Exists (key_file_path)) { - SparkleHelpers.DebugInfo ("Config", "Key already exists ('" + key_file_name + "'), " + - "leaving it untouched"); - return; - } - - if (!Directory.Exists (keys_path)) - Directory.CreateDirectory (keys_path); - - if (!File.Exists (key_file_name)) { - Process process = new Process () { - EnableRaisingEvents = true - }; - - process.StartInfo.WorkingDirectory = keys_path; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.FileName = "ssh-keygen"; - - // -t is the crypto type - // -P is the password (none) - // -f is the file name to store the private key in - process.StartInfo.Arguments = "-t rsa -P \"\" -f " + key_file_name; - - process.Exited += delegate { - SparkleHelpers.DebugInfo ("Config", "Created private key '" + key_file_name + "'"); - SparkleHelpers.DebugInfo ("Config", "Created public key '" + key_file_name + ".pub'"); - - // Create an easily accessible copy of the public - // key in the user's SparkleShare folder - File.Copy (key_file_path + ".pub", - Path.Combine (SparklePath, UserName + "'s key.txt")); - }; - - process.Start (); - process.WaitForExit (); - } - } - - - // Gets the avatar for a specific email address and size - public void FetchAvatars (List emails, int size) - { - List old_avatars = new List (); - bool avatar_fetched = false; - string avatar_path = new string [] { - Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath), - "icons", size + "x" + size, "status"}.Combine (); - - if (!Directory.Exists (avatar_path)) { - Directory.CreateDirectory (avatar_path); - SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'"); - } - - foreach (string raw_email in emails) { - - // Gravatar wants lowercase emails - string email = raw_email.ToLower (); - string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email); - - if (File.Exists (avatar_file_path)) { - FileInfo avatar_info = new FileInfo (avatar_file_path); - - // Delete avatars older than a month - if (avatar_info.CreationTime < DateTime.Now.AddMonths (-1)) { - avatar_info.Delete (); - old_avatars.Add (email); - } - - } else { - WebClient client = new WebClient (); - string url = "http://gravatar.com/avatar/" + GetMD5 (email) + - ".jpg?s=" + size + "&d=404"; - - try { - // Fetch the avatar - byte [] buffer = client.DownloadData (url); - - // Write the avatar data to a - // if not empty - if (buffer.Length > 255) { - avatar_fetched = true; - File.WriteAllBytes (avatar_file_path, buffer); - SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar for " + email); - } - - } catch (WebException e) { - SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email); - - // Stop downloading further avatars if we have no internet access - if (e.Status == WebExceptionStatus.Timeout) - break; - } - } - } - - // Fetch new versions of the avatars that we - // deleted because they were too old - if (old_avatars.Count > 0) - FetchAvatars (old_avatars, size); - - if (AvatarFetched != null && avatar_fetched) - AvatarFetched (); - } - - - public string GetAvatar (string email, int size) - { - string avatar_file_path = SparkleHelpers.CombineMore ( - Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath), "icons", - size + "x" + size, "status", "avatar-" + email); - - return avatar_file_path; - } - - - public void FetchFolder (string server, string remote_folder) - { - server = server.Trim (); - remote_folder = remote_folder.Trim (); - - string tmp_path = SparkleConfig.DefaultConfig.TmpPath; - if (!Directory.Exists (tmp_path)) - Directory.CreateDirectory (tmp_path); - - // Strip the '.git' from the name - string canonical_name = Path.GetFileNameWithoutExtension (remote_folder); - string tmp_folder = Path.Combine (tmp_path, canonical_name); - - string backend = null; - -/* if (remote_folder.EndsWith (".hg")) { - remote_folder = remote_folder.Substring (0, (remote_folder.Length - 3)); - fetcher = new SparkleFetcherHg (server, remote_folder, tmp_folder); - backend = "Hg"; - - } else if (remote_folder.EndsWith (".scp")) { - remote_folder = remote_folder.Substring (0, (remote_folder.Length - 4)); - fetcher = new SparkleFetcherScp (server, remote_folder, tmp_folder); - backend = "Scp"; - - } else {*/ - this.fetcher = new SparkleFetcherGit (server, remote_folder, tmp_folder); - backend = "Git"; - //} - - bool target_folder_exists = Directory.Exists ( - Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name)); - - // Add a numbered suffix to the nameif a folder with the same name - // already exists. Example: "Folder (2)" - int i = 1; - while (target_folder_exists) { - i++; - target_folder_exists = Directory.Exists ( - Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name + " (" + i + ")")); - } - - string target_folder_name = canonical_name; - if (i > 1) - target_folder_name += " (" + i + ")"; - - this.fetcher.Finished += delegate { - - // Needed to do the moving - SparkleHelpers.ClearAttributes (tmp_folder); - string target_folder_path = Path.Combine ( - SparkleConfig.DefaultConfig.FoldersPath, target_folder_name); - - try { - Directory.Move (tmp_folder, target_folder_path); - } catch (Exception e) { - SparkleHelpers.DebugInfo ("Controller", "Error moving folder: " + e.Message); - } - - SparkleConfig.DefaultConfig.AddFolder (target_folder_name, this.fetcher.RemoteUrl, backend); - AddRepository (target_folder_path); - - if (FolderFetched != null) - FolderFetched (); - - FolderSize = GetFolderSize (); - - if (FolderSizeChanged != null) - FolderSizeChanged (FolderSize); - - if (FolderListChanged != null) - FolderListChanged (); - - this.fetcher.Dispose (); - - if (Directory.Exists (tmp_path)) - Directory.Delete (tmp_path, true); - }; - - - this.fetcher.Failed += delegate { - if (FolderFetchError != null) - FolderFetchError (this.fetcher.RemoteUrl); - - this.fetcher.Dispose (); - - if (Directory.Exists (tmp_path)) - Directory.Delete (tmp_path, true); - }; - - - this.fetcher.ProgressChanged += delegate (double percentage) { - if (FolderFetching != null) - FolderFetching (percentage); - }; - - - this.fetcher.Start (); - } - - - public void StopFetcher () - { - if (fetcher != null) - fetcher.Stop (); - } - - - // Creates an MD5 hash of input - private string GetMD5 (string s) - { - MD5 md5 = new MD5CryptoServiceProvider (); - Byte[] bytes = ASCIIEncoding.Default.GetBytes (s); - Byte[] encoded_bytes = md5.ComputeHash (bytes); - return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", ""); - } - - - // Checks whether there are any folders syncing and - // quits if safe - public void TryQuit () - { - foreach (SparkleRepoBase repo in Repositories) { - if (repo.Status == SyncStatus.SyncUp || - repo.Status == SyncStatus.SyncDown || - repo.IsBuffering) { - - if (OnQuitWhileSyncing != null) - OnQuitWhileSyncing (); - - return; - } - } - - Quit (); - } - - - public void Quit () - { - foreach (SparkleRepoBase repo in Repositories) - repo.Dispose (); - - Environment.Exit (0); - } - - - // Checks to see if an email address is valid - public bool IsValidEmail (string email) - { - Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", RegexOptions.IgnoreCase); - return regex.IsMatch (email); - } - - - - - public void AddNoteToFolder (string folder_name, string revision, string note) - { - folder_name = folder_name.Replace ("%20", " "); - note = note.Replace ("%20", " "); - - foreach (SparkleRepoBase repo in Repositories) { - if (repo.Name.Equals (folder_name)) - repo.AddNote (revision, note); - } - } - - - - - private string [] tango_palette = new string [] {"#eaab00", "#e37222", - "#3892ab", "#33c2cb", "#19b271", "#9eab05", "#8599a8", "#9ca696", - "#b88454", "#cc0033", "#8f6678", "#8c6cd0", "#796cbf", "#4060af", - "#aa9c8f", "#818a8f"}; - - private string AssignColor (string s) - { - string hash = GetMD5 (s).Substring (0, 8); - string numbers = Regex.Replace (hash, "[a-z]", ""); - int number = 3 + int.Parse (numbers); - return this.tango_palette [number % this.tango_palette.Length]; - } - } - - - public class ChangeSet : SparkleChangeSet { } - - - // All change sets that happened on a day - public class ActivityDay : List - { - public DateTime DateTime; - - public ActivityDay (DateTime date_time) - { - DateTime = date_time; - DateTime = new DateTime (DateTime.Year, DateTime.Month, DateTime.Day); } } } diff --git a/SparkleShare/SparkleControllerBase.cs b/SparkleShare/SparkleControllerBase.cs new file mode 100755 index 00000000..79d38140 --- /dev/null +++ b/SparkleShare/SparkleControllerBase.cs @@ -0,0 +1,1169 @@ +// SparkleShare, a collaboration and sharing tool. +// Copyright (C) 2010 Hylke Bons +// +// 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 . + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; + +using Mono.Unix; +using SparkleLib; + +namespace SparkleShare { + + public abstract class SparkleControllerBase { + + public List Repositories; + public string FolderSize; + public readonly string SparklePath = SparkleConfig.DefaultConfig.FoldersPath; + + public event OnQuitWhileSyncingEventHandler OnQuitWhileSyncing; + public delegate void OnQuitWhileSyncingEventHandler (); + + public event FolderFetchedEventHandler FolderFetched; + public delegate void FolderFetchedEventHandler (); + + public event FolderFetchErrorEventHandler FolderFetchError; + public delegate void FolderFetchErrorEventHandler (string remote_url); + + public event FolderFetchingEventHandler FolderFetching; + public delegate void FolderFetchingEventHandler (double percentage); + + public event FolderListChangedEventHandler FolderListChanged; + public delegate void FolderListChangedEventHandler (); + + public event FolderSizeChangedEventHandler FolderSizeChanged; + public delegate void FolderSizeChangedEventHandler (string folder_size); + + public event AvatarFetchedEventHandler AvatarFetched; + public delegate void AvatarFetchedEventHandler (); + + public event OnIdleEventHandler OnIdle; + public delegate void OnIdleEventHandler (); + + public event OnSyncingEventHandler OnSyncing; + public delegate void OnSyncingEventHandler (); + + public event OnErrorEventHandler OnError; + public delegate void OnErrorEventHandler (); + + public event OnInvitationEventHandler OnInvitation; + public delegate void OnInvitationEventHandler (string server, string folder, string token); + + public event ConflictNotificationRaisedEventHandler ConflictNotificationRaised; + public delegate void ConflictNotificationRaisedEventHandler (); + + public event NotificationRaisedEventHandler NotificationRaised; + public delegate void NotificationRaisedEventHandler (string user_name, string user_email, + string message, string repository_path); + + private SparkleFetcherBase fetcher; + + + // Short alias for the translations + public static string _ (string s) + { + return Catalog.GetString (s); + } + + + public SparkleControllerBase () + { + } + + + public virtual void Initialize () + { + InstallLauncher (); + EnableSystemAutostart (); + + // Create the SparkleShare folder and add it to the bookmarks + if (CreateSparkleShareFolder ()) + AddToBookmarks (); + + FolderSize = GetFolderSize (); + + if (FirstRun) + SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); + else + AddKey (); + + // Watch the SparkleShare folder + FileSystemWatcher watcher = new FileSystemWatcher (SparkleConfig.DefaultConfig.FoldersPath) { + IncludeSubdirectories = false, + EnableRaisingEvents = true, + Filter = "*" + }; + + // Remove the repository when a delete event occurs + watcher.Deleted += delegate (object o, FileSystemEventArgs args) { + RemoveRepository (args.FullPath); + SparkleConfig.DefaultConfig.RemoveFolder (Path.GetFileName (args.Name)); + + if (FolderListChanged != null) + FolderListChanged (); + + FolderSize = GetFolderSize (); + + if (FolderSizeChanged != null) + FolderSizeChanged (FolderSize); + }; + + + watcher.Created += delegate (object o, FileSystemEventArgs args) { + + // Handle invitations when the user saves an + // invitation into the SparkleShare folder + if (args.Name.EndsWith (".sparkle") && !FirstRun) { + XmlDocument xml_doc = new XmlDocument (); + xml_doc.Load (args.Name); + + string server = xml_doc.GetElementsByTagName ("server") [0].InnerText; + string folder = xml_doc.GetElementsByTagName ("folder") [0].InnerText; + string token = xml_doc.GetElementsByTagName ("token") [0].InnerText; + + // FIXME: this is broken :\ + if (OnInvitation != null) + OnInvitation (server, folder, token); + } + }; + + new Thread (new ThreadStart (PopulateRepositories)).Start (); + } + + + public bool FirstRun { + get { + return SparkleConfig.DefaultConfig.User.Email.Equals ("Unknown"); + } + } + + + // Uploads the user's public key to the server + public bool AcceptInvitation (string server, string folder, string token) + { + // The location of the user's public key for SparkleShare + string public_key_file_path = SparkleHelpers.CombineMore (SparkleConfig.DefaultConfig.HomePath, ".ssh", + "sparkleshare." + UserEmail + ".key.pub"); + + if (!File.Exists (public_key_file_path)) + return false; + + StreamReader reader = new StreamReader (public_key_file_path); + string public_key = reader.ReadToEnd (); + reader.Close (); + + string url = "https://" + server + "/?folder=" + folder + + "&token=" + token + "&pubkey=" + public_key; + + SparkleHelpers.DebugInfo ("WebRequest", url); + + HttpWebRequest request = (HttpWebRequest) WebRequest.Create (url); + HttpWebResponse response = (HttpWebResponse) request.GetResponse(); + + if (response.StatusCode == HttpStatusCode.OK) { + response.Close (); + return true; + + } else { + response.Close (); + return false; + } + } + + + public List Folders { + get { + List folders = SparkleConfig.DefaultConfig.Folders; + folders.Sort (); + return folders; + } + } + + + public List PreviousHosts { + get { + List hosts = SparkleConfig.DefaultConfig.HostsWithUsername; + hosts.AddRange(SparkleConfig.DefaultConfig.Hosts); + hosts.Sort (); + return hosts; + } + } + + + public List UnsyncedFolders { + get { + List unsynced_folders = new List (); + + foreach (SparkleRepoBase repo in Repositories) { + if (repo.HasUnsyncedChanges) + unsynced_folders.Add (repo.Name); + } + + return unsynced_folders; + } + } + + + public List GetLog () + { + List list = new List (); + + foreach (SparkleRepoBase repo in Repositories) { + List change_sets = repo.GetChangeSets (50); + + if (change_sets != null) + list.AddRange (change_sets); + else + SparkleHelpers.DebugInfo ("Log", "Could not create log for " + repo.Name); + } + + list.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); + list.Reverse (); + + if (list.Count > 100) + return list.GetRange (0, 100); + else + return list.GetRange (0, list.Count); + } + + + public List GetLog (string name) + { + if (name == null) + return GetLog (); + + string path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, name}.Combine (); + int log_size = 50; + + foreach (SparkleRepoBase repo in Repositories) { + if (repo.LocalPath.Equals (path)) + return repo.GetChangeSets (log_size); + } + + return null; + } + + + public abstract string EventLogHTML { get; } + public abstract string DayEntryHTML { get; } + public abstract string EventEntryHTML { get; } + + + public string GetHTMLLog (List change_sets) + { + List activity_days = new List (); + List emails = new List (); + + change_sets.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); + change_sets.Reverse (); + + if (change_sets.Count == 0) + return null; + + foreach (SparkleChangeSet change_set in change_sets) { + if (!emails.Contains (change_set.User.Email)) + emails.Add (change_set.User.Email); + + bool change_set_inserted = false; + foreach (ActivityDay stored_activity_day in activity_days) { + if (stored_activity_day.DateTime.Year == change_set.Timestamp.Year && + stored_activity_day.DateTime.Month == change_set.Timestamp.Month && + stored_activity_day.DateTime.Day == change_set.Timestamp.Day) { + + bool squash = false; + foreach (SparkleChangeSet existing_set in stored_activity_day) { + if (change_set.User.Name.Equals (existing_set.User.Name) && + change_set.User.Email.Equals (existing_set.User.Email) && + change_set.Folder.Equals (existing_set.Folder)) { + + existing_set.Added.AddRange (change_set.Added); + existing_set.Edited.AddRange (change_set.Edited); + existing_set.Deleted.AddRange (change_set.Deleted); + existing_set.MovedFrom.AddRange (change_set.MovedFrom); + existing_set.MovedTo.AddRange (change_set.MovedTo); + existing_set.Notes.AddRange (change_set.Notes); + + existing_set.Added = existing_set.Added.Distinct ().ToList (); + existing_set.Edited = existing_set.Edited.Distinct ().ToList (); + existing_set.Deleted = existing_set.Deleted.Distinct ().ToList (); + + if (DateTime.Compare (existing_set.Timestamp, change_set.Timestamp) < 1) { + existing_set.FirstTimestamp = existing_set.Timestamp; + existing_set.Timestamp = change_set.Timestamp; + existing_set.Revision = change_set.Revision; + + } else { + existing_set.FirstTimestamp = change_set.Timestamp; + } + + squash = true; + } + } + + if (!squash) + stored_activity_day.Add (change_set); + + change_set_inserted = true; + break; + } + } + + if (!change_set_inserted) { + ActivityDay activity_day = new ActivityDay (change_set.Timestamp); + activity_day.Add (change_set); + activity_days.Add (activity_day); + } + } + + new Thread (new ThreadStart (delegate { + FetchAvatars (emails, 48); + })).Start (); + + string event_log_html = EventLogHTML; + string day_entry_html = DayEntryHTML; + string event_entry_html = EventEntryHTML; + string event_log = ""; + + foreach (ActivityDay activity_day in activity_days) { + string event_entries = ""; + + foreach (SparkleChangeSet change_set in activity_day) { + string event_entry = "
"; + + if (change_set.IsMagical) { + event_entry += "
Did something magical
"; + + } else { + if (change_set.Edited.Count > 0) { + foreach (string file_path in change_set.Edited) { + string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, + change_set.Folder, file_path}.Combine (); + + if (File.Exists (absolute_file_path)) + event_entry += "
" + file_path + "
"; + else + event_entry += "
" + file_path + "
"; + } + } + + if (change_set.Added.Count > 0) { + foreach (string file_path in change_set.Added) { + string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, + change_set.Folder, file_path}.Combine (); + + if (File.Exists (absolute_file_path)) + event_entry += "
" + file_path + "
"; + else + event_entry += "
" + file_path + "
"; + } + } + + if (change_set.Deleted.Count > 0) { + foreach (string file_path in change_set.Deleted) { + string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, + change_set.Folder, file_path}.Combine (); + + if (File.Exists (absolute_file_path)) + event_entry += "
" + file_path + "
"; + else + event_entry += "
" + file_path + "
"; + } + } + + if (change_set.MovedFrom.Count > 0) { + int i = 0; + foreach (string file_path in change_set.MovedFrom) { + string to_file_path = change_set.MovedTo [i]; + + string absolute_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, + change_set.Folder, file_path}.Combine (); + + string absolute_to_file_path = new string [] {SparkleConfig.DefaultConfig.FoldersPath, + change_set.Folder, to_file_path}.Combine (); + + if (File.Exists (absolute_file_path)) + event_entry += "
" + file_path + "
"; + else + event_entry += "
" + file_path + "
"; + + if (File.Exists (absolute_to_file_path)) + event_entry += "" + to_file_path + "
"; + else + event_entry += to_file_path + ""; + + i++; + } + } + } + + string comments = ""; + comments = "
"; + + if (change_set.Notes != null) { + change_set.Notes.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); + + foreach (SparkleNote note in change_set.Notes) { + + string note_avatar = GetAvatar (note.User.Email, 48); + if (File.Exists (note_avatar)) + note_avatar = "file://" + note_avatar; + else + note_avatar = ""; + + comments += "
" + + "

" + + note.User.Name + "

" + + note.Body + + "
"; + } + } + + comments += "
"; + + string change_set_avatar = GetAvatar (change_set.User.Email, 48); + if (File.Exists (change_set_avatar)) + change_set_avatar = "file://" + change_set_avatar; + else + change_set_avatar = ""; + + event_entry += "
"; + + string timestamp = change_set.Timestamp.ToString ("H:mm"); + + if (!change_set.FirstTimestamp.Equals (new DateTime ())) + timestamp = change_set.FirstTimestamp.ToString ("H:mm") + + " – " + timestamp; + + event_entries += event_entry_html.Replace ("", event_entry) + .Replace ("", change_set.User.Name) + .Replace ("", change_set_avatar) + .Replace ("", timestamp) + .Replace ("", change_set.Folder) + .Replace ("", change_set.Revision) + .Replace ("", AssignColor (change_set.Folder)) + .Replace ("", comments); + } + + string day_entry = ""; + DateTime today = DateTime.Now; + DateTime yesterday = DateTime.Now.AddDays (-1); + + if (today.Day == activity_day.DateTime.Day && + today.Month == activity_day.DateTime.Month && + today.Year == activity_day.DateTime.Year) { + + day_entry = day_entry_html.Replace ("", "Today"); + + } else if (yesterday.Day == activity_day.DateTime.Day && + yesterday.Month == activity_day.DateTime.Month && + yesterday.Year == activity_day.DateTime.Year) { + + day_entry = day_entry_html.Replace ("", "Yesterday"); + + } else { + if (activity_day.DateTime.Year != DateTime.Now.Year) { + + // TRANSLATORS: This is the date in the event logs + day_entry = day_entry_html.Replace ("", + activity_day.DateTime.ToString (_("dddd, MMMM d, yyyy"))); + + } else { + + // TRANSLATORS: This is the date in the event logs, without the year + day_entry = day_entry_html.Replace ("", + activity_day.DateTime.ToString (_("dddd, MMMM d"))); + } + } + + event_log += day_entry.Replace ("", event_entries); + } + + string html = event_log_html.Replace ("", event_log) + .Replace ("", UserName) + .Replace ("", "file://" + GetAvatar (UserEmail, 48)); + + return html; + } + + + // Creates a .desktop entry in autostart folder to + // start SparkleShare automatically at login + public abstract void EnableSystemAutostart (); + + // Installs a launcher so the user can launch SparkleShare + // from the Internet category if needed + public abstract void InstallLauncher (); + + // Adds the SparkleShare folder to the user's + // list of bookmarked places + public abstract void AddToBookmarks (); + + // Creates the SparkleShare folder in the user's home folder + public abstract bool CreateSparkleShareFolder (); + + // Opens the SparkleShare folder or an (optional) subfolder + public abstract void OpenSparkleShareFolder (string subfolder); + + + // Fires events for the current syncing state + public void UpdateState () + { + foreach (SparkleRepoBase repo in Repositories) { + if (repo.Status == SyncStatus.SyncDown || + repo.Status == SyncStatus.SyncUp || + repo.IsBuffering) { + + if (OnSyncing != null) + OnSyncing (); + + return; + + } else if (repo.HasUnsyncedChanges) { + if (OnError != null) + OnError (); + + return; + } + } + + if (OnIdle != null) + OnIdle (); + + FolderSize = GetFolderSize (); + + if (FolderSizeChanged != null) + FolderSizeChanged (FolderSize); + } + + + // Adds a repository to the list of repositories + private void AddRepository (string folder_path) + { + if (folder_path.Equals (SparkleConfig.DefaultConfig.TmpPath)) + return; + + string folder_name = Path.GetFileName (folder_path); + string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name); + + if (backend == null) + return; + + SparkleRepoBase repo = new SparkleRepoGit (folder_path, SparkleBackend.DefaultBackend); + + repo.NewChangeSet += delegate (SparkleChangeSet change_set) { + string message = FormatMessage (change_set); + + if (NotificationRaised != null) + NotificationRaised (change_set.User.Name, change_set.User.Email, message, repo.LocalPath); + }; + + repo.NewNote += delegate (string user_name, string user_email) { + if (NotificationRaised != null) + NotificationRaised (user_name, user_email, + "added a note to " + Path.GetFileName (repo.LocalPath), repo.LocalPath); + }; + + repo.ConflictResolved += delegate { + if (ConflictNotificationRaised != null) + ConflictNotificationRaised (); + }; + + repo.SyncStatusChanged += delegate (SyncStatus status) { +/* if (status == SyncStatus.SyncUp) { + foreach (string path in repo.UnsyncedFilePaths) + Console.WriteLine (path); + } +*/ + if (status == SyncStatus.Idle || + status == SyncStatus.SyncUp || + status == SyncStatus.SyncDown || + status == SyncStatus.Error) { + + UpdateState (); + } + }; + + repo.ChangesDetected += delegate { + UpdateState (); + }; + + Repositories.Add (repo); + } + + + // 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 < Repositories.Count; i++) { + SparkleRepoBase repo = Repositories [i]; + + if (repo.Name.Equals (folder_name)) { + repo.Dispose (); + Repositories.Remove (repo); + repo = null; + break; + } + } + } + + + // Updates the list of repositories with all the + // folders in the SparkleShare folder + private void PopulateRepositories () + { + Repositories = new List (); + + 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); + } + + if (FolderListChanged != null) + FolderListChanged (); + + FolderSize = GetFolderSize (); + + if (FolderSizeChanged != null) + FolderSizeChanged (FolderSize); + } + + + public bool NotificationsEnabled { + get { + string notifications_enabled = + SparkleConfig.DefaultConfig.GetConfigOption ("notifications"); + + if (String.IsNullOrEmpty (notifications_enabled)) { + SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); + return true; + + } else { + return notifications_enabled.Equals (bool.TrueString); + } + } + } + + + public void ToggleNotifications () { + bool notifications_enabled = + SparkleConfig.DefaultConfig.GetConfigOption ("notifications") + .Equals (bool.TrueString); + + if (notifications_enabled) + SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.FalseString); + else + SparkleConfig.DefaultConfig.SetConfigOption ("notifications", bool.TrueString); + } + + + private string GetFolderSize () + { + double folder_size = CalculateFolderSize ( + new DirectoryInfo (SparkleConfig.DefaultConfig.FoldersPath)); + + return FormatFolderSize (folder_size); + } + + + private string FormatMessage (SparkleChangeSet change_set) + { + string file_name = ""; + string message = ""; + + if (change_set.Added.Count > 0) { + file_name = change_set.Added [0]; + message = String.Format (_("added ‘{0}’"), file_name); + } + + if (change_set.MovedFrom.Count > 0) { + file_name = change_set.MovedFrom [0]; + message = String.Format (_("moved ‘{0}’"), file_name); + } + + if (change_set.Edited.Count > 0) { + file_name = change_set.Edited [0]; + message = String.Format (_("edited ‘{0}’"), file_name); + } + + if (change_set.Deleted.Count > 0) { + file_name = change_set.Deleted [0]; + message = String.Format (_("deleted ‘{0}’"), file_name); + } + + int changes_count = (change_set.Added.Count + + change_set.Edited.Count + + change_set.Deleted.Count + + change_set.MovedFrom.Count) - 1; + + if (changes_count > 0) { + string msg = Catalog.GetPluralString ("and {0} more", "and {0} more", changes_count); + message += " " + String.Format (msg, changes_count); + + } else if (changes_count < 0) { + message += _("did something magical"); + } + + return message; + } // TODO: move to bubbles controller + + + // Recursively gets a folder's size in bytes + private double CalculateFolderSize (DirectoryInfo parent) + { + if (!Directory.Exists (parent.ToString ())) + return 0; + + double size = 0; + + // Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential + // crashes when files are being queried whilst the files have already been deleted. + if (parent.Name.Equals ("rebase-apply") || + parent.Name.Equals (".tmp")) + return 0; + + try { + foreach (FileInfo file in parent.GetFiles()) { + if (!file.Exists) + return 0; + + size += file.Length; + } + + foreach (DirectoryInfo directory in parent.GetDirectories()) + size += CalculateFolderSize (directory); + + } catch (Exception) { + return 0; + } + + return size; + } + + + // Format a file size nicely with small caps. + // Example: 1048576 becomes "1 ᴍʙ" + private string FormatFolderSize (double byte_count) + { + if (byte_count >= 1099511627776) + return String.Format ("{0:##.##} ᴛʙ", Math.Round (byte_count / 1099511627776, 1)); + else if (byte_count >= 1073741824) + return String.Format ("{0:##.##} ɢʙ", Math.Round (byte_count / 1073741824, 1)); + else if (byte_count >= 1048576) + return String.Format ("{0:##.##} ᴍʙ", Math.Round (byte_count / 1048576, 1)); + else if (byte_count >= 1024) + return String.Format ("{0:##.##} ᴋʙ", Math.Round (byte_count / 1024, 1)); + else + return byte_count.ToString () + " bytes"; + } + + + public void OpenSparkleShareFolder () + { + OpenSparkleShareFolder (""); + } + + + // Adds the user's SparkleShare key to the ssh-agent, + // so all activity is done with this key + public void AddKey () + { + string keys_path = Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath); + string key_file_name = "sparkleshare." + UserEmail + ".key"; + + Process process = new Process (); + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = "ssh-add"; + process.StartInfo.Arguments = "\"" + Path.Combine (keys_path, key_file_name) + "\""; + process.Start (); + process.WaitForExit (); + } + + + public bool BackendIsPresent { + get { + return SparkleBackend.DefaultBackend.IsPresent; + } + } + + + // Looks up the user's name from the global configuration + public string UserName + { + get { + return SparkleConfig.DefaultConfig.User.Name; + } + + set { + SparkleConfig.DefaultConfig.User = new SparkleUser (value, UserEmail); + } + } + + + // Looks up the user's email from the global configuration + public string UserEmail + { + get { + return SparkleConfig.DefaultConfig.User.Email; + } + + set { + SparkleConfig.DefaultConfig.User = new SparkleUser (UserName, value); + } + } + + + // Generates and installs an RSA keypair to identify this system + public void GenerateKeyPair () + { + string keys_path = Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath); + string key_file_name = "sparkleshare." + UserEmail + ".key"; + string key_file_path = Path.Combine (keys_path, key_file_name); + + if (File.Exists (key_file_path)) { + SparkleHelpers.DebugInfo ("Config", "Key already exists ('" + key_file_name + "'), " + + "leaving it untouched"); + return; + } + + if (!Directory.Exists (keys_path)) + Directory.CreateDirectory (keys_path); + + if (!File.Exists (key_file_name)) { + Process process = new Process () { + EnableRaisingEvents = true + }; + + process.StartInfo.WorkingDirectory = keys_path; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.FileName = "ssh-keygen"; + + // -t is the crypto type + // -P is the password (none) + // -f is the file name to store the private key in + process.StartInfo.Arguments = "-t rsa -P \"\" -f " + key_file_name; + + process.Exited += delegate { + SparkleHelpers.DebugInfo ("Config", "Created private key '" + key_file_name + "'"); + SparkleHelpers.DebugInfo ("Config", "Created public key '" + key_file_name + ".pub'"); + + // Create an easily accessible copy of the public + // key in the user's SparkleShare folder + File.Copy (key_file_path + ".pub", + Path.Combine (SparklePath, UserName + "'s key.txt")); + }; + + process.Start (); + process.WaitForExit (); + } + } + + + // Gets the avatar for a specific email address and size + public void FetchAvatars (List emails, int size) + { + List old_avatars = new List (); + bool avatar_fetched = false; + string avatar_path = new string [] { + Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath), + "icons", size + "x" + size, "status"}.Combine (); + + if (!Directory.Exists (avatar_path)) { + Directory.CreateDirectory (avatar_path); + SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'"); + } + + foreach (string raw_email in emails) { + + // Gravatar wants lowercase emails + string email = raw_email.ToLower (); + string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email); + + if (File.Exists (avatar_file_path)) { + FileInfo avatar_info = new FileInfo (avatar_file_path); + + // Delete avatars older than a month + if (avatar_info.CreationTime < DateTime.Now.AddMonths (-1)) { + avatar_info.Delete (); + old_avatars.Add (email); + } + + } else { + WebClient client = new WebClient (); + string url = "http://gravatar.com/avatar/" + GetMD5 (email) + + ".jpg?s=" + size + "&d=404"; + + try { + // Fetch the avatar + byte [] buffer = client.DownloadData (url); + + // Write the avatar data to a + // if not empty + if (buffer.Length > 255) { + avatar_fetched = true; + File.WriteAllBytes (avatar_file_path, buffer); + SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar for " + email); + } + + } catch (WebException e) { + SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email); + + // Stop downloading further avatars if we have no internet access + if (e.Status == WebExceptionStatus.Timeout) + break; + } + } + } + + // Fetch new versions of the avatars that we + // deleted because they were too old + if (old_avatars.Count > 0) + FetchAvatars (old_avatars, size); + + if (AvatarFetched != null && avatar_fetched) + AvatarFetched (); + } + + + public string GetAvatar (string email, int size) + { + string avatar_file_path = SparkleHelpers.CombineMore ( + Path.GetDirectoryName (SparkleConfig.DefaultConfig.FullPath), "icons", + size + "x" + size, "status", "avatar-" + email); + + return avatar_file_path; + } + + + public void FetchFolder (string server, string remote_folder) + { + server = server.Trim (); + remote_folder = remote_folder.Trim (); + + string tmp_path = SparkleConfig.DefaultConfig.TmpPath; + if (!Directory.Exists (tmp_path)) + Directory.CreateDirectory (tmp_path); + + // Strip the '.git' from the name + string canonical_name = Path.GetFileNameWithoutExtension (remote_folder); + string tmp_folder = Path.Combine (tmp_path, canonical_name); + + string backend = null; + +/* if (remote_folder.EndsWith (".hg")) { + remote_folder = remote_folder.Substring (0, (remote_folder.Length - 3)); + fetcher = new SparkleFetcherHg (server, remote_folder, tmp_folder); + backend = "Hg"; + + } else if (remote_folder.EndsWith (".scp")) { + remote_folder = remote_folder.Substring (0, (remote_folder.Length - 4)); + fetcher = new SparkleFetcherScp (server, remote_folder, tmp_folder); + backend = "Scp"; + + } else {*/ + this.fetcher = new SparkleFetcherGit (server, remote_folder, tmp_folder); + backend = "Git"; + //} + + bool target_folder_exists = Directory.Exists ( + Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name)); + + // Add a numbered suffix to the nameif a folder with the same name + // already exists. Example: "Folder (2)" + int i = 1; + while (target_folder_exists) { + i++; + target_folder_exists = Directory.Exists ( + Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name + " (" + i + ")")); + } + + string target_folder_name = canonical_name; + if (i > 1) + target_folder_name += " (" + i + ")"; + + this.fetcher.Finished += delegate { + + // Needed to do the moving + SparkleHelpers.ClearAttributes (tmp_folder); + string target_folder_path = Path.Combine ( + SparkleConfig.DefaultConfig.FoldersPath, target_folder_name); + + try { + Directory.Move (tmp_folder, target_folder_path); + } catch (Exception e) { + SparkleHelpers.DebugInfo ("Controller", "Error moving folder: " + e.Message); + } + + SparkleConfig.DefaultConfig.AddFolder (target_folder_name, this.fetcher.RemoteUrl, backend); + AddRepository (target_folder_path); + + if (FolderFetched != null) + FolderFetched (); + + FolderSize = GetFolderSize (); + + if (FolderSizeChanged != null) + FolderSizeChanged (FolderSize); + + if (FolderListChanged != null) + FolderListChanged (); + + this.fetcher.Dispose (); + + if (Directory.Exists (tmp_path)) + Directory.Delete (tmp_path, true); + }; + + + this.fetcher.Failed += delegate { + if (FolderFetchError != null) + FolderFetchError (this.fetcher.RemoteUrl); + + this.fetcher.Dispose (); + + if (Directory.Exists (tmp_path)) + Directory.Delete (tmp_path, true); + }; + + + this.fetcher.ProgressChanged += delegate (double percentage) { + if (FolderFetching != null) + FolderFetching (percentage); + }; + + + this.fetcher.Start (); + } + + + public void StopFetcher () + { + if (fetcher != null) + fetcher.Stop (); + } + + + // Creates an MD5 hash of input + private string GetMD5 (string s) + { + MD5 md5 = new MD5CryptoServiceProvider (); + Byte[] bytes = ASCIIEncoding.Default.GetBytes (s); + Byte[] encoded_bytes = md5.ComputeHash (bytes); + return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", ""); + } + + + // Checks whether there are any folders syncing and + // quits if safe + public void TryQuit () + { + foreach (SparkleRepoBase repo in Repositories) { + if (repo.Status == SyncStatus.SyncUp || + repo.Status == SyncStatus.SyncDown || + repo.IsBuffering) { + + if (OnQuitWhileSyncing != null) + OnQuitWhileSyncing (); + + return; + } + } + + Quit (); + } + + + public void Quit () + { + foreach (SparkleRepoBase repo in Repositories) + repo.Dispose (); + + Environment.Exit (0); + } + + + // Checks to see if an email address is valid + public bool IsValidEmail (string email) + { + Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", RegexOptions.IgnoreCase); + return regex.IsMatch (email); + } + + + + + public void AddNoteToFolder (string folder_name, string revision, string note) + { + folder_name = folder_name.Replace ("%20", " "); + note = note.Replace ("%20", " "); + + foreach (SparkleRepoBase repo in Repositories) { + if (repo.Name.Equals (folder_name)) + repo.AddNote (revision, note); + } + } + + + + + private string [] tango_palette = new string [] {"#eaab00", "#e37222", + "#3892ab", "#33c2cb", "#19b271", "#9eab05", "#8599a8", "#9ca696", + "#b88454", "#cc0033", "#8f6678", "#8c6cd0", "#796cbf", "#4060af", + "#aa9c8f", "#818a8f"}; + + private string AssignColor (string s) + { + string hash = GetMD5 (s).Substring (0, 8); + string numbers = Regex.Replace (hash, "[a-z]", ""); + int number = 3 + int.Parse (numbers); + return this.tango_palette [number % this.tango_palette.Length]; + } + } + + + public class ChangeSet : SparkleChangeSet { } + + + // All change sets that happened on a day + public class ActivityDay : List + { + public DateTime DateTime; + + public ActivityDay (DateTime date_time) + { + DateTime = date_time; + DateTime = new DateTime (DateTime.Year, DateTime.Month, DateTime.Day); + } + } +} diff --git a/SparkleShare/SparkleLinController.cs b/SparkleShare/SparkleLinController.cs deleted file mode 100755 index 0cc2168e..00000000 --- a/SparkleShare/SparkleLinController.cs +++ /dev/null @@ -1,214 +0,0 @@ -// SparkleShare, a collaboration and sharing tool. -// Copyright (C) 2010 Hylke Bons -// -// 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 . - - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; - -using Mono.Unix; -using SparkleLib; - -namespace SparkleShare { - - public class SparkleLinController : SparkleController { - - public SparkleLinController () : base () - { - - } - - - // Creates a .desktop entry in autostart folder to - // start SparkleShare automatically at login - public override void EnableSystemAutostart () - { - string autostart_path = Path.Combine (Environment.GetFolderPath ( - Environment.SpecialFolder.ApplicationData), "autostart"); - - string desktopfile_path = Path.Combine (autostart_path, "sparkleshare.desktop"); - - if (!Directory.Exists (autostart_path)) - Directory.CreateDirectory (autostart_path); - - if (!File.Exists (desktopfile_path)) { - TextWriter writer = new StreamWriter (desktopfile_path); - writer.WriteLine ("[Desktop Entry]\n" + - "Type=Application\n" + - "Name=SparkleShare\n" + - "Exec=sparkleshare start\n" + - "Icon=folder-sparkleshare\n" + - "Terminal=false\n" + - "X-GNOME-Autostart-enabled=true\n" + - "Categories=Network"); - writer.Close (); - - // Give the launcher the right permissions so it can be launched by the user - UnixFileInfo file_info = new UnixFileInfo (desktopfile_path); - file_info.Create (FileAccessPermissions.UserReadWriteExecute); - - SparkleHelpers.DebugInfo ("Controller", "Enabled autostart on login"); - } - } - - - // Installs a launcher so the user can launch SparkleShare - // from the Internet category if needed - public override void InstallLauncher () - { - string apps_path = - new string [] {SparkleConfig.DefaultConfig.HomePath, - ".local", "share", "applications"}.Combine (); - - string desktopfile_path = Path.Combine (apps_path, "sparkleshare.desktop"); - - if (!File.Exists (desktopfile_path)) { - if (!Directory.Exists (apps_path)) - Directory.CreateDirectory (apps_path); - - TextWriter writer = new StreamWriter (desktopfile_path); - writer.WriteLine ("[Desktop Entry]\n" + - "Type=Application\n" + - "Name=SparkleShare\n" + - "Comment=Share documents\n" + - "Exec=sparkleshare start\n" + - "Icon=folder-sparkleshare\n" + - "Terminal=false\n" + - "Categories=Network;"); - writer.Close (); - - // Give the launcher the right permissions so it can be launched by the user - UnixFileInfo file_info = new UnixFileInfo (desktopfile_path); - file_info.FileAccessPermissions = FileAccessPermissions.UserReadWriteExecute; - - SparkleHelpers.DebugInfo ("Controller", "Created '" + desktopfile_path + "'"); - } - } - - - // Adds the SparkleShare folder to the user's - // list of bookmarked places - public override void AddToBookmarks () - { - string bookmarks_file_path = Path.Combine (SparkleConfig.DefaultConfig.HomePath, ".gtk-bookmarks"); - string sparkleshare_bookmark = "file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"; - - if (File.Exists (bookmarks_file_path)) { - StreamReader reader = new StreamReader (bookmarks_file_path); - string bookmarks = reader.ReadToEnd (); - reader.Close (); - - if (!bookmarks.Contains (sparkleshare_bookmark)) { - TextWriter writer = File.AppendText (bookmarks_file_path); - writer.WriteLine ("file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"); - writer.Close (); - } - } else { - StreamWriter writer = new StreamWriter (bookmarks_file_path); - writer.WriteLine ("file://" + SparkleConfig.DefaultConfig.FoldersPath + " SparkleShare"); - writer.Close (); - } - } - - - // Creates the SparkleShare folder in the user's home folder - public override bool CreateSparkleShareFolder () - { - if (!Directory.Exists (SparkleConfig.DefaultConfig.FoldersPath)) { - - Directory.CreateDirectory (SparkleConfig.DefaultConfig.FoldersPath); - SparkleHelpers.DebugInfo ("Controller", "Created '" + SparkleConfig.DefaultConfig.FoldersPath + "'"); - - string gvfs_command_path = - new string [] {Path.VolumeSeparatorChar.ToString (), - "usr", "bin", "gvfs-set-attribute"}.Combine (); - - // Add a special icon to the SparkleShare folder - if (File.Exists (gvfs_command_path)) { - Process process = new Process (); - - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.FileName = "gvfs-set-attribute"; - - // Clear the custom (legacy) icon path - process.StartInfo.Arguments = "-t unset " + SparkleConfig.DefaultConfig.FoldersPath + " metadata::custom-icon"; - process.Start (); - process.WaitForExit (); - - // Give the SparkleShare folder an icon name, so that it scales - process.StartInfo.Arguments = SparkleConfig.DefaultConfig.FoldersPath + " metadata::custom-icon-name 'folder-sparkleshare'"; - process.Start (); - process.WaitForExit (); - } - - return true; - } - - return false; - } - - - public override string EventLogHTML { - get { - string path = new string [] {Defines.PREFIX, - "share", "sparkleshare", "html", "event-log.html"}.Combine (); - - string html = String.Join (Environment.NewLine, File.ReadAllLines (path)); - - html = html.Replace ("", "file://" + - new string [] {Defines.PREFIX, "share", "sparkleshare", "html", "jquery.js"}.Combine ()); - - return html; - } - } - - - public override string DayEntryHTML { - get { - string path = new string [] {Defines.PREFIX, - "share", "sparkleshare", "html", "day-entry.html"}.Combine (); - - return String.Join (Environment.NewLine, File.ReadAllLines (path)); - } - } - - - public override string EventEntryHTML { - get { - string path = new string [] {Defines.PREFIX, - "share", "sparkleshare", "html", "event-entry.html"}.Combine (); - - return String.Join (Environment.NewLine, File.ReadAllLines (path)); - } - } - - - public override void OpenSparkleShareFolder (string subfolder) - { - string folder = Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, subfolder); - - Process process = new Process (); - process.StartInfo.FileName = "xdg-open"; - process.StartInfo.Arguments = "\"" + folder + "\""; - process.Start (); - } - } -} diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index ad2795f7..199f702d 100755 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -9,6 +9,7 @@ SparkleShare 2.0 SparkleShare + v3.5 true @@ -37,15 +38,9 @@
- - - {2C914413-B31C-4362-93C7-1AE34F09112A} - SparkleLib - - - + @@ -63,7 +58,6 @@ - @@ -78,5 +72,6 @@ + diff --git a/SparkleShare/SparkleShare.sln b/SparkleShare/SparkleShare.sln new file mode 100644 index 00000000..9716fc21 --- /dev/null +++ b/SparkleShare/SparkleShare.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare", "SparkleShare.csproj", "{728483AA-E34B-4441-BF2C-C8BC2901E4E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = SparkleShare.csproj + EndGlobalSection +EndGlobal