/SparkleLib/SparkleFetcherBase.cs
C# | 272 lines | 188 code | 66 blank | 18 comment | 18 complexity | a609b76642d90d11d7c1d769b3a2fe27 MD5 | raw file
1// SparkleShare, a collaboration and sharing tool. 2// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com> 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU Lesser General Public License as 6// published by the Free Software Foundation, either version 3 of the 7// License, or (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU General Public License for more details. 13// 14// You should have received a copy of the GNU General Public License 15// along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 18using System; 19using System.Collections.Generic; 20using System.Diagnostics; 21using System.IO; 22using System.Security.Cryptography; 23using System.Threading; 24 25namespace SparkleLib { 26 27 public class SparkleFetcherInfo { 28 public string Address; 29 public string RemotePath; 30 public string Backend; 31 public string Fingerprint; 32 public string TargetDirectory; 33 public string AnnouncementsUrl; 34 public bool FetchPriorHistory; 35 } 36 37 38 public abstract class SparkleFetcherBase { 39 40 public event Action Started = delegate { }; 41 public event Action Failed = delegate { }; 42 43 public event FinishedEventHandler Finished = delegate { }; 44 public delegate void FinishedEventHandler (bool repo_is_encrypted, bool repo_is_empty, string [] warnings); 45 46 public event ProgressChangedEventHandler ProgressChanged = delegate { }; 47 public delegate void ProgressChangedEventHandler (double percentage, double speed); 48 49 50 public abstract bool Fetch (); 51 public abstract void Stop (); 52 public abstract bool IsFetchedRepoEmpty { get; } 53 public abstract bool IsFetchedRepoPasswordCorrect (string password); 54 public abstract void EnableFetchedRepoCrypto (string password); 55 56 public double ProgressPercentage { get; private set; } 57 public double ProgressSpeed { get; private set; } 58 59 public Uri RemoteUrl { get; protected set; } 60 public string RequiredFingerprint { get; protected set; } 61 public readonly bool FetchPriorHistory = false; 62 public string TargetFolder { get; protected set; } 63 public bool IsActive { get; protected set; } 64 public string Identifier; 65 public SparkleFetcherInfo OriginalFetcherInfo; 66 67 public string [] Warnings { 68 get { 69 return this.warnings.ToArray (); 70 } 71 } 72 73 public string [] Errors { 74 get { 75 return this.errors.ToArray (); 76 } 77 } 78 79 80 protected List<string> warnings = new List<string> (); 81 protected List<string> errors = new List<string> (); 82 83 protected string [] ExcludeRules = new string [] { 84 "*.autosave", // Various autosaving apps 85 "*~", // gedit and emacs 86 ".~lock.*", // LibreOffice 87 "*.part", "*.crdownload", // Firefox and Chromium temporary download files 88 ".*.sw[a-z]", "*.un~", "*.swp", "*.swo", // vi(m) 89 ".directory", // KDE 90 "*.kate-swp", // Kate 91 ".DS_Store", "Icon\r", "._*", ".Spotlight-V100", ".Trashes", // Mac OS X 92 "*(Autosaved).graffle", // Omnigraffle 93 "Thumbs.db", "Desktop.ini", // Windows 94 "~*.tmp", "~*.TMP", "*~*.tmp", "*~*.TMP", // MS Office 95 "~*.ppt", "~*.PPT", "~*.pptx", "~*.PPTX", 96 "~*.xls", "~*.XLS", "~*.xlsx", "~*.XLSX", 97 "~*.doc", "~*.DOC", "~*.docx", "~*.DOCX", 98 "~$*", 99 "*.a$v", // QuarkXPress 100 "*/CVS/*", ".cvsignore", "*/.cvsignore", // CVS 101 "/.svn/*", "*/.svn/*", // Subversion 102 "/.hg/*", "*/.hg/*", "*/.hgignore", // Mercurial 103 "/.bzr/*", "*/.bzr/*", "*/.bzrignore", // Bazaar 104 "*<*", "*>*", "*:*", "*\"*", "*|*", "*\\?*", "*\\**", "*\\\\*" // Not allowed on Windows systems, 105 // see (http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx) 106 }; 107 108 109 private Thread thread; 110 111 112 public SparkleFetcherBase (SparkleFetcherInfo info) 113 { 114 OriginalFetcherInfo = info; 115 RequiredFingerprint = info.Fingerprint; 116 FetchPriorHistory = info.FetchPriorHistory; 117 string remote_path = info.RemotePath.Trim ("/".ToCharArray ()); 118 string address = info.Address; 119 120 if (address.EndsWith ("/")) 121 address = address.Substring (0, address.Length - 1); 122 123 if (!remote_path.StartsWith ("/")) 124 remote_path = "/" + remote_path; 125 126 if (!address.Contains ("://")) 127 address = "ssh://" + address; 128 129 TargetFolder = info.TargetDirectory; 130 131 RemoteUrl = new Uri (address + remote_path); 132 IsActive = false; 133 } 134 135 136 public void Start () 137 { 138 IsActive = true; 139 Started (); 140 141 SparkleLogger.LogInfo ("Fetcher", TargetFolder + " | Fetching folder: " + RemoteUrl); 142 143 try { 144 if (Directory.Exists (TargetFolder)) 145 Directory.Delete (TargetFolder, true); 146 147 } catch (IOException) { 148 this.errors.Add ("\"" + TargetFolder + "\" is read-only."); 149 Failed (); 150 return; 151 } 152 153 this.thread = new Thread (() => { 154 if (Fetch ()) { 155 Thread.Sleep (500); 156 SparkleLogger.LogInfo ("Fetcher", "Finished"); 157 158 IsActive = false; 159 160 bool repo_is_encrypted = (RemoteUrl.AbsolutePath.Contains ("-crypto") || 161 RemoteUrl.Host.Equals ("sparkleshare.net")); 162 163 Finished (repo_is_encrypted, IsFetchedRepoEmpty, Warnings); 164 165 } else { 166 Thread.Sleep (500); 167 168 if (IsActive) { 169 SparkleLogger.LogInfo ("Fetcher", "Failed"); 170 Failed (); 171 172 } else { 173 SparkleLogger.LogInfo ("Fetcher", "Failed: cancelled by user"); 174 } 175 176 IsActive = false; 177 } 178 }); 179 180 this.thread.Start (); 181 } 182 183 184 public virtual void Complete () 185 { 186 string identifier_path = Path.Combine (TargetFolder, ".sparkleshare"); 187 188 if (File.Exists (identifier_path)) { 189 Identifier = File.ReadAllText (identifier_path).Trim (); 190 191 } else { 192 Identifier = CreateIdentifier (); 193 File.WriteAllText (identifier_path, Identifier); 194 195 if (IsFetchedRepoEmpty) 196 CreateInitialChangeSet (); 197 } 198 199 File.SetAttributes (identifier_path, FileAttributes.Hidden); 200 } 201 202 203 // Create an initial change set when the 204 // user has fetched an empty remote folder 205 private void CreateInitialChangeSet () 206 { 207 string file_path = Path.Combine (TargetFolder, "SparkleShare.txt"); 208 string n = Environment.NewLine; 209 210 UriBuilder uri_builder = new UriBuilder (RemoteUrl); 211 212 if (RemoteUrl.Scheme.Contains ("http")) { 213 uri_builder.UserName = ""; 214 uri_builder.Password = ""; 215 } 216 217 string text = "Congratulations, you've successfully created a SparkleShare repository!" + n + 218 n + 219 "Any files you add or change in this folder will be automatically synced to " + n + 220 uri_builder.ToString () + " and everyone connected to it." + n + 221 n + 222 "SparkleShare is an Open Source software program that helps people collaborate and " + n + 223 "share files. If you like what we do, consider buying us a beer: http://www.sparkleshare.org/" + n + 224 n + 225 "Have fun! :)" + n; 226 227 if (RemoteUrl.AbsolutePath.Contains ("-crypto") || RemoteUrl.Host.Equals ("sparkleshare.net")) 228 text = text.Replace ("a SparkleShare repository", "an encrypted SparkleShare repository"); 229 230 File.WriteAllText (file_path, text); 231 } 232 233 234 public static string CreateIdentifier () 235 { 236 return Path.GetRandomFileName ().SHA1 (); 237 } 238 239 240 public void Dispose () 241 { 242 if (this.thread != null) 243 this.thread.Abort (); 244 } 245 246 247 protected void OnProgressChanged (double percentage, double speed) { 248 ProgressChanged (percentage, speed); 249 } 250 251 252 protected string GenerateCryptoSalt () 253 { 254 string salt = Path.GetRandomFileName ().SHA1 (); 255 return salt.Substring (0, 16); 256 } 257 258 259 public static string GetBackend (string address) 260 { 261 if (address.StartsWith ("ssh+")) { 262 string backend = address.Substring (0, address.IndexOf ("://")); 263 backend = backend.Substring (4); 264 265 return char.ToUpper (backend [0]) + backend.Substring (1); 266 267 } else { 268 return "Git"; 269 } 270 } 271 } 272}