PageRenderTime 26ms CodeModel.GetById 12ms app.highlight 10ms RepoModel.GetById 2ms app.codeStats 0ms

/SparkleLib/SparkleFetcherBase.cs

http://github.com/hbons/SparkleShare
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}