/// <summary> /// Main updater method. Fetches the manifest and then processes its contents. /// </summary> public void Update() { FileFetcher fetcher = new FileFetcher(); try { // Begin the update OnStateChanged(UpdateState.UpdateStarted); // Make sure the base directory exists if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); // Grab the manifest OnStateChanged(UpdateState.FetchingManifest); fetcher.FetchFile(NewManifest, sourceUrl, baseDirectory, null); ReadManifest(targetManifestDict, targetIgnorePatterns, targetExcludePatterns, Path.Combine(baseDirectory, NewManifest)); //List<Regex> clientExcludes = new List<Regex>(); //clientExcludes.Add(new Regex("Media/.*")); BuildManifest(prefixDirectory, fullScan, targetExcludePatterns); ProcessManifest(fetcher); } catch (Exception e) { Trace.TraceError("Error updating media: {0}.", e.StackTrace); error = string.Format("Error updating media: {0} Source URL: '{1}', manifest name: '{2}'. ", e.Message, sourceUrl, NewManifest); if (log != null) log.Write(error); UpdaterStatus status = new UpdaterStatus(); status.message = error; OnUpdateAborted(status); } if (log != null) { log.Close(); log = null; } }
/// <summary> /// Examine the local manifest and the remote manifest, and build /// a concept of what files need to be added/removed/modified. /// </summary> public void ProcessManifest(FileFetcher fetcher) { string baseDir = baseDirectory; string currentManifestFile = Path.Combine(baseDir, CurrentManifest); string newManifestFile = Path.Combine(baseDir, NewManifest); ReadManifest(currentManifestDict, currentIgnorePatterns, currentExcludePatterns, currentManifestFile); OnStateChanged(UpdateState.FetchingManifest); fetcher.FetchFile(NewManifest, sourceUrl, baseDir, null); ReadManifest(targetManifestDict, targetIgnorePatterns, targetExcludePatterns, newManifestFile); fetcher.ProgressUpdate += fetcher_ProgressUpdate; bytesNeeded = 0; filesNeeded = 0; List<string> newFiles = new List<string>(); List<string> removedFiles = new List<string>(); List<string> modifiedFiles = new List<string>(); foreach (string key in targetManifestDict.Keys) { ManifestEntry targetEntry = targetManifestDict[key]; if (targetEntry.type == "dir") { if (IsIgnored(targetIgnorePatterns, targetEntry.name)) Debug.Assert(false, "Ignoring directory: " + targetEntry.name); } // if the entry is ignored, just skip it if (IsIgnored(targetIgnorePatterns, targetEntry.name)) continue; if (targetEntry.type == "dir") { MakeDirectory(targetEntry.name); if (!currentManifestDict.ContainsKey(key)) { UpdateFileStatus ufs = new UpdateFileStatus(); ufs.file = targetEntry.name; ufs.length = 0; OnFileAdded(ufs); } continue; } if (!currentManifestDict.ContainsKey(key)) { newFiles.Add(key); bytesNeeded += targetEntry.length; filesNeeded++; } else { ManifestEntry currentEntry = currentManifestDict[key]; Debug.Assert(currentEntry.digest != null); Debug.Assert(targetEntry.digest != null); Debug.Assert(currentEntry.digest.Length == targetEntry.digest.Length); bool matches = true; for (int i = 0; i < currentEntry.digest.Length; ++i) { if (currentEntry.digest[i] != targetEntry.digest[i]) { matches = false; break; } } if (!matches) { modifiedFiles.Add(key); bytesNeeded += targetEntry.length; filesNeeded++; } } } foreach (string key in currentManifestDict.Keys) { if (IsIgnored(targetIgnorePatterns, key)) { UpdateFileStatus ufs = new UpdateFileStatus(); ufs.file = key; OnFileIgnored(ufs); continue; } // TODO: this should be cleaned up to prevent me from // removing entries managed by another repository if (!targetManifestDict.ContainsKey(key)) removedFiles.Add(key); } // At this point, we are about to start fetching new files, // so remove our existing manifest. This will cause us to // do a full scan if our download is interrupted, which will // be more efficient than fetching everything again. File.Delete(currentManifestFile); UpdaterStatus updateStatus = new UpdaterStatus(); updateStatus.files = filesNeeded; updateStatus.filesFetched = 0; updateStatus.bytes = bytesNeeded; updateStatus.bytesFetched = 0; updateStatus.bytesTransferred = 0; OnUpdateStarted(updateStatus); OnStateChanged(UpdateState.UpdatingFiles); // Sort and reverse the removed files so that subdirectory entries are listed // before their parent directories. removedFiles.Sort(); removedFiles.Reverse(); foreach (string file in removedFiles) { UpdateFileStatus ufs = new UpdateFileStatus(); ufs.file = file; string fname = Path.Combine(baseDir, file); if (Directory.Exists(fname)) Directory.Delete(fname); else if (File.Exists(fname)) File.Delete(fname); else Debug.Assert(false, "Unexpected removal of entry: " + fname + string.Format(" - '{0}' + '{1}'", baseDir, file)); OnFileRemoved(ufs); } // Handle modified files foreach (string file in modifiedFiles) { long lastBytesUpdated = bytesUpdated; UpdateFileStatus ufs = new UpdateFileStatus(); ufs.file = file; ufs.length = targetManifestDict[file].length; OnFileFetchStarted(ufs); // Trace.TraceInformation("Fetching modified file: {0}", file); // Trace.TraceInformation("Old Digest: {0}", currentManifestDict[file].DigestString); // Trace.TraceInformation("New Digest: {0}", targetManifestDict[file].DigestString); long length, compressedLength; if (!fetcher.FetchFile(file, sourceUrl, baseDir, targetManifestDict[file].digest, out length, out compressedLength)) throw new Exception(string.Format("Unable to retrieve valid file: '{0}'", file)); if (length != ufs.length) { string msg = string.Format("Unable to retrieve valid file: '{0}' - size mismatch (got {1} but expected {2})", file, length, ufs.length); throw new Exception(msg); } ufs.compressedLength = compressedLength; OnFileFetchEnded(ufs); OnFileModified(ufs); filesUpdated++; bytesUpdated = lastBytesUpdated + ufs.length; } // Handle new files foreach (string file in newFiles) { long lastBytesUpdated = bytesUpdated; UpdateFileStatus ufs = new UpdateFileStatus(); ufs.file = file; ufs.length = targetManifestDict[file].length; OnFileFetchStarted(ufs); // Trace.TraceInformation("Fetching new file: {0}", file); long length, compressedLength; if (!fetcher.FetchFile(file, sourceUrl, baseDir, targetManifestDict[file].digest, out length, out compressedLength)) throw new Exception(string.Format("Unable to retrieve valid file: '{0}'", file)); if (length != ufs.length) { string msg = string.Format("Unable to retrieve valid file: '{0}' - size mismatch (got {1} but expected {2})", file, length, ufs.length); throw new Exception(msg); } ufs.compressedLength = compressedLength; OnFileFetchEnded(ufs); OnFileAdded(ufs); filesUpdated++; bytesUpdated = lastBytesUpdated + ufs.length; } File.Copy(newManifestFile, currentManifestFile); updateStatus.filesFetched = filesUpdated; updateStatus.bytesFetched = fetcher.BytesFetched; updateStatus.bytesTransferred = fetcher.BytesTransferred; OnUpdateCompleted(updateStatus); OnStateChanged(UpdateState.UpdateEnded); }