// 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 "";
}
}
}