| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | namespace WixToolset.Core.Bind |
| 4 | { |
| 5 | using System; |
| 6 | using System.Collections.Generic; |
| 7 | using System.IO; |
| 8 | using System.Linq; |
| 9 | using System.Security.Cryptography; |
| 10 | using System.Text; |
| 11 | |
| 12 | /// <summary> |
| 13 | /// Internal helper class used to extract embedded files. |
| 14 | /// </summary> |
| 15 | internal class ExtractEmbeddedFiles |
| 16 | { |
| 17 | private readonly Dictionary<Uri, SortedList<string, string>> filesWithEmbeddedFiles = new Dictionary<Uri, SortedList<string, string>>(); |
| 18 | |
| 19 | public IEnumerable<Uri> Uris => this.filesWithEmbeddedFiles.Keys; |
| 20 | |
| 21 | /// <summary> |
| 22 | /// Adds an embedded file index to track and returns the path where the embedded file will be extracted. Duplicates will return the same extract path. |
| 23 | /// </summary> |
| 24 | /// <param name="uri">Uri to file containing the embedded files.</param> |
| 25 | /// <param name="embeddedFileId">Id of the embedded file to extract.</param> |
| 26 | /// <param name="extractFolder">Folder where extracted files should be placed.</param> |
| 27 | /// <returns>The extract path for the embedded file.</returns> |
| 28 | public string AddEmbeddedFileToExtract(Uri uri, string embeddedFileId, string extractFolder) |
| 29 | { |
| 30 | // If the uri to the file that contains the embedded file does not already have embedded files |
| 31 | // being extracted, create the dictionary to track that. |
| 32 | if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) |
| 33 | { |
| 34 | extracts = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase); |
| 35 | this.filesWithEmbeddedFiles.Add(uri, extracts); |
| 36 | } |
| 37 | |
| 38 | // If the embedded file is not already tracked in the dictionary of extracts, add it. |
| 39 | if (!extracts.TryGetValue(embeddedFileId, out var extractPath)) |
| 40 | { |
| 41 | var localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(uri.LocalPath); |
| 42 | var unique = this.HashUri(uri.AbsoluteUri); |
| 43 | var extractedName = String.Concat(localFileNameWithoutExtension, "_", unique); |
| 44 | |
| 45 | extractPath = Path.GetFullPath(Path.Combine(extractFolder, extractedName, embeddedFileId)); |
| 46 | extracts.Add(embeddedFileId, extractPath); |
| 47 | } |
| 48 | |
| 49 | return extractPath; |
| 50 | } |
| 51 | |
| 52 | public IReadOnlyList<ExpectedExtractFile> GetExpectedEmbeddedFiles() |
| 53 | { |
| 54 | var files = new List<ExpectedExtractFile>(); |
| 55 | |
| 56 | foreach (var uriWithExtracts in this.filesWithEmbeddedFiles) |
| 57 | { |
| 58 | foreach (var extracts in uriWithExtracts.Value) |
| 59 | { |
| 60 | files.Add(new ExpectedExtractFile |
| 61 | { |
| 62 | Uri = uriWithExtracts.Key, |
| 63 | EmbeddedFileId = extracts.Key, |
| 64 | OutputPath = extracts.Value, |
| 65 | }); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | return files; |
| 70 | } |
| 71 | |
| 72 | public IEnumerable<ExpectedExtractFile> GetExtractFilesForUri(Uri uri) |
| 73 | { |
| 74 | if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) |
| 75 | { |
| 76 | extracts = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase); |
| 77 | } |
| 78 | |
| 79 | return extracts.Select(e => new ExpectedExtractFile { Uri = uri, EmbeddedFileId = e.Key, OutputPath = e.Value }); |
| 80 | } |
| 81 | |
| 82 | private string HashUri(string uri) |
| 83 | { |
| 84 | using (SHA1 sha1 = new SHA1CryptoServiceProvider()) |
| 85 | { |
| 86 | var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(uri)); |
| 87 | return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_'); |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | } |