aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs146
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
3namespace 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}