// 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. namespace WixToolset.Core.Burn.Bundles { using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Xml; using WixToolset.Core.Cab; /// /// Burn PE reader for the WiX toolset. /// /// This class encapsulates reading from a stub EXE with containers attached /// for dissecting bundled/chained setup packages. /// /// using (BurnReader reader = BurnReader.Open(fileExe, this.core, guid)) /// { /// reader.ExtractUXContainer(file1, tempFolder); /// } /// internal class BurnReader : BurnCommon { private bool disposed; private bool invalidBundle; private BinaryReader binaryReader; private List attachedContainerPayloadNames; /// /// Creates a BurnReader for reading a PE file. /// /// File to read. private BurnReader(string fileExe) : base(fileExe) { this.attachedContainerPayloadNames = new List(); } /// /// Gets the underlying stream. /// public Stream Stream { get { return (null != this.binaryReader) ? this.binaryReader.BaseStream : null; } } internal static BurnReader Open(object inputFilePath) { throw new NotImplementedException(); } /// /// Opens a Burn reader. /// /// Path to file. /// Burn reader. public static BurnReader Open(string fileExe) { BurnReader reader = new BurnReader(fileExe); reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); if (!reader.Initialize(reader.binaryReader)) { reader.invalidBundle = true; } return reader; } /// /// Gets the UX container from the exe and extracts its contents to the output directory. /// /// Directory to write extracted files to. /// True if successful, false otherwise public bool ExtractUXContainer(string outputDirectory, string tempDirectory) { // No UX container to extract if (this.UXAddress == 0 || this.UXSize == 0) { return false; } if (this.invalidBundle) { return false; } Directory.CreateDirectory(outputDirectory); string tempCabPath = Path.Combine(tempDirectory, "ux.cab"); string manifestOriginalPath = Path.Combine(outputDirectory, "0"); string manifestPath = Path.Combine(outputDirectory, "manifest.xml"); this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) { BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); } using (var extract = new WixExtractCab()) { extract.Extract(tempCabPath, outputDirectory); } Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); File.Delete(manifestPath); File.Move(manifestOriginalPath, manifestPath); XmlDocument document = new XmlDocument(); document.Load(manifestPath); XmlNamespaceManager namespaceManager = new XmlNamespaceManager(document.NameTable); namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); XmlNodeList uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager); XmlNodeList payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager); foreach (XmlNode uxPayload in uxPayloads) { XmlNode sourcePathNode = uxPayload.Attributes.GetNamedItem("SourcePath"); XmlNode filePathNode = uxPayload.Attributes.GetNamedItem("FilePath"); string sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value); string destinationPath = Path.Combine(outputDirectory, filePathNode.Value); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); File.Delete(destinationPath); File.Move(sourcePath, destinationPath); } foreach (XmlNode payload in payloads) { XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath"); XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath"); XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging"); string sourcePath = sourcePathNode.Value; string destinationPath = filePathNode.Value; string packaging = packagingNode.Value; if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase)) { this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath)); } } return true; } internal void ExtractUXContainer(string uxExtractPath, object intermediateFolder) { throw new NotImplementedException(); } /// /// Gets the attached container from the exe and extracts its contents to the output directory. /// /// Directory to write extracted files to. /// True if successful, false otherwise public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory) { // No attached container to extract if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0) { return false; } if (this.invalidBundle) { return false; } Directory.CreateDirectory(outputDirectory); string tempCabPath = Path.Combine(tempDirectory, "attached.cab"); this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin); using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) { BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize); } using (WixExtractCab extract = new WixExtractCab()) { extract.Extract(tempCabPath, outputDirectory); } foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) { string sourcePath = Path.Combine(outputDirectory, (string)entry.Key); string destinationPath = Path.Combine(outputDirectory, (string)entry.Value); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); File.Delete(destinationPath); File.Move(sourcePath, destinationPath); } return true; } /// /// Dispose object. /// /// True when releasing managed objects. protected override void Dispose(bool disposing) { if (!this.disposed) { if (disposing && this.binaryReader != null) { this.binaryReader.Close(); this.binaryReader = null; } this.disposed = true; } } } }