// Merges the fetched changes private bool Merge() { string message = FormatCommitMessage(); if (message != null) { Add(); Commit(message); } SparkleGit git; // Stop if we're already in a merge because something went wrong if (this.in_merge) { git = new SparkleGit(LocalPath, "merge --abort"); git.StartAndWaitForExit(); return(false); } // Temporarily change the ignorecase setting to true to avoid // conflicts in file names due to letter case changes git = new SparkleGit(LocalPath, "config core.ignorecase true"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "merge FETCH_HEAD"); git.StartInfo.RedirectStandardOutput = false; string error_output = git.StartAndReadStandardError(); if (git.ExitCode != 0) { // Stop when we can't merge due to locked local files // error: cannot stat 'filename': Permission denied if (error_output.Contains("error: cannot stat")) { Error = ErrorStatus.UnreadableFiles; SparkleLogger.LogInfo("Git", Name + " | Error status changed to " + Error); git = new SparkleGit(LocalPath, "merge --abort"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(false); } else { SparkleLogger.LogInfo("Git", error_output); SparkleLogger.LogInfo("Git", Name + " | Conflict detected, trying to get out..."); while (this.in_merge && HasLocalChanges) { try { ResolveConflict(); } catch (Exception e) { SparkleLogger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again...", e); } } SparkleLogger.LogInfo("Git", Name + " | Conflict resolved"); } } git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(true); }
public override bool SyncUp() { if (!Add()) { Error = ErrorStatus.UnreadableFiles; return(false); } string message = base.status_message.Replace("\"", "\\\""); if (string.IsNullOrEmpty(message)) { message = FormatCommitMessage(); } if (message != null) { Commit(message); } if (this.use_git_bin) { SparkleGitBin git_bin = new SparkleGitBin(LocalPath, "push"); git_bin.StartAndWaitForExit(); // TODO: Progress } SparkleGit git = new SparkleGit(LocalPath, "push --progress \"" + RemoteUrl + "\" " + this.branch); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = this.progress_regex.Match(line); double speed = 0.0; double number = 0.0; if (match.Success) { try { number = double.Parse(match.Groups [1].Value, new CultureInfo("en-US")); } catch (FormatException) { SparkleLogger.LogInfo("Git", "Error parsing progress: \"" + match.Groups [1] + "\""); } // The pushing progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Writing objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); Match speed_match = this.speed_regex.Match(line); if (speed_match.Success) { try { speed = double.Parse(speed_match.Groups [1].Value, new CultureInfo("en-US")) * 1024; } catch (FormatException) { SparkleLogger.LogInfo("Git", "Error parsing speed: \"" + speed_match.Groups [1] + "\""); } if (speed_match.Groups [2].Value.Equals("M")) { speed = speed * 1024; } } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); if (FindError(line)) { return(false); } } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { ClearCache(); string salt_file_path = new string [] { LocalPath, ".git", "salt" }.Combine(); // If the repo is encrypted, create a branch to // store the salt in and push it to the host if (File.Exists(salt_file_path)) { string salt = File.ReadAllText(salt_file_path).Trim(); SparkleGit git_salt = new SparkleGit(LocalPath, "branch salt-" + salt); git_salt.StartAndWaitForExit(); git_salt = new SparkleGit(LocalPath, "push origin salt-" + salt); git_salt.StartAndWaitForExit(); File.Delete(salt_file_path); } return(true); } else { Error = ErrorStatus.HostUnreachable; return(false); } }
public override bool SyncDown() { SparkleGit git = new SparkleGit(LocalPath, "fetch --progress \"" + RemoteUrl + "\" " + this.branch); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = this.progress_regex.Match(line); double speed = 0.0; double number = 0.0; if (match.Success) { try { number = double.Parse(match.Groups [1].Value, new CultureInfo("en-US")); } catch (FormatException) { SparkleLogger.LogInfo("Git", "Error parsing progress: \"" + match.Groups [1] + "\""); } // The fetching progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Receiving objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); Match speed_match = this.speed_regex.Match(line); if (speed_match.Success) { try { speed = double.Parse(speed_match.Groups [1].Value, new CultureInfo("en-US")) * 1024; } catch (FormatException) { SparkleLogger.LogInfo("Git", "Error parsing speed: \"" + speed_match.Groups [1] + "\""); } if (speed_match.Groups [2].Value.Equals("M")) { speed = speed * 1024; } } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); if (FindError(line)) { return(false); } } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { if (Merge()) { ClearCache(); return(true); } else { return(false); } } else { Error = ErrorStatus.HostUnreachable; return(false); } }
public void FinishFetcher() { this.watcher.EnableRaisingEvents = false; this.fetcher.Complete(); string canonical_name = Path.GetFileName(this.fetcher.RemoteUrl.AbsolutePath); canonical_name = canonical_name.Replace("-crypto", ""); canonical_name = canonical_name.Replace("_", " "); bool target_folder_exists = Directory.Exists( Path.Combine(this.config.FoldersPath, canonical_name)); // Add a numbered suffix to the name if a folder with the same name // already exists. Example: "Folder (2)" int suffix = 1; while (target_folder_exists) { suffix++; target_folder_exists = Directory.Exists( Path.Combine(this.config.FoldersPath, canonical_name + " (" + suffix + ")")); } string target_folder_name = canonical_name; if (suffix > 1) { target_folder_name += " (" + suffix + ")"; } string target_folder_path = Path.Combine(this.config.FoldersPath, target_folder_name); try { Directory.Move(this.fetcher.TargetFolder, target_folder_path); } catch (Exception e) { SparkleLogger.LogInfo("Controller", "Error moving directory: \"" + e.Message + "\", trying again..."); try { ClearDirectoryAttributes(this.fetcher.TargetFolder); Directory.Move(this.fetcher.TargetFolder, target_folder_path); } catch (Exception x) { SparkleLogger.LogInfo("Controller", "Error moving directory: " + x.Message); this.watcher.EnableRaisingEvents = true; return; } } string backend = SparkleFetcherBase.GetBackend(this.fetcher.RemoteUrl.AbsolutePath); this.config.AddFolder(target_folder_name, this.fetcher.Identifier, this.fetcher.RemoteUrl.ToString(), backend); FolderFetched(this.fetcher.RemoteUrl.ToString(), this.fetcher.Warnings.ToArray()); AddRepository(target_folder_path); FolderListChanged(); this.fetcher.Dispose(); this.fetcher = null; this.watcher.EnableRaisingEvents = true; }
public string GetAvatar(string email, int size) { ServicePointManager.ServerCertificateValidationCallback = GetAvatarValidationCallBack; string fetch_avatars_option = this.config.GetConfigOption("fetch_avatars"); if (fetch_avatars_option != null && fetch_avatars_option.Equals(bool.FalseString)) { return(null); } email = email.ToLower(); if (this.skipped_avatars.Contains(email)) { return(null); } string avatars_path = new string [] { Path.GetDirectoryName(this.config.FullPath), "avatars", size + "x" + size }.Combine(); string avatar_file_path = Path.Combine(avatars_path, email.MD5() + ".png"); if (File.Exists(avatar_file_path)) { if (new FileInfo(avatar_file_path).CreationTime < DateTime.Now.AddDays(-1)) { File.Delete(avatar_file_path); } else { return(avatar_file_path); } } WebClient client = new WebClient(); string url = "https://gravatar.com/avatar/" + email.MD5() + ".png?s=" + size + "&d=404"; try { byte [] buffer = client.DownloadData(url); if (buffer.Length > 255) { if (!Directory.Exists(avatars_path)) { Directory.CreateDirectory(avatars_path); SparkleLogger.LogInfo("Controller", "Created '" + avatars_path + "'"); } File.WriteAllBytes(avatar_file_path, buffer); SparkleLogger.LogInfo("Controller", "Fetched " + size + "x" + size + " avatar for " + email); return(avatar_file_path); } else { return(null); } } catch (WebException e) { SparkleLogger.LogInfo("Controller", "Error fetching avatar for " + email + ": " + e.Message); skipped_avatars.Add(email); return(null); } }
private void CheckRepositories() { lock (this.check_repos_lock) { string path = Config.FoldersPath; // Detect any renames foreach (string folder_path in Directory.GetDirectories(path)) { string folder_name = Path.GetFileName(folder_path); if (folder_name.Equals(".tmp")) { continue; } if (Config.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 (Config.IdentifierExists(identifier)) { RemoveRepository(folder_path); Config.RenameFolder(identifier, folder_name); string new_folder_path = Path.Combine(path, folder_name); AddRepository(new_folder_path); SparkleLogger.LogInfo("Controller", "Renamed folder with identifier " + identifier + " to '" + folder_name + "'"); } } } // Remove any deleted folders foreach (string folder_name in Config.Folders) { string folder_path = new SparkleFolder(folder_name).FullPath; if (!Directory.Exists(folder_path)) { Config.RemoveFolder(folder_name); RemoveRepository(folder_path); SparkleLogger.LogInfo("Controller", "Removed folder '" + folder_name + "' from config"); } else { AddRepository(folder_path); } } // Remove any duplicate folders string previous_name = ""; foreach (string folder_name in Config.Folders) { if (!string.IsNullOrEmpty(previous_name) && folder_name.Equals(previous_name)) { Config.RemoveFolder(folder_name); } else { previous_name = folder_name; } } FolderListChanged(); } }
public static string GetAvatar(string email, int size, string target_path) { ServicePointManager.ServerCertificateValidationCallback = GetAvatarValidationCallBack; email = email.ToLower(); if (skipped_avatars.Contains(email)) { return(null); } string avatars_path = new string [] { Path.GetDirectoryName(target_path), "avatars", size + "x" + size }.Combine(); string avatar_file_path; try { avatar_file_path = Path.Combine(avatars_path, email.MD5() + ".png"); } catch (InvalidOperationException e) { SparkleLogger.LogInfo("Avatars", "Error fetching avatar for " + email, e); return(null); } if (File.Exists(avatar_file_path)) { if (new FileInfo(avatar_file_path).CreationTime < DateTime.Now.AddDays(-1)) { File.Delete(avatar_file_path); } else { return(avatar_file_path); } } WebClient client = new WebClient(); string url = "https://gravatar.com/avatar/" + email.MD5() + ".png?s=" + size + "&d=404"; try { byte [] buffer = client.DownloadData(url); if (buffer.Length > 255) { if (!Directory.Exists(avatars_path)) { Directory.CreateDirectory(avatars_path); SparkleLogger.LogInfo("Avatars", "Created '" + avatars_path + "'"); } File.WriteAllBytes(avatar_file_path, buffer); SparkleLogger.LogInfo("Avatars", "Fetched " + size + "x" + size + " avatar for " + email); return(avatar_file_path); } else { return(null); } } catch (Exception e) { SparkleLogger.LogInfo("Avatars", "Error fetching avatar for " + email, e); skipped_avatars.Add(email); return(null); } }
public override bool SyncDown() { SparkleGit git = new SparkleGit(LocalPath, "fetch --progress \"" + RemoteUrl + "\" master"); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; Regex progress_regex = new Regex(@"([0-9]+)%", RegexOptions.Compiled); while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = progress_regex.Match(line); string speed = ""; double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The fetching progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Receiving objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); if (line.Contains("|")) { speed = line.Substring(line.IndexOf("|") + 1).Trim(); speed = speed.Replace(", done.", "").Trim(); speed = speed.Replace("i", ""); speed = speed.Replace("KB/s", "ᴋʙ/s"); speed = speed.Replace("MB/s", "ᴍʙ/s"); } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { Rebase(); string identifier_file_path = Path.Combine(LocalPath, ".sparkleshare"); File.SetAttributes(identifier_file_path, FileAttributes.Hidden); ClearCache(); return(true); } else { return(false); } }
private void ResolveConflict() { // This is a list of conflict status codes that Git uses, their // meaning, and how SparkleShare should handle them. // // DD unmerged, both deleted -> Do nothing // AU unmerged, added by us -> Use server's, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use server's, save ours as a timestamped copy // DU unmerged, deleted by us -> Use server's // AA unmerged, both added -> Use server's, save ours as a timestamped copy // UU unmerged, both modified -> Use server's, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files // // Note that a rebase merge works by replaying each commit from the working branch on // top of the upstream branch. Because of this, when a merge conflict happens the // side reported as 'ours' is the so-far rebased series, starting with upstream, // and 'theirs' is the working branch. In other words, the sides are swapped. // // So: 'ours' means the 'server's version' and 'theirs' means the 'local version' after this comment SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput(); string [] lines = output.Split("\n".ToCharArray()); bool changes_added = false; foreach (string line in lines) { string conflicting_path = line.Substring(3); conflicting_path = EnsureSpecialCharacters(conflicting_path); SparkleLogger.LogInfo("Git", Name + " | Conflict type: " + line); // Ignore conflicts in the .sparkleshare file and use the local version if (conflicting_path.EndsWith(".sparkleshare") || conflicting_path.EndsWith(".empty")) { // Recover local version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); File.SetAttributes(Path.Combine(LocalPath, conflicting_path), FileAttributes.Hidden); changes_added = true; continue; } // Both the local and server version have been modified if (line.StartsWith("UU") || line.StartsWith("AA") || line.StartsWith("AU") || line.StartsWith("UA")) { // Recover local version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); // Append a timestamp to local version. // Windows doesn't allow colons in the file name, so // we use "h" between the hours and minutes instead. string timestamp = DateTime.Now.ToString("MMM d H\\hmm"); string their_path = Path.GetFileNameWithoutExtension(conflicting_path) + " (" + base.local_config.User.Name + ", " + timestamp + ")" + Path.GetExtension(conflicting_path); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); string abs_their_path = Path.Combine(LocalPath, their_path); File.Move(abs_conflicting_path, abs_their_path); // Recover server version SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); changes_added = true; // The local version has been modified, but the server version was removed } else if (line.StartsWith("DU")) { // The modified local version is already in the // checkout, so it just needs to be added. // // We need to specifically mention the file, so // we can't reuse the Add () method SparkleGit git_add = new SparkleGit(LocalPath, "add \"" + conflicting_path + "\""); git_add.StartAndWaitForExit(); changes_added = true; } } Add(); SparkleGit git; if (changes_added) { git = new SparkleGit(LocalPath, "rebase --continue"); } else { git = new SparkleGit(LocalPath, "rebase --skip"); } git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); }
// Merges the fetched changes private bool Rebase() { if (HasLocalChanges) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } // Temporarily change the ignorecase setting to true to avoid // conflicts in file names due to case changes SparkleGit git = new SparkleGit(LocalPath, "config core.ignorecase true"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "rebase FETCH_HEAD"); git.StartInfo.RedirectStandardOutput = false; string error_output = git.StartAndReadStandardError(); if (git.ExitCode != 0) { // Stop when we can't rebase due to locked local files // error: cannot stat 'filename': Permission denied if (error_output.Contains("error: cannot stat")) { Error = ErrorStatus.LockedFiles; SparkleLogger.LogInfo("Git", Name + " | Error status changed to " + Error); git = new SparkleGit(LocalPath, "rebase --abort"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(false); } else { SparkleLogger.LogInfo("Git", Name + " | Conflict detected, trying to get out..."); string rebase_apply_path = new string [] { LocalPath, ".git", "rebase-apply" }.Combine(); while (Directory.Exists(rebase_apply_path) && HasLocalChanges) { try { ResolveConflict(); } catch (IOException e) { SparkleLogger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again...", e); } } SparkleLogger.LogInfo("Git", Name + " | Conflict resolved"); OnConflictResolved(); } } git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(true); }
public override bool SyncUp() { if (HasLocalChanges) { Add(); string message = FormatCommitMessage(); Commit(message); } SparkleGit git; if (this.use_git_bin) { if (this.remote_url_is_set) { git = new SparkleGit(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\""); git.StartAndWaitForExit(); this.remote_url_is_set = true; } SparkleGitBin git_bin = new SparkleGitBin(LocalPath, "push"); git_bin.StartAndWaitForExit(); // TODO: Progress } git = new SparkleGit(LocalPath, "push --progress " + // Redirects progress stats to standarderror "\"" + RemoteUrl + "\" master"); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; Regex progress_regex = new Regex(@"([0-9]+)%", RegexOptions.Compiled); while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = progress_regex.Match(line); string speed = ""; double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The pushing progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Writing objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { if (line.StartsWith("ERROR: QUOTA EXCEEDED")) { int quota_limit = int.Parse(line.Substring(21).Trim()); throw new QuotaExceededException("Quota exceeded", quota_limit); } // "Writing objects" stage number = (number / 100 * 80 + 20); if (line.Contains("|")) { speed = line.Substring(line.IndexOf("|") + 1).Trim(); speed = speed.Replace(", done.", "").Trim(); speed = speed.Replace("i", ""); speed = speed.Replace("KB/s", "ᴋʙ/s"); speed = speed.Replace("MB/s", "ᴍʙ/s"); } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { ClearCache(); return(true); } else { return(false); } }
public SparkleSetup() : base() { Controller.HideWindowEvent += delegate { Application.Invoke(delegate { HideAll(); }); }; Controller.ShowWindowEvent += delegate { Application.Invoke(delegate { ShowAll(); Present(); }); }; Controller.ChangePageEvent += delegate(PageType type, string [] warnings) { Application.Invoke(delegate { Reset(); switch (type) { case PageType.Setup: { Header = "Welcome to CmisSync!"; Description = "First off, what's your name and email?\nThis information is only visible to team members."; Table table = new Table(2, 3, true) { RowSpacing = 6, ColumnSpacing = 6 }; Label name_label = new Label("<b>" + "Full Name:" + "</b>") { UseMarkup = true, Xalign = 1 }; Entry name_entry = new Entry(UnixUserInfo.GetRealUser().RealName) { Xalign = 0, ActivatesDefault = true }; Entry email_entry = new Entry() { Xalign = 0, ActivatesDefault = true }; name_entry.Changed += delegate { Controller.CheckSetupPage(); }; email_entry.Changed += delegate { Controller.CheckSetupPage(); }; Label email_label = new Label("<b>" + "Email:" + "</b>") { UseMarkup = true, Xalign = 1 }; table.Attach(name_label, 0, 1, 0, 1); table.Attach(name_entry, 1, 2, 0, 1); table.Attach(email_label, 0, 1, 1, 2); table.Attach(email_entry, 1, 2, 1, 2); VBox wrapper = new VBox(false, 9); wrapper.PackStart(table, true, false, 0); Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.SetupPageCancelled(); }; Button continue_button = new Button("Continue") { Sensitive = false }; continue_button.Clicked += delegate(object o, EventArgs args) { string full_name = name_entry.Text; string email = email_entry.Text; Controller.SetupPageCompleted(); }; AddButton(cancel_button); AddButton(continue_button); Add(wrapper); Controller.UpdateSetupContinueButtonEvent += delegate(bool button_enabled) { Application.Invoke(delegate { continue_button.Sensitive = button_enabled; }); }; Controller.CheckSetupPage(); break; } case PageType.Add1: { Header = "Where is your organization's server?"; VBox layout_vertical = new VBox(false, 12); HBox layout_fields = new HBox(true, 12); VBox layout_address = new VBox(true, 0); VBox layout_user = new VBox(true, 0); VBox layout_password = new VBox(true, 0); // Address Entry address_entry = new Entry() { Text = Controller.PreviousAddress, Sensitive = (Controller.SelectedPlugin.Address == null), ActivatesDefault = true }; Label address_example = new Label() { Xalign = 0, UseMarkup = true, Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + Controller.SelectedPlugin.AddressExample + "</span>" }; Label address_help_label = new Label() { Text = "Help: ", // TODO FontSize = 11, // TODO Foreground = new SolidColorBrush(Color.FromRgb(128, 128, 128)) }; /* TODO Run run = new Run("Where to find this address"); * Hyperlink link = new Hyperlink(run); * link.NavigateUri = new Uri("https://github.com/nicolas-raoul/CmisSync/wiki/What-address"); * address_help_label.Inlines.Add(link); * link.RequestNavigate += (sender, e) => * { * System.Diagnostics.Process.Start(e.Uri.ToString()); * };*/ Label address_error_label = new Label() { // TODO FontSize = 11, // TODO Foreground = new SolidColorBrush(Color.FromRgb(255, 128, 128)), // TODO Visibility = Visibility.Hidden }; // User Entry user_entry = new Entry() { Text = Controller.PreviousPath, Sensitive = (Controller.SelectedPlugin.User == null), ActivatesDefault = true }; Label user_example = new Label() { Xalign = 0, UseMarkup = true, Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + Controller.SelectedPlugin.UserExample + "</span>" }; // Password Entry password_entry = new Entry() { Text = Controller.PreviousPath, Sensitive = (Controller.SelectedPlugin.Password == null), ActivatesDefault = true }; Label password_example = new Label() { Xalign = 0, UseMarkup = true, Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + Controller.SelectedPlugin.PasswordExample + "</span>" }; Controller.ChangeAddressFieldEvent += delegate(string text, string example_text, FieldState state) { Application.Invoke(delegate { address_entry.Text = text; address_entry.Sensitive = (state == FieldState.Enabled); address_example.Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + example_text + "</span>"; }); }; Controller.ChangeUserFieldEvent += delegate(string text, string example_text, FieldState state) { Application.Invoke(delegate { user_entry.Text = text; user_entry.Sensitive = (state == FieldState.Enabled); user_example.Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + example_text + "</span>"; }); }; Controller.ChangePasswordFieldEvent += delegate(string text, string example_text, FieldState state) { Application.Invoke(delegate { password_entry.Text = text; password_entry.Sensitive = (state == FieldState.Enabled); password_example.Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + example_text + "</span>"; }); }; Controller.CheckAddPage(address_entry.Text); address_entry.Changed += delegate { Controller.CheckAddPage(address_entry.Text); }; // Address layout_address.PackStart(new Label() { Markup = "<b>" + "Address:" + "</b>", Xalign = 0 }, true, true, 0); layout_address.PackStart(address_entry, false, false, 0); layout_address.PackStart(address_example, false, false, 0); // User layout_user.PackStart(new Label() { Markup = "<b>" + "User:"******"</b>", Xalign = 0 }, true, true, 0); layout_user.PackStart(user_entry, false, false, 0); layout_user.PackStart(user_example, false, false, 0); // Password layout_password.PackStart(new Label() { Markup = "<b>" + "password:"******"</b>", Xalign = 0 }, true, true, 0); layout_password.PackStart(password_entry, false, false, 0); layout_password.PackStart(password_example, false, false, 0); layout_fields.PackStart(layout_address); layout_fields.PackStart(layout_user); layout_fields.PackStart(layout_password); layout_vertical.PackStart(new Label(""), false, false, 0); layout_vertical.PackStart(layout_fields, false, false, 0); Add(layout_vertical); // Cancel button Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.PageCancelled(); }; // Continue button Button continue_button = new Button("Continue") { Sensitive = false }; continue_button.Clicked += delegate { // Show wait cursor // TODO System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor; SparkleLogger.LogInfo("SparkleSetup", "address:" + address_entry.Text + " user:"******" password:"******"Sorry, CmisSync can not find a CMIS server at this address.\nPlease check again.\nIf you are sure about the address, open it in a browser and post\nthe resulting XML to the CmisSync forum."; // TODO address_error_label.Visibility = Visibility.Visible; } else { SparkleLogger.LogInfo("SparkleSetup", "repositories[0]:" + Controller.repositories[0]); // Continue to folder selection Controller.Add1PageCompleted( address_entry.Text, user_entry.Text, password_entry.Text); } }; Controller.UpdateAddProjectButtonEvent += delegate(bool button_enabled) { Application.Invoke(delegate { continue_button.Sensitive = button_enabled; }); }; AddButton(cancel_button); AddButton(continue_button); Controller.CheckAddPage(address_entry.Text); break; } case PageType.Add2: { Header = "Which remote folder do you want to sync?"; VBox layout_vertical = new VBox(false, 12); HBox layout_fields = new HBox(true, 12); VBox layout_repository = new VBox(true, 0); VBox layout_path = new VBox(true, 0); // Repository Entry repository_entry = new Entry() { Text = Controller.repositories[0], // TODO put all elements in a tree Sensitive = (Controller.SelectedPlugin.Repository == null), ActivatesDefault = true }; Label repository_example = new Label() { Xalign = 0, UseMarkup = true, Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + Controller.SelectedPlugin.RepositoryExample + "</span>" }; // Path Entry path_entry = new Entry() { Text = "/", Sensitive = (Controller.SelectedPlugin.Path == null), ActivatesDefault = true }; Label path_example = new Label() { Xalign = 0, UseMarkup = true, Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + Controller.SelectedPlugin.PathExample + "</span>" }; Controller.ChangeRepositoryFieldEvent += delegate(string text, string example_text, FieldState state) { Application.Invoke(delegate { repository_entry.Text = text; repository_entry.Sensitive = (state == FieldState.Enabled); repository_example.Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + example_text + "</span>"; }); }; Controller.ChangePathFieldEvent += delegate(string text, string example_text, FieldState state) { Application.Invoke(delegate { path_entry.Text = text; path_entry.Sensitive = (state == FieldState.Enabled); path_example.Markup = "<span size=\"small\" fgcolor=\"" + SecondaryTextColor + "\">" + example_text + "</span>"; }); }; //Controller.CheckAddPage (address_entry.Text); // Repository layout_repository.PackStart(new Label() { Markup = "<b>" + "Repository:" + "</b>", Xalign = 0 }, true, true, 0); layout_repository.PackStart(repository_entry, false, false, 0); layout_repository.PackStart(repository_example, false, false, 0); // Path layout_path.PackStart(new Label() { Markup = "<b>" + "Remote Path:" + "</b>", Xalign = 0 }, true, true, 0); layout_path.PackStart(path_entry, false, false, 0); layout_path.PackStart(path_example, false, false, 0); layout_fields.PackStart(layout_repository); layout_fields.PackStart(layout_path); layout_vertical.PackStart(new Label(""), false, false, 0); layout_vertical.PackStart(layout_fields, false, false, 0); Add(layout_vertical); // Cancel button Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.PageCancelled(); }; Button add_button = new Button("Add") { //Sensitive = false }; add_button.Clicked += delegate { Controller.Add2PageCompleted(repository_entry.Text, path_entry.Text); }; Controller.UpdateAddProjectButtonEvent += delegate(bool button_enabled) { Application.Invoke(delegate { add_button.Sensitive = button_enabled; }); }; AddButton(cancel_button); AddButton(add_button); //Controller.CheckAddPage (address_entry.Text); break; } case PageType.Invite: { Header = "You've received an invite!"; Description = "Do you want to add this project to SparkleShare?"; Table table = new Table(2, 3, true) { RowSpacing = 6, ColumnSpacing = 6 }; Label address_label = new Label("Address:") { Xalign = 1 }; Label path_label = new Label("Remote Path:") { Xalign = 1 }; Label address_value = new Label("<b>" + Controller.PendingInvite.Address + "</b>") { UseMarkup = true, Xalign = 0 }; Label path_value = new Label("<b>" + Controller.PendingInvite.RemotePath + "</b>") { UseMarkup = true, Xalign = 0 }; table.Attach(address_label, 0, 1, 0, 1); table.Attach(address_value, 1, 2, 0, 1); table.Attach(path_label, 0, 1, 1, 2); table.Attach(path_value, 1, 2, 1, 2); VBox wrapper = new VBox(false, 9); wrapper.PackStart(table, true, false, 0); Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.PageCancelled(); }; Button add_button = new Button("Add"); add_button.Clicked += delegate { Controller.InvitePageCompleted(); }; AddButton(cancel_button); AddButton(add_button); Add(wrapper); break; } case PageType.Syncing: { Header = String.Format("Adding project ‘{0}’…", Controller.SyncingFolder); Description = "This may either take a short or a long time depending on the project's size."; this.progress_bar.Fraction = Controller.ProgressBarPercentage / 100; Button finish_button = new Button() { Sensitive = false, Label = "Finish" }; Button cancel_button = new Button() { Label = "Cancel" }; cancel_button.Clicked += delegate { Controller.SyncingCancelled(); }; AddButton(cancel_button); AddButton(finish_button); Controller.UpdateProgressBarEvent += delegate(double percentage) { Application.Invoke(delegate { this.progress_bar.Fraction = percentage / 100; }); }; if (this.progress_bar.Parent != null) { (this.progress_bar.Parent as Container).Remove(this.progress_bar); } VBox bar_wrapper = new VBox(false, 0); bar_wrapper.PackStart(this.progress_bar, false, false, 15); Add(bar_wrapper); break; } case PageType.Error: { Header = "Oops! Something went wrong" + "…"; VBox points = new VBox(false, 0); Image list_point_one = new Image(SparkleUIHelpers.GetIcon("go-next", 16)); Image list_point_two = new Image(SparkleUIHelpers.GetIcon("go-next", 16)); Image list_point_three = new Image(SparkleUIHelpers.GetIcon("go-next", 16)); Label label_one = new Label() { Markup = "<b>" + Controller.PreviousUrl + "</b> is the address we've compiled. " + "Does this look alright?", Wrap = true, Xalign = 0 }; Label label_two = new Label() { Text = "Do you have access rights to this remote project?", Wrap = true, Xalign = 0 }; points.PackStart(new Label("Please check the following:") { Xalign = 0 }, false, false, 6); HBox point_one = new HBox(false, 0); point_one.PackStart(list_point_one, false, false, 0); point_one.PackStart(label_one, true, true, 12); points.PackStart(point_one, false, false, 12); HBox point_two = new HBox(false, 0); point_two.PackStart(list_point_two, false, false, 0); point_two.PackStart(label_two, true, true, 12); points.PackStart(point_two, false, false, 12); if (warnings.Length > 0) { string warnings_markup = ""; foreach (string warning in warnings) { warnings_markup += "\n<b>" + warning + "</b>"; } Label label_three = new Label() { Markup = "Here's the raw error message:" + warnings_markup, Wrap = true, Xalign = 0 }; HBox point_three = new HBox(false, 0); point_three.PackStart(list_point_three, false, false, 0); point_three.PackStart(label_three, true, true, 12); points.PackStart(point_three, false, false, 12); } points.PackStart(new Label(""), true, true, 0); Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.PageCancelled(); }; Button try_again_button = new Button("Try Again…") { Sensitive = true }; try_again_button.Clicked += delegate { Controller.ErrorPageCompleted(); }; AddButton(cancel_button); AddButton(try_again_button); Add(points); break; } case PageType.CryptoSetup: { Header = "Set up file encryption"; Description = "This project is supposed to be encrypted, but it doesn't yet have a password set. Please provide one below."; Label password_label = new Label("<b>" + "Password:"******"</b>") { UseMarkup = true, Xalign = 1 }; Entry password_entry = new Entry() { Xalign = 0, Visibility = false, ActivatesDefault = true }; CheckButton show_password_check_button = new CheckButton("Show password") { Active = false, Xalign = 0, }; show_password_check_button.Toggled += delegate { password_entry.Visibility = !password_entry.Visibility; }; password_entry.Changed += delegate { Controller.CheckCryptoSetupPage(password_entry.Text); }; Button continue_button = new Button("Continue") { Sensitive = false }; continue_button.Clicked += delegate { Controller.CryptoSetupPageCompleted(password_entry.Text); }; Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.CryptoPageCancelled(); }; Controller.UpdateCryptoSetupContinueButtonEvent += delegate(bool button_enabled) { Application.Invoke(delegate { continue_button.Sensitive = button_enabled; }); }; Table table = new Table(2, 3, true) { RowSpacing = 6, ColumnSpacing = 6 }; table.Attach(password_label, 0, 1, 0, 1); table.Attach(password_entry, 1, 2, 0, 1); table.Attach(show_password_check_button, 1, 2, 1, 2); VBox wrapper = new VBox(false, 9); wrapper.PackStart(table, true, false, 0); Image warning_image = new Image( SparkleUIHelpers.GetIcon("dialog-information", 24) ); Label warning_label = new Label() { Xalign = 0, Wrap = true, Text = "This password can't be changed later, and your files can't be recovered if it's forgotten." }; HBox warning_layout = new HBox(false, 0); warning_layout.PackStart(warning_image, false, false, 15); warning_layout.PackStart(warning_label, true, true, 0); VBox warning_wrapper = new VBox(false, 0); warning_wrapper.PackStart(warning_layout, false, false, 15); wrapper.PackStart(warning_wrapper, false, false, 0); Add(wrapper); AddButton(cancel_button); AddButton(continue_button); break; } case PageType.CryptoPassword: { Header = "This project contains encrypted files"; Description = "Please enter the password to see their contents."; Label password_label = new Label("<b>" + "Password:"******"</b>") { UseMarkup = true, Xalign = 1 }; Entry password_entry = new Entry() { Xalign = 0, Visibility = false, ActivatesDefault = true }; CheckButton show_password_check_button = new CheckButton("Show password") { Active = false, Xalign = 0 }; show_password_check_button.Toggled += delegate { password_entry.Visibility = !password_entry.Visibility; }; password_entry.Changed += delegate { Controller.CheckCryptoPasswordPage(password_entry.Text); }; Button continue_button = new Button("Continue") { Sensitive = false }; continue_button.Clicked += delegate { Controller.CryptoPasswordPageCompleted(password_entry.Text); }; Button cancel_button = new Button("Cancel"); cancel_button.Clicked += delegate { Controller.CryptoPageCancelled(); }; Controller.UpdateCryptoPasswordContinueButtonEvent += delegate(bool button_enabled) { Application.Invoke(delegate { continue_button.Sensitive = button_enabled; }); }; Table table = new Table(2, 3, true) { RowSpacing = 6, ColumnSpacing = 6 }; table.Attach(password_label, 0, 1, 0, 1); table.Attach(password_entry, 1, 2, 0, 1); table.Attach(show_password_check_button, 1, 2, 1, 2); VBox wrapper = new VBox(false, 9); wrapper.PackStart(table, true, false, 0); Add(wrapper); AddButton(cancel_button); AddButton(continue_button); break; } case PageType.Finished: { UrgencyHint = true; if (!HasToplevelFocus) { string title = "Your documents are ready!"; string subtext = "You can find them in your CmisSync folder."; Program.UI.Bubbles.Controller.ShowBubble(title, subtext, null); } Header = "Your documents are ready!"; Description = "You can find them in your CmisSync folder."; // A button that opens the synced folder Button open_folder_button = new Button(string.Format("Open {0}", System.IO.Path.GetFileName(Controller.PreviousPath))); open_folder_button.Clicked += delegate { Controller.OpenFolderClicked(); }; Button finish_button = new Button("Finish"); finish_button.Clicked += delegate { Controller.FinishPageCompleted(); }; if (warnings.Length > 0) { Image warning_image = new Image( SparkleUIHelpers.GetIcon("dialog-information", 24) ); Label warning_label = new Label(warnings [0]) { Xalign = 0, Wrap = true }; HBox warning_layout = new HBox(false, 0); warning_layout.PackStart(warning_image, false, false, 15); warning_layout.PackStart(warning_label, true, true, 0); VBox warning_wrapper = new VBox(false, 0); warning_wrapper.PackStart(warning_layout, false, false, 0); Add(warning_wrapper); } else { Add(null); } AddButton(open_folder_button); AddButton(finish_button); break; } case PageType.Tutorial: { switch (Controller.TutorialPageNumber) { case 1: { Header = "What's happening next?"; Description = "CmisSync creates a special folder on your computer " + "that will keep track of your folders."; Button skip_tutorial_button = new Button("Skip Tutorial"); skip_tutorial_button.Clicked += delegate { Controller.TutorialSkipped(); }; Button continue_button = new Button("Continue"); continue_button.Clicked += delegate { Controller.TutorialPageCompleted(); }; Image slide = SparkleUIHelpers.GetImage("tutorial-slide-1.png"); Add(slide); AddButton(skip_tutorial_button); AddButton(continue_button); break; } case 2: { Header = "Sharing files with others"; Description = "All files added to the server are automatically synced to your " + "local folder."; Button continue_button = new Button("Continue"); continue_button.Clicked += delegate { Controller.TutorialPageCompleted(); }; Image slide = SparkleUIHelpers.GetImage("tutorial-slide-2.png"); Add(slide); AddButton(continue_button); break; } case 3: { Header = "The status icon is here to help"; Description = "It shows the syncing progress, provides easy access to " + "your folders and let's you view recent changes."; Button continue_button = new Button("Continue"); continue_button.Clicked += delegate { Controller.TutorialPageCompleted(); }; Image slide = SparkleUIHelpers.GetImage("tutorial-slide-3.png"); Add(slide); AddButton(continue_button); break; } case 4: { Header = "Adding repository folders to CmisSync"; Description = " " + " "; Image slide = SparkleUIHelpers.GetImage("tutorial-slide-4.png"); Button finish_button = new Button("Finish"); finish_button.Clicked += delegate { Controller.TutorialPageCompleted(); }; CheckButton check_button = new CheckButton("Add CmisSync to startup items") { Active = true }; check_button.Toggled += delegate { Controller.StartupItemChanged(check_button.Active); }; Add(slide); AddOption(check_button); AddButton(finish_button); break; } } break; } } ShowAll(); }); }; }
public virtual void Initialize() { SparkleLogger.LogInfo("Environment", "SparkleShare version: " + SparkleLib.SparkleBackend.Version + ", Operating system: " + SparkleLib.SparkleBackend.Platform + " (" + Environment.OSVersion + ")"); SparklePlugin.PluginsPath = PluginsPath; InstallProtocolHandler(); try { if (CreateSparkleShareFolder()) { AddToBookmarks(); } } catch (DirectoryNotFoundException) { this.lost_folders_path = true; } if (FirstRun) { Config.SetConfigOption("notifications", bool.TrueString); } else { string keys_path = Path.GetDirectoryName(Config.FullPath); string key_file_path = ""; foreach (string file_path in Directory.GetFiles(keys_path)) { string file_name = Path.GetFileName(file_path); if (file_name.EndsWith(".key")) { key_file_path = Path.Combine(keys_path, file_name); // Replace spaces with underscores in old keys if (file_name.Contains(" ")) { string new_file_name = file_name.Replace(" ", "_"); File.Move(key_file_path, Path.Combine(keys_path, new_file_name)); File.Move(key_file_path + ".pub", Path.Combine(keys_path, new_file_name + ".pub")); key_file_path = Path.Combine(keys_path, new_file_name); } SparkleKeys.ImportPrivateKey(key_file_path); break; } } CurrentUser.PublicKey = File.ReadAllText(key_file_path + ".pub"); SparkleKeys.ListPrivateKeys(); } // Watch the SparkleShare folder this.watcher = new FileSystemWatcher() { Filter = "*", IncludeSubdirectories = false, Path = FoldersPath }; watcher.Created += OnFolderActivity; // FIXME watcher.Deleted += OnFolderActivity; // FIXME watcher.Renamed += OnFolderActivity; watcher.EnableRaisingEvents = true; }
public void FinishFetcher() { this.watcher.EnableRaisingEvents = false; this.fetcher.Complete(); string canonical_name = Path.GetFileName(this.fetcher.RemoteUrl.AbsolutePath); if (canonical_name.EndsWith(".git")) { canonical_name = canonical_name.Replace(".git", ""); } canonical_name = canonical_name.Replace("-crypto", ""); canonical_name = canonical_name.ReplaceUnderscoreWithSpace(); canonical_name = canonical_name.Replace("%20", " "); bool target_folder_exists = Directory.Exists( Path.Combine(Config.FoldersPath, canonical_name)); // Add a numbered suffix to the name if a folder with the same name // already exists. Example: "Folder (2)" int suffix = 1; while (target_folder_exists) { suffix++; target_folder_exists = Directory.Exists( Path.Combine(Config.FoldersPath, canonical_name + " (" + suffix + ")")); } string target_folder_name = canonical_name; if (suffix > 1) { target_folder_name += " (" + suffix + ")"; } string target_folder_path = Path.Combine(Config.FoldersPath, target_folder_name); try { Directory.Move(this.fetcher.TargetFolder, target_folder_path); } catch (Exception e) { SparkleLogger.LogInfo("Controller", "Error moving directory, trying again...", e); try { ClearDirectoryAttributes(this.fetcher.TargetFolder); Directory.Move(this.fetcher.TargetFolder, target_folder_path); } catch (Exception x) { SparkleLogger.LogInfo("Controller", "Error moving directory", x); this.fetcher.Dispose(); this.fetcher = null; this.watcher.EnableRaisingEvents = true; return; } } string backend = SparkleFetcherBase.GetBackend(this.fetcher.RemoteUrl.ToString()); Config.AddFolder(target_folder_name, this.fetcher.Identifier, this.fetcher.RemoteUrl.ToString(), backend); if (this.fetcher.OriginalFetcherInfo.AnnouncementsUrl != null) { Config.SetFolderOptionalAttribute(target_folder_name, "announcements_url", this.fetcher.OriginalFetcherInfo.AnnouncementsUrl); } RepositoriesLoaded = true; FolderFetched(this.fetcher.RemoteUrl.ToString(), this.fetcher.Warnings.ToArray()); AddRepository(target_folder_path); FolderListChanged(); this.fetcher.Dispose(); this.fetcher = null; this.watcher.EnableRaisingEvents = true; }
private void ResolveConflict() { // This is a list of conflict status codes that Git uses, their // meaning, and how SparkleShare should handle them. // // DD unmerged, both deleted -> Do nothing // AU unmerged, added by us -> Use server's, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use server's, save ours as a timestamped copy // DU unmerged, deleted by us -> Use server's // AA unmerged, both added -> Use server's, save ours as a timestamped copy // UU unmerged, both modified -> Use server's, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput(); string [] lines = output.Split("\n".ToCharArray()); bool trigger_conflict_event = false; foreach (string line in lines) { string conflicting_path = line.Substring(3); conflicting_path = EnsureSpecialCharacters(conflicting_path); conflicting_path = conflicting_path.Trim("\"".ToCharArray()); // Remove possible rename indicators string [] separators = { " -> \"", " -> " }; foreach (string separator in separators) { if (conflicting_path.Contains(separator)) { conflicting_path = conflicting_path.Substring( conflicting_path.IndexOf(separator) + separator.Length); } } SparkleLogger.LogInfo("Git", Name + " | Conflict type: " + line); // Ignore conflicts in hidden files and use the local versions if (conflicting_path.EndsWith(".sparkleshare") || conflicting_path.EndsWith(".empty")) { SparkleLogger.LogInfo("Git", Name + " | Ignoring conflict in special file: " + conflicting_path); // Recover local version SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); if (File.Exists(abs_conflicting_path)) { File.SetAttributes(abs_conflicting_path, FileAttributes.Hidden); } continue; } SparkleLogger.LogInfo("Git", Name + " | Resolving: " + conflicting_path); // Both the local and server version have been modified if (line.StartsWith("UU") || line.StartsWith("AA") || line.StartsWith("AU") || line.StartsWith("UA")) { // Recover local version SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); // Append a timestamp to local version. // Windows doesn't allow colons in the file name, so // we use "h" between the hours and minutes instead. string timestamp = DateTime.Now.ToString("MMM d H\\hmm"); string our_path = Path.GetFileNameWithoutExtension(conflicting_path) + " (" + base.local_config.User.Name + ", " + timestamp + ")" + Path.GetExtension(conflicting_path); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); string abs_our_path = Path.Combine(LocalPath, our_path); if (File.Exists(abs_conflicting_path) && !File.Exists(abs_our_path)) { File.Move(abs_conflicting_path, abs_our_path); } // Recover server version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); trigger_conflict_event = true; // The server version has been modified, but the local version was removed } else if (line.StartsWith("DU")) { // The modified local version is already in the checkout, so it just needs to be added. // We need to specifically mention the file, so we can't reuse the Add () method SparkleGit git_add = new SparkleGit(LocalPath, "add \"" + conflicting_path + "\""); git_add.StartAndWaitForExit(); // The local version has been modified, but the server version was removed } else if (line.StartsWith("UD")) { // Recover server version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); // Server and local versions were removed } else if (line.StartsWith("DD")) { SparkleLogger.LogInfo("Git", Name + " | No need to resolve: " + line); // New local files } else if (line.StartsWith("??")) { SparkleLogger.LogInfo("Git", Name + " | Found new file, no need to resolve: " + line); } else { SparkleLogger.LogInfo("Git", Name + " | Don't know what to do with: " + line); } } Add(); SparkleGit git = new SparkleGit(LocalPath, "commit --message \"Conflict resolution by SparkleShare\""); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); if (trigger_conflict_event) { OnConflictResolved(); } }
public override bool Fetch() { if (FetchPriorHistory) { this.git = new SparkleGit(SparkleConfig.DefaultConfig.TmpPath, "clone --progress --no-checkout \"" + RemoteUrl + "\" \"" + TargetFolder + "\""); } else { this.git = new SparkleGit(SparkleConfig.DefaultConfig.TmpPath, "clone --progress --no-checkout --depth=1 \"" + RemoteUrl + "\" \"" + TargetFolder + "\""); } this.git.StartInfo.RedirectStandardError = true; this.git.Start(); double percentage = 1.0; Regex progress_regex = new Regex(@"([0-9]+)%", RegexOptions.Compiled); DateTime last_change = DateTime.Now; TimeSpan change_interval = new TimeSpan(0, 0, 0, 1); while (!this.git.StandardError.EndOfStream) { string line = this.git.StandardError.ReadLine(); Match match = progress_regex.Match(line); double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The cloning progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Receiving objects" stage which we count as the last 80% if (line.Contains("|")) { number = (number / 100 * 80 + 20); // "Receiving objects" stage } else { number = (number / 100 * 20); // "Compressing objects" stage } } else { SparkleLogger.LogInfo("Fetcher", line); line = line.Trim(new char [] { ' ', '@' }); if (line.StartsWith("fatal:", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith("error:", StringComparison.InvariantCultureIgnoreCase)) { base.errors.Add(line); } else if (line.StartsWith("WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!")) { base.errors.Add("warning: Remote host identification has changed!"); } else if (line.StartsWith("WARNING: POSSIBLE DNS SPOOFING DETECTED!")) { base.errors.Add("warning: Possible DNS spoofing detected!"); } } if (number >= percentage) { percentage = number; if (DateTime.Compare(last_change, DateTime.Now.Subtract(change_interval)) < 0) { base.OnProgressChanged(percentage); last_change = DateTime.Now; } } } this.git.WaitForExit(); if (this.git.ExitCode == 0) { while (percentage < 100) { percentage += 25; if (percentage >= 100) { break; } Thread.Sleep(500); base.OnProgressChanged(percentage); } base.OnProgressChanged(100); InstallConfiguration(); InstallExcludeRules(); InstallAttributeRules(); AddWarnings(); return(true); } else { return(false); } }
private List <SparkleChangeSet> GetChangeSetsInternal(string path) { List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); SparkleGit git; if (path == null) { git = new SparkleGit(LocalPath, "log --since=1.month --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); } else { path = path.Replace("\\", "/"); git = new SparkleGit(LocalPath, "log --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges -- \"" + path + "\""); } string output = git.StartAndReadStandardOutput(); if (path == null && string.IsNullOrWhiteSpace(output)) { git = new SparkleGit(LocalPath, "log -n 75 --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); output = git.StartAndReadStandardOutput(); } string [] lines = output.Split("\n".ToCharArray()); List <string> entries = new List <string> (); // Split up commit entries int line_number = 0; bool first_pass = true; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith("commit") && !first_pass) { entries.Add(entry); entry = ""; line_number = 0; } else { first_pass = false; } // Only parse first 250 files to prevent memory issues if (line_number < 250) { entry += line + "\n"; line_number++; } last_entry = entry; } entries.Add(last_entry); // Parse commit entries foreach (string log_entry in entries) { Match match = this.log_regex.Match(log_entry); if (!match.Success) { match = this.merge_regex.Match(log_entry); if (!match.Success) { continue; } } SparkleChangeSet change_set = new SparkleChangeSet(); change_set.Folder = new SparkleFolder(Name); change_set.Revision = match.Groups [1].Value; change_set.User = new SparkleUser(match.Groups [2].Value, match.Groups [3].Value); change_set.RemoteUrl = RemoteUrl; change_set.Timestamp = new DateTime(int.Parse(match.Groups [4].Value), int.Parse(match.Groups [5].Value), int.Parse(match.Groups [6].Value), int.Parse(match.Groups [7].Value), int.Parse(match.Groups [8].Value), int.Parse(match.Groups [9].Value)); string time_zone = match.Groups [10].Value; int our_offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours; int their_offset = int.Parse(time_zone.Substring(0, 3)); change_set.Timestamp = change_set.Timestamp.AddHours(their_offset * -1); change_set.Timestamp = change_set.Timestamp.AddHours(our_offset); string [] entry_lines = log_entry.Split("\n".ToCharArray()); // Parse file list. Lines containing file changes start with ":" foreach (string entry_line in entry_lines) { // Skip lines containing backspace characters if (!entry_line.StartsWith(":") || entry_line.Contains("\\177")) { continue; } string file_path = entry_line.Substring(39); if (file_path.Equals(".sparkleshare")) { continue; } string type_letter = entry_line [37].ToString(); bool change_is_folder = false; if (file_path.EndsWith(".empty")) { file_path = file_path.Substring(0, file_path.Length - ".empty".Length); change_is_folder = true; } try { file_path = EnsureSpecialCharacters(file_path); } catch (Exception e) { SparkleLogger.LogInfo("Local", "Error parsing file name '" + file_path + "'", e); continue; } file_path = file_path.Replace("\\\"", "\""); SparkleChange change = new SparkleChange() { Path = file_path, IsFolder = change_is_folder, Timestamp = change_set.Timestamp, Type = SparkleChangeType.Added }; if (type_letter.Equals("R")) { int tab_pos = entry_line.LastIndexOf("\t"); file_path = entry_line.Substring(42, tab_pos - 42); string to_file_path = entry_line.Substring(tab_pos + 1); try { file_path = EnsureSpecialCharacters(file_path); } catch (Exception e) { SparkleLogger.LogInfo("Local", "Error parsing file name '" + file_path + "'", e); continue; } try { to_file_path = EnsureSpecialCharacters(to_file_path); } catch (Exception e) { SparkleLogger.LogInfo("Local", "Error parsing file name '" + to_file_path + "'", e); continue; } file_path = file_path.Replace("\\\"", "\""); to_file_path = to_file_path.Replace("\\\"", "\""); if (file_path.EndsWith(".empty")) { file_path = file_path.Substring(0, file_path.Length - 6); change_is_folder = true; } if (to_file_path.EndsWith(".empty")) { to_file_path = to_file_path.Substring(0, to_file_path.Length - 6); change_is_folder = true; } change.Path = file_path; change.MovedToPath = to_file_path; change.Type = SparkleChangeType.Moved; } else if (type_letter.Equals("M")) { change.Type = SparkleChangeType.Edited; } else if (type_letter.Equals("D")) { change.Type = SparkleChangeType.Deleted; } change_set.Changes.Add(change); } // Group commits per user, per day if (change_sets.Count > 0 && path == null) { SparkleChangeSet last_change_set = change_sets [change_sets.Count - 1]; if (change_set.Timestamp.Year == last_change_set.Timestamp.Year && change_set.Timestamp.Month == last_change_set.Timestamp.Month && change_set.Timestamp.Day == last_change_set.Timestamp.Day && change_set.User.Name.Equals(last_change_set.User.Name)) { last_change_set.Changes.AddRange(change_set.Changes); if (DateTime.Compare(last_change_set.Timestamp, change_set.Timestamp) < 1) { last_change_set.FirstTimestamp = last_change_set.Timestamp; last_change_set.Timestamp = change_set.Timestamp; last_change_set.Revision = change_set.Revision; } else { last_change_set.FirstTimestamp = change_set.Timestamp; } } else { change_sets.Add(change_set); } } else { // Don't show removals or moves in the revision list of a file if (path != null) { List <SparkleChange> changes_to_skip = new List <SparkleChange> (); foreach (SparkleChange change in change_set.Changes) { if ((change.Type == SparkleChangeType.Deleted || change.Type == SparkleChangeType.Moved) && change.Path.Equals(path)) { changes_to_skip.Add(change); } } foreach (SparkleChange change_to_skip in changes_to_skip) { change_set.Changes.Remove(change_to_skip); } } change_sets.Add(change_set); } } return(change_sets); }
public void StartFetcher(string address, string required_fingerprint, string remote_path, string announcements_url, bool fetch_prior_history) { if (announcements_url != null) { announcements_url = announcements_url.Trim(); } string tmp_path = this.config.TmpPath; if (!Directory.Exists(tmp_path)) { Directory.CreateDirectory(tmp_path); File.SetAttributes(tmp_path, File.GetAttributes(tmp_path) | FileAttributes.Hidden); } string canonical_name = Path.GetFileName(remote_path); string tmp_folder = Path.Combine(tmp_path, canonical_name); string backend = SparkleFetcherBase.GetBackend(address); if (address.StartsWith("ssh+")) { address = "ssh" + address.Substring(address.IndexOf("://")); } try { this.fetcher = (SparkleFetcherBase)Activator.CreateInstance( Type.GetType("SparkleLib." + backend + ".SparkleFetcher, SparkleLib." + backend), address, required_fingerprint, remote_path, tmp_folder, fetch_prior_history ); } catch (Exception e) { SparkleLogger.LogInfo("Controller", "Failed to load '" + backend + "' backend for '" + canonical_name + "' " + e.Message); FolderFetchError(Path.Combine(address, remote_path).Replace(@"\", "/"), new string [] { "Failed to load \"" + backend + "\" backend for \"" + canonical_name + "\"" }); return; } this.fetcher.Finished += delegate(bool repo_is_encrypted, bool repo_is_empty, string [] warnings) { if (repo_is_encrypted && repo_is_empty) { ShowSetupWindowEvent(PageType.CryptoSetup); } else if (repo_is_encrypted) { ShowSetupWindowEvent(PageType.CryptoPassword); } else { FinishFetcher(); } }; this.fetcher.Failed += delegate { FolderFetchError(this.fetcher.RemoteUrl.ToString(), this.fetcher.Errors); StopFetcher(); }; this.fetcher.ProgressChanged += delegate(double percentage) { FolderFetching(percentage); }; this.fetcher.Start(); }
private void AddRepository(string folder_path) { SparkleRepoBase repo = null; string folder_name = Path.GetFileName(folder_path); string backend = Config.GetBackendForFolder(folder_name); try { repo = (SparkleRepoBase)Activator.CreateInstance( Type.GetType("SparkleLib." + backend + ".SparkleRepo, SparkleLib." + backend), new object [] { folder_path, Config }); } catch (Exception e) { SparkleLogger.LogInfo("Controller", "Failed to load backend '" + backend + "' for '" + folder_name + "': ", e); return; } repo.ChangesDetected += delegate { UpdateState(); }; repo.SyncStatusChanged += delegate(SyncStatus status) { if (status == SyncStatus.Idle) { ProgressPercentage = 0.0; ProgressSpeedUp = 0.0; ProgressSpeedDown = 0.0; } UpdateState(); }; repo.ProgressChanged += delegate { ProgressPercentage = 0.0; ProgressSpeedUp = 0.0; ProgressSpeedDown = 0.0; double percentage = 0.0; int repo_count = 0; foreach (SparkleRepoBase rep in Repositories) { if (rep.ProgressPercentage > 0) { percentage += rep.ProgressPercentage; repo_count++; } if (rep.Status == SyncStatus.SyncUp) { ProgressSpeedUp += rep.ProgressSpeed; } if (rep.Status == SyncStatus.SyncDown) { ProgressSpeedDown += rep.ProgressSpeed; } } if (repo_count > 0) { ProgressPercentage = percentage / repo_count; } UpdateState(); }; repo.NewChangeSet += delegate(SparkleChangeSet change_set) { if (AvatarsEnabled) { change_set.User.AvatarFilePath = SparkleAvatars.GetAvatar(change_set.User.Email, 48, Config.FullPath); } NotificationRaised(change_set); }; repo.ConflictResolved += delegate { AlertNotificationRaised("Conflict happened", "Don't worry, we've made a copy of each conflicting file."); }; this.repositories.Add(repo); this.repositories.Sort((x, y) => string.Compare(x.Name, y.Name)); repo.Initialize(); }
public static string GetAvatar(string email, int size, string target_path) { #if __MonoCS__ ServicePointManager.ServerCertificateValidationCallback = GetAvatarValidationCallBack; #endif email = email.ToLower(); if (skipped_avatars.Contains(email)) { return(null); } string avatars_path = new string [] { Path.GetDirectoryName(target_path), "avatars", size + "x" + size }.Combine(); // Search avatars by file name, ignore extension // Delete files over a day old // Return first matching file if (Directory.Exists(avatars_path)) { foreach (string file_path in Directory.GetFiles(avatars_path, email.MD5() + "*")) { if (new FileInfo(file_path).LastWriteTime < DateTime.Now.AddDays(-1)) { File.Delete(file_path); } else { return(file_path); } } } string avatar_file_path; try { avatar_file_path = Path.Combine(avatars_path, email.MD5()); } catch (InvalidOperationException e) { SparkleLogger.LogInfo("Avatars", "Error fetching avatar for " + email, e); return(null); } WebClient client = new WebClient(); string url = "https://gravatar.com/avatar/" + email.MD5() + ".png?s=" + size + "&d=404"; try { byte [] buffer = client.DownloadData(url); if (client.ResponseHeaders ["content-type"].Equals(MediaTypeNames.Image.Jpeg, StringComparison.InvariantCultureIgnoreCase)) { avatar_file_path += ".jpg"; } else if (client.ResponseHeaders ["content-type"].Equals(MediaTypeNames.Image.Gif, StringComparison.InvariantCultureIgnoreCase)) { avatar_file_path += ".gif"; } else { avatar_file_path += ".png"; } if (buffer.Length > 255) { if (!Directory.Exists(avatars_path)) { Directory.CreateDirectory(avatars_path); SparkleLogger.LogInfo("Avatars", "Created '" + avatars_path + "'"); } File.WriteAllBytes(avatar_file_path, buffer); SparkleLogger.LogInfo("Avatars", "Fetched " + size + "x" + size + " avatar for " + email); return(avatar_file_path); } else { return(null); } } catch (Exception e) { SparkleLogger.LogInfo("Avatars", "Error fetching avatar for " + email, e); skipped_avatars.Add(email); return(null); } }