/// <summary> /// Export the certificate to a file /// </summary> static public void ExportCertificate() { if (Config.ProvisionFile == null) { Program.Error("Missing -ProvisionFile=... argument"); return; } if (Config.OutputCertificate == null) { Program.Error("Missing -OutputCertificate=... argument"); return; } // export the signing certificate to a file MobileProvision Provision = MobileProvisionParser.ParseFile(Config.ProvisionFile); X509Certificate2 Certificate = CodeSignatureBuilder.FindCertificate(Provision); if (Certificate == null) { Program.Error("Failed to find a valid certificate"); return; } byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A"); File.WriteAllBytes(Config.OutputCertificate, Data); }
public static void TryInstallingMobileProvision(string ProvisionFilename, bool ShowPrompt = true) { if (!String.IsNullOrEmpty(ProvisionFilename) || ShowOpenFileDialog(MobileProvisionFilter, "Choose a mobile provision to install", "mobileprovision", "", ref ChoosingFilesToInstallDirectory, out ProvisionFilename)) { try { // Determine if this is a development or distribution certificate bool bIsDistribution = false; MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionFilename); bIsDistribution = IsProfileForDistribution(Provision); // use the input filename if the GameName is empty string DestName = string.IsNullOrEmpty(Program.GameName) ? Path.GetFileNameWithoutExtension(ProvisionFilename) : Program.GameName; // Copy the file into the destination location string EffectivePrefix = bIsDistribution ? "Distro_" : Config.SigningPrefix; string DestinationFilename = Path.Combine(Config.ProvisionDirectory, EffectivePrefix + DestName + ".mobileprovision"); DestinationFilename = DestinationFilename.Replace("\\", "/"); if (File.Exists(DestinationFilename)) { MobileProvision OldProvision = MobileProvisionParser.ParseFile(DestinationFilename); string MessagePrompt = String.Format( "{0} already contains a {1} mobile provision file. Do you want to replace the provision '{2}' with '{3}'?", Config.BuildDirectory, bIsDistribution ? "distribution" : "development", OldProvision.ProvisionName, Provision.ProvisionName); if (ShowPrompt && System.Windows.Forms.MessageBox.Show(MessagePrompt, Config.AppDisplayName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } if (DestinationFilename != ProvisionFilename) { FileOperations.DeleteFile(DestinationFilename); } } if (DestinationFilename != ProvisionFilename) { FileOperations.CopyRequiredFile(ProvisionFilename, DestinationFilename); } } catch (Exception ex) { ShowError(String.Format("Encountered an error '{0} while trying to install a mobile provision", ex.Message)); } } }
public void LoadMobileProvision(string CFBundleIdentifier) { byte[] MobileProvisionFile = GetMobileProvision(CFBundleIdentifier); if (MobileProvisionFile != null) { Provision = MobileProvisionParser.ParseFile(MobileProvisionFile); Program.Log("Using mobile provision '{0}' to code sign", Provision.ProvisionName); // Re-embed the mobile provision FileSystem.WriteAllBytes("embedded.mobileprovision", MobileProvisionFile); Program.Log(" ... Writing updated embedded.mobileprovision"); } }
void PickIpaAndCheckForUDID(string UDID) { if (UDID != null) { string PickedFilename; if (ToolsHub.ShowOpenFileDialog(ToolsHub.IpaFilter, "Picking an application to check", "ipa", "", ref ToolsHub.ChoosingIpaDirectory, out PickedFilename)) { MobileProvision Provision = MobileProvisionParser.ParseIPA(PickedFilename); CheckProvisionForUDID( Provision, UDID, String.Format("embedded mobile provision in {0}", Path.GetFileName(PickedFilename))); } } }
void PickProvisionAndCheckForUDID(string UDID) { if (UDID != null) { string PickedFilename; if (ToolsHub.ShowOpenFileDialog(ToolsHub.MobileProvisionFilter, "Picking a mobile provision to check", "mobileprovision", "", ref ToolsHub.ChoosingIpaDirectory, out PickedFilename)) { MobileProvision Provision = MobileProvisionParser.ParseFile(PickedFilename); CheckProvisionForUDID( Provision, UDID, String.Format("mobile provision {0}", Path.GetFileNameWithoutExtension(PickedFilename))); } } }
public static void InstallIPAOnConnectedDevices(string IPAPath) { // Read the mobile provision to check for issues FileOperations.ReadOnlyZipFileSystem Zip = new FileOperations.ReadOnlyZipFileSystem(IPAPath); MobileProvision Provision = null; try { MobileProvisionParser.ParseFile(Zip.ReadAllBytes("embedded.mobileprovision")); } catch (System.Exception ex) { Program.Warning(String.Format("Couldn't find an embedded mobile provision ({0})", ex.Message)); Provision = null; } Zip.Close(); if (Provision != null) { var DeviceList = DeploymentHelper.Get().EnumerateConnectedDevices(); foreach (var DeviceInfo in DeviceList) { string UDID = DeviceInfo.UDID; string DeviceName = DeviceInfo.DeviceName; // Check the IPA's mobile provision against the connected device to make sure this device is authorized // We'll still try installing anyways, but this message is more friendly than the failure we get back from MobileDeviceInterface if (UDID != String.Empty) { if (!Provision.ContainsUDID(UDID)) { Program.Warning(String.Format("Embedded provision in IPA does not include the UDID {0} of device '{1}'. The installation is likely to fail.", UDID, DeviceName)); } } else { Program.Warning(String.Format("Unable to query device for UDID, and therefore unable to verify IPA embedded mobile provision contains this device.")); } } } DeploymentHelper.Get().InstallIPAOnDevice(IPAPath); }
/// <summary> /// Export the certificate to a file /// </summary> static public void ExportCertificate() { string ProvisionWithPrefix; if (!FindMobileProvision("", out ProvisionWithPrefix)) { Program.Error("Missing provision"); return; } if (Config.Certificate == null) { Program.Error("Missing -Certificate=... argument"); return; } // export the signing certificate to a file MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionWithPrefix); var Certificate = CodeSignatureBuilder.FindCertificate(Provision); byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A"); File.WriteAllBytes(Config.Certificate, Data); }
/// <summary> /// Copy the files always needed (even in a stub IPA) /// </summary> static public void CopyFilesNeededForMakeApp() { // Copy Info.plist over (modifiying it as needed) string SourcePListFilename = Utilities.GetPrecompileSourcePListFilename(); Utilities.PListHelper Info = Utilities.PListHelper.CreateFromFile(SourcePListFilename); // Edit the plist CookTime.UpdateVersion(Info); // Write out the <GameName>-Info.plist file to the xcode staging directory string TargetPListFilename = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + "-Info.plist"); Directory.CreateDirectory(Path.GetDirectoryName(TargetPListFilename)); string OutString = Info.SaveToString(); OutString = OutString.Replace("${EXECUTABLE_NAME}", Program.GameName); OutString = OutString.Replace("${BUNDLE_IDENTIFIER}", Program.GameName.Replace("_", "")); // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist // eventually we want to generate this based on what the user has set in the project settings string[] IPhoneConfigs = { "Default-IPhone6", "Landscape", "{375, 667}", "Default-IPhone6", "Portrait", "{375, 667}", "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}", "Default-IPhone6Plus-Portrait", "Portrait", "{414, 736}", "Default", "Landscape", "{320, 480}", "Default", "Portrait", "{320, 480}", "Default-568h", "Landscape", "{320, 568}", "Default-568h", "Portrait", "{320, 568}", "Default-IPhoneX-Landscape", "Landscape", "{375, 812}", "Default-IPhoneX-Portrait", "Portrait", "{375, 812}", }; StringBuilder NewLaunchImagesString = new StringBuilder("<key>UILaunchImages~iphone</key>\n\t\t<array>\n"); for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 3) { NewLaunchImagesString.Append("\t\t\t<dict>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageMinimumOSVersion</key>\n"); NewLaunchImagesString.Append("\t\t\t\t<string>8.0</string>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageName</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 0]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageOrientation</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 1]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageSize</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 2]); NewLaunchImagesString.Append("\t\t\t</dict>\n"); } // close it out NewLaunchImagesString.Append("\t\t\t</array>\n\t\t<key>UILaunchImages~ipad</key>"); OutString = OutString.Replace("<key>UILaunchImages~ipad</key>", NewLaunchImagesString.ToString()); byte[] RawInfoPList = Encoding.UTF8.GetBytes(OutString); File.WriteAllBytes(TargetPListFilename, RawInfoPList); Program.Log("Updating .plist: {0} --> {1}", SourcePListFilename, TargetPListFilename); // look for an entitlements file (optional) string SourceEntitlements = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements"); // set where to make the entitlements file ( string TargetEntitlements = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + ".entitlements"); if (File.Exists(SourceEntitlements)) { FileOperations.CopyRequiredFile(SourceEntitlements, TargetEntitlements); } else { // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, // which is based on distribution or not (true means debuggable) File.WriteAllText(TargetEntitlements, string.Format("<plist><dict><key>get-task-allow</key><{0}/></dict></plist>", Config.bForDistribution ? "false" : "true")); } // Copy the mobile provision file over string CFBundleIdentifier = null; Info.GetString("CFBundleIdentifier", out CFBundleIdentifier); bool bNameMatch; string ProvisionWithPrefix = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory + "/NotForLicensees/", Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory, "UE4Game.mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory + "/NotForLicensees/", "UE4Game.mobileprovision"); } } } } string FinalMobileProvisionFilename = Path.Combine(Config.PCXcodeStagingDir, MacMobileProvisionFilename); FileOperations.CopyRequiredFile(ProvisionWithPrefix, FinalMobileProvisionFilename); // make sure this .mobileprovision file is newer than any other .mobileprovision file on the Mac (this file gets multiple games named the same file, // so the time stamp checking can fail when moving between games, a la the buildmachines!) File.SetLastWriteTime(FinalMobileProvisionFilename, DateTime.UtcNow); string ProjectFile = Config.RootRelativePath + @"Engine\Intermediate\ProjectFiles\UE4.xcodeproj\project.pbxproj"; if (Program.GameName != "UE4Game") { ProjectFile = Path.GetDirectoryName(Config.IntermediateDirectory) + @"\ProjectFiles\" + Program.GameName + @".xcodeproj\project.pbxproj"; } FileOperations.CopyRequiredFile(ProjectFile, Path.Combine(Config.PCXcodeStagingDir, @"project.pbxproj.datecheck")); // copy the signing certificate over // export the signing certificate to a file MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionWithPrefix); var Certificate = CodeSignatureBuilder.FindCertificate(Provision); byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A"); File.WriteAllBytes(Path.Combine(Config.PCXcodeStagingDir, MacSigningIdentityFilename), Data); Config.CodeSigningIdentity = Certificate.FriendlyName; // since the pipeline will use a temporary keychain that will contain only this certificate, this should be the only identity that will work CurrentBaseXCodeCommandLine = GetBaseXcodeCommandline(); // get the UUID string AllText = File.ReadAllText(FinalMobileProvisionFilename); string UUID = ""; int idx = AllText.IndexOf("<key>UUID</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; UUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } CurrentBaseXCodeCommandLine += String.Format(" PROVISIONING_PROFILE=" + UUID); // needs Mac line endings so it can be executed string SrcPath = @"..\..\..\Build\" + Config.OSString + @"\XcodeSupportFiles\prepackage.sh"; string DestPath = Path.Combine(Config.PCXcodeStagingDir, @"prepackage.sh"); Program.Log(" ... '" + SrcPath + "' -> '" + DestPath + "'"); string SHContents = File.ReadAllText(SrcPath); SHContents = SHContents.Replace("\r\n", "\n"); File.WriteAllText(DestPath, SHContents); CookTime.CopySignedFiles(); }
public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true, bool bCheckDistro = true) { bNameMatch = false; // remap the gamename if necessary string GameName = Program.GameName; if (GameName == "UE4Game") { if (Config.ProjectFile.Length > 0) { GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile); } } // ensure the provision directory exists if (!Directory.Exists(Config.ProvisionDirectory)) { Directory.CreateDirectory(Config.ProvisionDirectory); } if (Config.bProvision) { if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision)) { return(Config.ProvisionDirectory + "/" + Config.Provision); } } #region remove after we provide an install mechanism CacheMobileProvisions(); #endregion // cache the provision library Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); ProvisionLibrary.Add(Provision, p); if (p.FileName.Contains(p.UUID) && !File.Exists(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"))) { File.Copy(Provision, Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision")); p = MobileProvisionParser.ParseFile(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision")); ProvisionLibrary.Add(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"), p); } } Program.Log("Searching for mobile provisions that match the game '{0}' (distribution: {3}) with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory, Config.bForDistribution); // check the cache for a provision matching the app id (com.company.Game) // First checking for a contains match and then for a wildcard match for (int Phase = -1; Phase < 3; ++Phase) { if (Phase == -1 && string.IsNullOrEmpty(Config.ProvisionUUID)) { continue; } foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary) { string DebugName = Path.GetFileName(Pair.Key); MobileProvision TestProvision = Pair.Value; // make sure the file is not managed by Xcode if (Path.GetFileName(TestProvision.FileName).ToLower().Equals(TestProvision.UUID.ToLower() + ".mobileprovision")) { continue; } Program.LogVerbose(" Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName); if (TestProvision.ProvisionName == "iOS Team Provisioning Profile: " + CFBundleIdentifier) { Program.LogVerbose(" Failing as provisioning is automatic"); continue; } // check to see if the platform is the same as what we are looking for if (!string.IsNullOrEmpty(TestProvision.Platform) && TestProvision.Platform != Config.OSString && !string.IsNullOrEmpty(Config.OSString)) { //Program.LogVerbose(" Failing platform {0} Config: {1}", TestProvision.Platform, Config.OSString); continue; } // Validate the name bool bPassesNameCheck = false; if (Phase == -1) { bPassesNameCheck = TestProvision.UUID == Config.ProvisionUUID; bNameMatch = bPassesNameCheck; } else if (Phase == 0) { bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bNameMatch = bPassesNameCheck; } else if (Phase == 1) { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName); } } } else { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); bPassesNameCheck = CompanyName == "*"; } } if (!bPassesNameCheck && bCheckIdentifier) { Program.LogVerbose(" .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier); continue; } if (Config.bForDistribution) { // Check to see if this is a distribution provision. get-task-allow must be false for distro profiles. // TestProvision.ProvisionedDeviceIDs.Count==0 is not a valid check as ad-hoc distro profiles do list devices. bool bDistroProv = !TestProvision.bDebug; if (!bDistroProv) { Program.LogVerbose(" .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { if (bCheckDistro) { bool bPassesDebugCheck = TestProvision.bDebug; if (!bPassesDebugCheck) { Program.LogVerbose(" .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { if (!TestProvision.bDebug) { Config.bForceStripSymbols = true; } } } // Check to see if the provision is in date DateTime CurrentUTCTime = DateTime.UtcNow; bool bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate); if (!bPassesDateCheck) { Program.LogVerbose(" .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime); continue; } // check to see if we have a certificate for this provision bool bPassesHasMatchingCertCheck = false; if (bCheckCert) { X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision); bPassesHasMatchingCertCheck = (Cert != null); if (bPassesHasMatchingCertCheck && Config.bCert) { bPassesHasMatchingCertCheck &= (CryptoAdapter.GetFriendlyNameFromCert(Cert) == Config.Certificate); } } else { bPassesHasMatchingCertCheck = true; } if (!bPassesHasMatchingCertCheck) { Program.LogVerbose(" .. Failed to find a matching certificate that was in date"); continue; } // Made it past all the tests Program.LogVerbose(" Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName); return(Pair.Key); } } // check to see if there is already an embedded provision string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision"); Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present"); return(EmbeddedMobileProvisionFilename); }
public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true) { bNameMatch = false; // remap the gamename if necessary string GameName = Program.GameName; if (GameName == "UE4Game") { if (Config.ProjectFile.Length > 0) { GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile); } } // ensure the provision directory exists if (!Directory.Exists(Config.ProvisionDirectory)) { Directory.CreateDirectory(Config.ProvisionDirectory); } if (Config.bProvision) { if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision)) { return(Config.ProvisionDirectory + "/" + Config.Provision); } } #region remove after we provide an install mechanism // copy all of the provisions from the game directory to the library if (!String.IsNullOrEmpty(Config.ProjectFile)) { var ProjectFileBuildIOSPath = Path.GetDirectoryName(Config.ProjectFile) + "/Build/IOS/"; if (Directory.Exists(ProjectFileBuildIOSPath)) { foreach (string Provision in Directory.EnumerateFiles(ProjectFileBuildIOSPath, "*.mobileprovision", SearchOption.AllDirectories)) { if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision)) { FileInfo DestFileInfo; if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision))) { DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true); DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } } } } // copy all of the provisions from the engine directory to the library { if (Directory.Exists(Config.EngineBuildDirectory)) { foreach (string Provision in Directory.EnumerateFiles(Config.EngineBuildDirectory, "*.mobileprovision", SearchOption.AllDirectories)) { if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision)) { FileInfo DestFileInfo; if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision))) { DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true); DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } } } } #endregion // cache the provision library Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); ProvisionLibrary.Add(Provision, p); } Program.Log("Searching for mobile provisions that match the game '{0}' with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory); // check the cache for a provision matching the app id (com.company.Game) // First checking for a contains match and then for a wildcard match for (int Phase = 0; Phase < 3; ++Phase) { foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary) { string DebugName = Path.GetFileName(Pair.Key); MobileProvision TestProvision = Pair.Value; Program.LogVerbose(" Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName); // Validate the name bool bPassesNameCheck = false; if (Phase == 0) { bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bNameMatch = bPassesNameCheck; } else if (Phase == 1) { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName); } } } else { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); bPassesNameCheck = CompanyName == "*"; } } if (!bPassesNameCheck && bCheckIdentifier) { Program.LogVerbose(" .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier); continue; } if (Config.bForDistribution) { // check to see if this is a distribution provision bool bDistroProv = (TestProvision.ProvisionedDeviceIDs.Count == 0) && !TestProvision.bDebug; if (!bDistroProv) { Program.LogVerbose(" .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { // check to see if we pass the debug check for non-distribution bool bPassesDebugCheck = TestProvision.bDebug; if (!bPassesDebugCheck) { Program.LogVerbose(" .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } // Check to see if the provision is in date DateTime CurrentUTCTime = DateTime.UtcNow; bool bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate); if (!bPassesDateCheck) { Program.LogVerbose(" .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime); continue; } // check to see if we have a certificate for this provision bool bPassesHasMatchingCertCheck = false; if (bCheckCert) { X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision); bPassesHasMatchingCertCheck = (Cert != null); if (bPassesHasMatchingCertCheck && Config.bCert) { bPassesHasMatchingCertCheck &= (Cert.FriendlyName == Config.Certificate); } } else { bPassesHasMatchingCertCheck = true; } if (!bPassesHasMatchingCertCheck) { Program.LogVerbose(" .. Failed to find a matching certificate that was in date"); continue; } // Made it past all the tests Program.LogVerbose(" Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName); return(Pair.Key); } } // check to see if there is already an embedded provision string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision"); Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present"); return(EmbeddedMobileProvisionFilename); }
/// <summary> /// Makes sure the required files for code signing exist and can be found /// </summary> public static bool FindRequiredFiles(out MobileProvision Provision, out X509Certificate2 Cert, out bool bHasOverridesFile, out bool bNameMatch, bool bNameCheck = true) { Provision = null; Cert = null; bHasOverridesFile = File.Exists(Config.GetPlistOverrideFilename()); bNameMatch = false; string CFBundleIdentifier = Config.OverrideBundleName; if (string.IsNullOrEmpty(CFBundleIdentifier)) { // Load Info.plist, which guides nearly everything else string plistFile = Config.EngineBuildDirectory + "/UE4Game-Info.plist"; if (!string.IsNullOrEmpty(Config.ProjectFile)) { plistFile = Path.GetDirectoryName(Config.ProjectFile) + "/Intermediate/IOS/" + Path.GetFileNameWithoutExtension(Config.ProjectFile) + "-Info.plist"; if (!File.Exists(plistFile)) { plistFile = Config.IntermediateDirectory + "/UE4Game-Info.plist"; if (!File.Exists(plistFile)) { plistFile = Config.EngineBuildDirectory + "/UE4Game-Info.plist"; } } } Utilities.PListHelper Info = null; try { string RawInfoPList = File.ReadAllText(plistFile, Encoding.UTF8); Info = new Utilities.PListHelper(RawInfoPList);; } catch (Exception ex) { Console.WriteLine(ex.Message); } if (Info == null) { return(false); } // Get the name of the bundle Info.GetString("CFBundleIdentifier", out CFBundleIdentifier); if (CFBundleIdentifier == null) { return(false); } else { CFBundleIdentifier = CFBundleIdentifier.Replace("${BUNDLE_IDENTIFIER}", Path.GetFileNameWithoutExtension(Config.ProjectFile)); } } // Check for a mobile provision try { string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch); Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename); } catch (Exception) { } // if we have a null provision see if we can find a compatible provision without checking for a certificate if (Provision == null) { try { string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch, false); Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename); } catch (Exception) { } // if we have a null provision see if we can find a valid provision without checking for name match if (Provision == null && !bNameCheck) { try { string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch, false, false); Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename); } catch (Exception) { } } } // Check for a suitable signature to match the mobile provision if (Provision != null) { Cert = CodeSignatureBuilder.FindCertificate(Provision); } return(true); }
/// <summary> /// Finds all valid installed provisions /// </summary> public static void FindProvisions(string CFBundleIdentifier) { // cache the provision library string SelectedProvision = ""; string SelectedCert = ""; string SelectedFile = ""; int FoundName = -1; Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); DateTime EffectiveDate = p.CreationDate; DateTime ExpirationDate = p.ExpirationDate; DateTime Now = DateTime.UtcNow; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); bool bValid = false; X509Certificate2 Cert = FindCertificate(p); if (Cert != null) { bValid = (Cert.NotBefore < Now) && (Cert.NotAfter > Now); } bool bPassesNameCheck = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bool bPassesCompanyCheck = false; bool bPassesWildCardCheck = false; if (p.ApplicationIdentifier.Contains("*")) { string CompanyName = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesCompanyCheck = CFBundleIdentifier.StartsWith(CompanyName); } else { bPassesWildCardCheck = true; } } bool bDistribution = ((p.ProvisionedDeviceIDs.Count == 0) && !p.bDebug); string Validity = "VALID"; if (!bCertTimeIsValid) { Validity = "EXPIRED"; } else if (!bValid) { Validity = "NO_CERT"; } else if (!bPassesNameCheck && !bPassesWildCardCheck && !bPassesCompanyCheck) { Validity = "NO_MATCH"; } if ((string.IsNullOrWhiteSpace(SelectedProvision) || FoundName < 2) && Validity == "VALID" && !bDistribution) { int Prev = FoundName; if (bPassesNameCheck) { FoundName = 2; } else if (bPassesCompanyCheck && FoundName < 1) { FoundName = 1; } else if (bPassesWildCardCheck && FoundName == -1) { FoundName = 0; } if (FoundName != Prev) { SelectedProvision = p.ProvisionName; SelectedFile = Path.GetFileName(Provision); SelectedCert = Cert.FriendlyName; } } Program.LogVerbose("PROVISION-File:{0},Name:{1},Validity:{2},StartDate:{3},EndDate:{4},Type:{5}", Path.GetFileName(Provision), p.ProvisionName, Validity, EffectiveDate.ToString(), ExpirationDate.ToString(), bDistribution ? "DISTRIBUTION" : "DEVELOPMENT"); } Program.LogVerbose("MATCHED-Provision:{0},File:{1},Cert:{2}", SelectedProvision, SelectedFile, SelectedCert); }
/// <summary> /// Finds all valid installed provisions /// </summary> public static void FindProvisions(string CFBundleIdentifier) { if (!Directory.Exists(Config.ProvisionDirectory)) { Program.Error("Could not find provision directory '{0}'.", Config.ProvisionDirectory); Program.ReturnCode = (int)ErrorCodes.Error_ProvisionNotFound; return; } // cache the provision library string SelectedProvision = ""; string SelectedCert = ""; string SelectedFile = ""; int FoundName = -1; Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string ProvisionFile in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionFile); ProvisionLibrary.Add(ProvisionFile, Provision); } // first sort all profiles so we look at newer ones first. IEnumerable <string> ProfileKeys = ProvisionLibrary.Select(KV => KV.Key) .OrderByDescending(K => ProvisionLibrary[K].CreationDate) .ToArray(); // note - all of this is a near duplicate of code in MobileProvisionUtilities, which other functions // in this class call to do similar work! // @todo - unify all of this. foreach (string ProvisionFile in ProfileKeys) { MobileProvision p = ProvisionLibrary[ProvisionFile]; DateTime EffectiveDate = p.CreationDate; DateTime ExpirationDate = p.ExpirationDate; DateTime Now = DateTime.UtcNow; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); bool bValid = false; X509Certificate2 Cert = FindCertificate(p); if (Cert != null) { bValid = (Cert.NotBefore.ToUniversalTime() < Now) && (Cert.NotAfter.ToUniversalTime() > Now); } bool bPassesNameCheck = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bool bPassesCompanyCheck = false; bool bPassesWildCardCheck = false; if (p.ApplicationIdentifier.Contains("*")) { string CompanyName = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesCompanyCheck = CFBundleIdentifier.StartsWith(CompanyName); } else { bPassesWildCardCheck = true; } } bool bIsManaged = false; if (p.ProvisionName == "iOS Team Provisioning Profile: " + CFBundleIdentifier) { bIsManaged = true; } bool bDistribution = ((p.ProvisionedDeviceIDs.Count == 0) && !p.bDebug); string Validity = "VALID"; if (!bCertTimeIsValid) { Validity = "EXPIRED"; } else if (!bValid) { Validity = "NO_CERT"; } else if (!bPassesNameCheck && !bPassesWildCardCheck && !bPassesCompanyCheck) { Validity = "NO_MATCH"; } if (bIsManaged) { Validity = "MANAGED"; } if ((string.IsNullOrWhiteSpace(SelectedProvision) || FoundName < 2) && Validity == "VALID" && !bDistribution) { int Prev = FoundName; if (bPassesNameCheck) { FoundName = 2; } else if (bPassesCompanyCheck && FoundName < 1) { FoundName = 1; } else if (bPassesWildCardCheck && FoundName == -1) { FoundName = 0; } if (FoundName != Prev) { SelectedProvision = p.ProvisionName; SelectedFile = Path.GetFileName(ProvisionFile); SelectedCert = CryptoAdapter.GetFriendlyNameFromCert(Cert); } } Program.LogVerbose("PROVISION-File:{0},Name:{1},Validity:{2},StartDate:{3},EndDate:{4},Type:{5}", Path.GetFileName(ProvisionFile), p.ProvisionName, Validity, EffectiveDate.ToString(), ExpirationDate.ToString(), bDistribution ? "DISTRIBUTION" : "DEVELOPMENT"); } Program.LogVerbose("MATCHED-Provision:{0},File:{1},Cert:{2}", SelectedProvision, SelectedFile, SelectedCert); }