aboutsummaryrefslogtreecommitdiff
path: root/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs')
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
new file mode 100644
index 00000000..5b06b31e
--- /dev/null
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -0,0 +1,212 @@
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.Burn.Bundles
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.IO;
9 using System.Xml;
10 using WixToolset.Core.Native;
11 using WixToolset.Extensibility.Services;
12
13 /// <summary>
14 /// Burn PE reader for the WiX toolset.
15 /// </summary>
16 /// <remarks>This class encapsulates reading from a stub EXE with containers attached
17 /// for dissecting bundled/chained setup packages.</remarks>
18 /// <example>
19 /// using (BurnReader reader = BurnReader.Open(fileExe, this.core, guid))
20 /// {
21 /// reader.ExtractUXContainer(file1, tempFolder);
22 /// }
23 /// </example>
24 internal class BurnReader : BurnCommon
25 {
26 private bool disposed;
27
28 private bool invalidBundle;
29 private BinaryReader binaryReader;
30 private readonly List<DictionaryEntry> attachedContainerPayloadNames;
31
32 /// <summary>
33 /// Creates a BurnReader for reading a PE file.
34 /// </summary>
35 /// <param name="messaging"></param>
36 /// <param name="fileExe">File to read.</param>
37 private BurnReader(IMessaging messaging, string fileExe)
38 : base(messaging, fileExe)
39 {
40 this.attachedContainerPayloadNames = new List<DictionaryEntry>();
41 }
42
43 /// <summary>
44 /// Gets the underlying stream.
45 /// </summary>
46 public Stream Stream => this.binaryReader?.BaseStream;
47
48 internal static BurnReader Open(object inputFilePath)
49 {
50 throw new NotImplementedException();
51 }
52
53 /// <summary>
54 /// Opens a Burn reader.
55 /// </summary>
56 /// <param name="messaging"></param>
57 /// <param name="fileExe">Path to file.</param>
58 /// <returns>Burn reader.</returns>
59 public static BurnReader Open(IMessaging messaging, string fileExe)
60 {
61 var reader = new BurnReader(messaging, fileExe);
62
63 reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
64 if (!reader.Initialize(reader.binaryReader))
65 {
66 reader.invalidBundle = true;
67 }
68
69 return reader;
70 }
71
72 /// <summary>
73 /// Gets the UX container from the exe and extracts its contents to the output directory.
74 /// </summary>
75 /// <param name="outputDirectory">Directory to write extracted files to.</param>
76 /// <param name="tempDirectory">Scratch directory.</param>
77 /// <returns>True if successful, false otherwise</returns>
78 public bool ExtractUXContainer(string outputDirectory, string tempDirectory)
79 {
80 // No UX container to extract
81 if (this.UXAddress == 0 || this.UXSize == 0)
82 {
83 return false;
84 }
85
86 if (this.invalidBundle)
87 {
88 return false;
89 }
90
91 Directory.CreateDirectory(outputDirectory);
92 string tempCabPath = Path.Combine(tempDirectory, "ux.cab");
93 string manifestOriginalPath = Path.Combine(outputDirectory, "0");
94 string manifestPath = Path.Combine(outputDirectory, "manifest.xml");
95
96 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin);
97 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
98 {
99 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize);
100 }
101
102 var cabinet = new Cabinet(tempCabPath);
103 cabinet.Extract(outputDirectory);
104
105 Directory.CreateDirectory(Path.GetDirectoryName(manifestPath));
106 FileSystem.MoveFile(manifestOriginalPath, manifestPath);
107
108 XmlDocument document = new XmlDocument();
109 document.Load(manifestPath);
110 XmlNamespaceManager namespaceManager = new XmlNamespaceManager(document.NameTable);
111 namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace);
112 XmlNodeList uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager);
113 XmlNodeList payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager);
114
115 foreach (XmlNode uxPayload in uxPayloads)
116 {
117 XmlNode sourcePathNode = uxPayload.Attributes.GetNamedItem("SourcePath");
118 XmlNode filePathNode = uxPayload.Attributes.GetNamedItem("FilePath");
119
120 string sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value);
121 string destinationPath = Path.Combine(outputDirectory, filePathNode.Value);
122
123 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
124 FileSystem.MoveFile(sourcePath, destinationPath);
125 }
126
127 foreach (XmlNode payload in payloads)
128 {
129 XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath");
130 XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath");
131 XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging");
132
133 string sourcePath = sourcePathNode.Value;
134 string destinationPath = filePathNode.Value;
135 string packaging = packagingNode.Value;
136
137 if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase))
138 {
139 this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath));
140 }
141 }
142
143 return true;
144 }
145
146 internal void ExtractUXContainer(string uxExtractPath, object intermediateFolder)
147 {
148 throw new NotImplementedException();
149 }
150
151 /// <summary>
152 /// Gets the attached container from the exe and extracts its contents to the output directory.
153 /// </summary>
154 /// <param name="outputDirectory">Directory to write extracted files to.</param>
155 /// <param name="tempDirectory">Scratch directory.</param>
156 /// <returns>True if successful, false otherwise</returns>
157 public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory)
158 {
159 // No attached container to extract
160 if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0)
161 {
162 return false;
163 }
164
165 if (this.invalidBundle)
166 {
167 return false;
168 }
169
170 Directory.CreateDirectory(outputDirectory);
171 string tempCabPath = Path.Combine(tempDirectory, "attached.cab");
172
173 this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin);
174 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
175 {
176 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize);
177 }
178
179 var cabinet = new Cabinet(tempCabPath);
180 cabinet.Extract(outputDirectory);
181
182 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames)
183 {
184 string sourcePath = Path.Combine(outputDirectory, (string)entry.Key);
185 string destinationPath = Path.Combine(outputDirectory, (string)entry.Value);
186
187 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
188 FileSystem.MoveFile(sourcePath, destinationPath);
189 }
190
191 return true;
192 }
193
194 /// <summary>
195 /// Dispose object.
196 /// </summary>
197 /// <param name="disposing">True when releasing managed objects.</param>
198 protected override void Dispose(bool disposing)
199 {
200 if (!this.disposed)
201 {
202 if (disposing && this.binaryReader != null)
203 {
204 this.binaryReader.Close();
205 this.binaryReader = null;
206 }
207
208 this.disposed = true;
209 }
210 }
211 }
212}