diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs new file mode 100644 index 00000000..229e75b4 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs | |||
@@ -0,0 +1,146 @@ | |||
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.WindowsInstaller.Unbind | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Collections.Specialized; | ||
8 | using System.Globalization; | ||
9 | using System.IO; | ||
10 | using WixToolset.Core.Cab; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Rows; | ||
13 | using WixToolset.Msi; | ||
14 | |||
15 | internal class ExtractCabinetsCommand | ||
16 | { | ||
17 | public ExtractCabinetsCommand(Output output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder) | ||
18 | { | ||
19 | this.Output = output; | ||
20 | this.Database = database; | ||
21 | this.InputFilePath = inputFilePath; | ||
22 | this.ExportBasePath = exportBasePath; | ||
23 | this.IntermediateFolder = intermediateFolder; | ||
24 | } | ||
25 | |||
26 | private Output Output { get; } | ||
27 | |||
28 | private Database Database { get; } | ||
29 | |||
30 | private string InputFilePath { get; } | ||
31 | |||
32 | private string ExportBasePath { get; } | ||
33 | |||
34 | private string IntermediateFolder { get; } | ||
35 | |||
36 | public void Execute() | ||
37 | { | ||
38 | string databaseBasePath = Path.GetDirectoryName(this.InputFilePath); | ||
39 | StringCollection cabinetFiles = new StringCollection(); | ||
40 | SortedList embeddedCabinets = new SortedList(); | ||
41 | |||
42 | // index all of the cabinet files | ||
43 | if (OutputType.Module == this.Output.Type) | ||
44 | { | ||
45 | embeddedCabinets.Add(0, "MergeModule.CABinet"); | ||
46 | } | ||
47 | else if (null != this.Output.Tables["Media"]) | ||
48 | { | ||
49 | foreach (MediaRow mediaRow in this.Output.Tables["Media"].Rows) | ||
50 | { | ||
51 | if (null != mediaRow.Cabinet) | ||
52 | { | ||
53 | if (OutputType.Product == this.Output.Type || | ||
54 | (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation)) | ||
55 | { | ||
56 | if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) | ||
57 | { | ||
58 | embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1)); | ||
59 | } | ||
60 | else | ||
61 | { | ||
62 | cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet)); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // extract the embedded cabinet files from the database | ||
70 | if (0 < embeddedCabinets.Count) | ||
71 | { | ||
72 | using (View streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?")) | ||
73 | { | ||
74 | foreach (int diskId in embeddedCabinets.Keys) | ||
75 | { | ||
76 | using (Record record = new Record(1)) | ||
77 | { | ||
78 | record.SetString(1, (string)embeddedCabinets[diskId]); | ||
79 | streamsView.Execute(record); | ||
80 | } | ||
81 | |||
82 | using (Record record = streamsView.Fetch()) | ||
83 | { | ||
84 | if (null != record) | ||
85 | { | ||
86 | // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not case-sensitive, | ||
87 | // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work | ||
88 | string cabinetFile = Path.Combine(this.IntermediateFolder, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab")); | ||
89 | |||
90 | // ensure the parent directory exists | ||
91 | System.IO.Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile)); | ||
92 | |||
93 | using (FileStream fs = System.IO.File.Create(cabinetFile)) | ||
94 | { | ||
95 | int bytesRead; | ||
96 | byte[] buffer = new byte[512]; | ||
97 | |||
98 | while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length))) | ||
99 | { | ||
100 | fs.Write(buffer, 0, bytesRead); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | cabinetFiles.Add(cabinetFile); | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | // TODO: warning about missing embedded cabinet | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // extract the cabinet files | ||
116 | if (0 < cabinetFiles.Count) | ||
117 | { | ||
118 | string fileDirectory = Path.Combine(this.ExportBasePath, "File"); | ||
119 | |||
120 | // delete the directory and its files to prevent cab extraction due to an existing file | ||
121 | if (Directory.Exists(fileDirectory)) | ||
122 | { | ||
123 | Directory.Delete(fileDirectory, true); | ||
124 | } | ||
125 | |||
126 | // ensure the directory exists or extraction will fail | ||
127 | Directory.CreateDirectory(fileDirectory); | ||
128 | |||
129 | foreach (string cabinetFile in cabinetFiles) | ||
130 | { | ||
131 | using (var extractCab = new WixExtractCab()) | ||
132 | { | ||
133 | try | ||
134 | { | ||
135 | extractCab.Extract(cabinetFile, fileDirectory); | ||
136 | } | ||
137 | catch (FileNotFoundException) | ||
138 | { | ||
139 | throw new WixException(WixErrors.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetFile)); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | } | ||