// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using EpicGames.Core; namespace UnrealBuildTool { /// /// Allows interrogating the contents of a .mobileprovision /// class MobileProvisionContents { /// /// The contents of the provision /// XmlDocument Document; /// /// Map of key names to XML elements holding their values /// Dictionary NameToValue = new Dictionary(); /// /// Constructor /// /// XML file to create the mobile provision from. Call Read() to read from a signed file on disk. public MobileProvisionContents(XmlDocument Document) { this.Document = Document; XmlNodeList? Nodes = Document.SelectNodes("/plist/dict/key"); if (Nodes != null) { foreach (XmlElement? KeyElement in Nodes) { if (KeyElement == null) { continue; } XmlNode? ValueNode = KeyElement.NextSibling; while (ValueNode != null) { XmlElement? ValueElement = ValueNode as XmlElement; if (ValueElement != null) { NameToValue[KeyElement.InnerText] = ValueElement; break; } } } } } /// /// Gets the unique id for this mobileprovision /// /// UUID for the provision public string GetUniqueId() { XmlElement? UniqueIdElement; if (!NameToValue.TryGetValue("UUID", out UniqueIdElement)) { throw new BuildException("Missing UUID in MobileProvision"); } return UniqueIdElement.InnerText; } /// /// Gets the bundle id for this mobileprovision /// /// Bundle Identifier for the provision public string GetBundleIdentifier() { XmlElement? UniqueIdElement = null; XmlElement? UniqueIdEntitlement; if (!NameToValue.TryGetValue("Entitlements", out UniqueIdEntitlement) || UniqueIdEntitlement.Name != "dict") { throw new BuildException("Missing Entitlements in MobileProvision"); } XmlNodeList? Nodes = UniqueIdEntitlement.SelectNodes("key"); if (Nodes != null) { foreach (XmlElement? KeyElement in Nodes) { if (KeyElement == null) { continue; } Console.WriteLine("Found entitlement node:" + KeyElement.InnerText); if (!KeyElement.InnerText.Equals("application-identifier")) { continue; } UniqueIdElement = KeyElement.NextSibling as XmlElement; break; } } if (UniqueIdElement == null) { throw new BuildException("Missing Bundle Identifier in MobileProvision"); } return UniqueIdElement.InnerText.Substring(UniqueIdElement.InnerText.IndexOf('.') + 1); } /// /// Gets the team unique id for this mobileprovision /// /// Receives the team unique id /// True if the team unique ID was found, false otherwise public bool TryGetTeamUniqueId(out string? UniqueId) { XmlElement? UniqueIdElement; if (!NameToValue.TryGetValue("TeamIdentifier", out UniqueIdElement) || UniqueIdElement.Name != "array") { UniqueId = null; return false; } XmlElement? ValueElement = UniqueIdElement.SelectSingleNode("string") as XmlElement; if (ValueElement == null) { UniqueId = null; return false; } UniqueId = ValueElement.InnerText; return true; } /// /// Reads a mobileprovision from a file on disk /// /// Path to the file /// New mobile provision instance public static MobileProvisionContents Read(FileReference Location) { XmlDocument Document = ReadXml(Location); return new MobileProvisionContents(Document); } /// /// Reads the plist file inside a mobileprovision /// /// Path to the file /// XML plist extracted from the mobile provision public static XmlDocument ReadXml(FileReference Location) { // Provision data is stored as PKCS7-signed file in ASN.1 BER format using (BinaryReader Reader = new BinaryReader(File.Open(Location.FullName, FileMode.Open, FileAccess.Read))) { long Length = Reader.BaseStream.Length; while (Reader.BaseStream.Position < Length) { Asn.FieldInfo Field = Asn.ReadField(Reader); if (Field.Tag == Asn.FieldTag.OBJECT_IDENTIFIER) { int[] Identifier = Asn.ReadObjectIdentifier(Reader, Field.Length); if (Enumerable.SequenceEqual(Identifier, Asn.ObjectIdentifier.Pkcs7_Data)) { while (Reader.BaseStream.Position < Length) { Asn.FieldInfo NextField = Asn.ReadField(Reader); if (NextField.Tag == Asn.FieldTag.OCTET_STRING) { byte[] Data = Reader.ReadBytes(NextField.Length); XmlDocument Document = new XmlDocument(); Document.XmlResolver = null; Document.Load(new MemoryStream(Data)); return Document; } else { Asn.SkipValue(Reader, NextField); } } } } else { Asn.SkipValue(Reader, Field); } } throw new BuildException("No PKCS7-Data section found in {0}", Location); } } // return the outerXML of the node's value public string GetNodeXMLValueByName(string InValue) { XmlNodeList elemList = Document.GetElementsByTagName("key"); for (int i = 0; i < elemList.Count; i++) { XmlNode? Node = elemList[i]; if (Node != null && Node.InnerXml.Equals(InValue)) { XmlNode? valueNode = Node.NextSibling; if (valueNode != null) { return valueNode.OuterXml; } } } return ""; } // return the innerXML of the node's value public string GetNodeValueByName(string InValue) { XmlNodeList elemList = Document.GetElementsByTagName("key"); for (int i = 0; i < elemList.Count; i++) { XmlNode? Node = elemList[i]; if (Node != null && Node.InnerXml.Equals(InValue)) { XmlNode? valueNode = Node.NextSibling; if (valueNode != null) { if (valueNode.Name.Equals("array")) { XmlNode? firstChildNode = valueNode.FirstChild; if (firstChildNode != null) { return firstChildNode.InnerXml; } } else { return valueNode.InnerXml; } } } } return ""; } } }