コード例 #1
0
        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);
        }
コード例 #2
0
        /**
         * Copy a set of files
         */
        static public bool CopyFiles(string SourceFolder, string DestFolder, string DisplayDestFolder, string FileSpec, string ExcludeExtension)
        {
            if (DisplayDestFolder == null)
            {
                DisplayDestFolder = DestFolder;
            }

            Program.Log(" ... '" + SourceFolder + "\\" + FileSpec + "' -> '" + DisplayDestFolder + "'");

            // Ensure the source folder exists - fail otherwise
            DirectoryInfo SourceFolderInfo = new DirectoryInfo(SourceFolder);

            if (!SourceFolderInfo.Exists)
            {
                Program.Warning("Source folder does not exist: " + SourceFolderInfo);
                return(false);
            }

            // Ensure the destination folder exists - create otherwise
            DirectoryInfo DestFolderInfo = new DirectoryInfo(DestFolder);

            if (!DestFolderInfo.Exists)
            {
                Directory.CreateDirectory(DestFolder);
            }

            foreach (FileInfo SourceInfo in SourceFolderInfo.GetFiles(FileSpec))
            {
                // Don't copy any file with the excluded extension
                if (ExcludeExtension != null && SourceInfo.Extension == ExcludeExtension)
                {
                    continue;
                }

                string DestPathName = Path.Combine(DestFolderInfo.FullName, SourceInfo.Name);

                CopyFile(SourceInfo, DestPathName, false, DefaultRetryCount);
            }

            return(true);
        }
コード例 #3
0
        /**
         * Using the stub IPA previously compiled on the Mac, create a new IPA with assets
         */
        static public void RepackageIPAFromStub()
        {
            if (string.IsNullOrEmpty(Config.RepackageStagingDirectory) || !Directory.Exists(Config.RepackageStagingDirectory))
            {
                Program.Error("Directory specified with -stagedir could not be found!");
                return;
            }


            DateTime             StartTime  = DateTime.Now;
            CodeSignatureBuilder CodeSigner = null;

            // Clean the staging directory
            Program.ExecuteCommand("Clean", null);

            // Create a copy of the IPA so as to not trash the original
            ZipFile Zip = SetupWorkIPA();

            if (Zip == null)
            {
                return;
            }

            string ZipWorkingDir = String.Format("Payload/{0}{1}.app/", Program.GameName, Program.Architecture);

            FileOperations.ZipFileSystem FileSystem = new FileOperations.ZipFileSystem(Zip, ZipWorkingDir);

            // Check for a staged plist that needs to be merged into the main one
            {
                // Determine if there is a staged one we should try to use instead
                string PossiblePList = Path.Combine(Config.RepackageStagingDirectory, "Info.plist");
                if (File.Exists(PossiblePList))
                {
                    if (Config.bPerformResignWhenRepackaging)
                    {
                        Program.Log("Found Info.plist ({0}) in stage, which will be merged in with stub plist contents", PossiblePList);

                        // Merge the two plists, using the staged one as the authority when they conflict
                        byte[] StagePListBytes = File.ReadAllBytes(PossiblePList);
                        string StageInfoString = Encoding.UTF8.GetString(StagePListBytes);

                        byte[] StubPListBytes          = FileSystem.ReadAllBytes("Info.plist");
                        Utilities.PListHelper StubInfo = new Utilities.PListHelper(Encoding.UTF8.GetString(StubPListBytes));

                        StubInfo.MergePlistIn(StageInfoString);

                        // Write it back to the cloned stub, where it will be used for all subsequent actions
                        byte[] MergedPListBytes = Encoding.UTF8.GetBytes(StubInfo.SaveToString());
                        FileSystem.WriteAllBytes("Info.plist", MergedPListBytes);
                    }
                    else
                    {
                        Program.Warning("Found Info.plist ({0}) in stage that will be ignored; IPP cannot combine it with the stub plist since -sign was not specified", PossiblePList);
                    }
                }
            }

            // Get the name of the executable file
            string CFBundleExecutable;

            {
                // Load the .plist from the stub
                byte[] RawInfoPList = FileSystem.ReadAllBytes("Info.plist");

                Utilities.PListHelper Info = new Utilities.PListHelper(Encoding.UTF8.GetString(RawInfoPList));

                // Get the name of the executable file
                if (!Info.GetString("CFBundleExecutable", out CFBundleExecutable))
                {
                    throw new InvalidDataException("Info.plist must contain the key CFBundleExecutable");
                }
            }

            // Tell the file system about the executable file name so that we can set correct attributes on
            // the file when zipping it up
            FileSystem.ExecutableFileName = CFBundleExecutable;

            // Prepare for signing if requested
            if (Config.bPerformResignWhenRepackaging)
            {
                // Start the resign process (load the mobileprovision and info.plist, find the cert, etc...)
                CodeSigner            = new CodeSignatureBuilder();
                CodeSigner.FileSystem = FileSystem;

                CodeSigner.PrepareForSigning();

                // Merge in any user overrides that exist
                UpdateVersion(CodeSigner.Info);
            }

            // Empty the current staging directory
            FileOperations.DeleteDirectory(new DirectoryInfo(Config.PCStagingRootDir));

            // we will zip files in the pre-staged payload dir
            string ZipSourceDir = Config.RepackageStagingDirectory;

            // Save the zip
            Program.Log("Saving IPA ...");

            FilesBeingModifiedToPrintOut.Clear();
            Zip.SaveProgress += UpdateSaveProgress;

            Zip.CompressionLevel = (Ionic.Zlib.CompressionLevel)Config.RecompressionSetting;

            // Add all of the payload files, replacing existing files in the stub IPA if necessary (should only occur for icons)
            {
                string   SourceDir    = Path.GetFullPath(ZipSourceDir);
                string[] PayloadFiles = Directory.GetFiles(SourceDir, "*.*", SearchOption.AllDirectories);
                foreach (string Filename in PayloadFiles)
                {
                    // Get the relative path to the file (this implementation only works because we know the files are all
                    // deeper than the base dir, since they were generated from a search)
                    string AbsoluteFilename = Path.GetFullPath(Filename);
                    string RelativeFilename = AbsoluteFilename.Substring(SourceDir.Length + 1).Replace('\\', '/');

                    string ZipAbsolutePath = String.Format("Payload/{0}{1}.app/{1}",
                                                           Program.GameName,
                                                           Program.Architecture,
                                                           RelativeFilename);

                    byte[] FileContents = File.ReadAllBytes(AbsoluteFilename);
                    if (FileContents.Length == 0)
                    {
                        // Zero-length files added by Ionic cause installation/upgrade to fail on device with error 0xE8000050
                        // We store a single byte in the files as a workaround for now
                        FileContents    = new byte[1];
                        FileContents[0] = 0;
                    }

                    FileSystem.WriteAllBytes(RelativeFilename, FileContents);

                    if ((FileContents.Length >= 1024 * 1024) || (Config.bVerbose))
                    {
                        FilesBeingModifiedToPrintOut.Add(ZipAbsolutePath);
                    }
                }
            }

            // Re-sign the executable if there is a signing context
            if (CodeSigner != null)
            {
                if (Config.OverrideBundleName != null)
                {
                    CodeSigner.Info.SetString("CFBundleDisplayName", Config.OverrideBundleName);
                    string CFBundleIdentifier;
                    if (CodeSigner.Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
                    {
                        CodeSigner.Info.SetString("CFBundleIdentifier", CFBundleIdentifier + "_" + Config.OverrideBundleName);
                    }
                }
                CodeSigner.PerformSigning();
            }

            // Stick in the iTunesArtwork PNG if available
            string iTunesArtworkPath = Path.Combine(Config.BuildDirectory, "iTunesArtwork");

            if (File.Exists(iTunesArtworkPath))
            {
                Zip.UpdateFile(iTunesArtworkPath, "");
            }

            // Save the Zip

            Program.Log("Compressing files into IPA (-compress={1}).{0}", Config.bVerbose ? "" : "  Only large files will be listed next, but other files are also being packaged.", Config.RecompressionSetting);
            FileSystem.Close();

            TimeSpan ZipLength = DateTime.Now - StartTime;

            FileInfo FinalZipInfo = new FileInfo(Zip.Name);


            Program.Log(String.Format("Finished repackaging into {2:0.00} MB IPA, written to '{0}' (took {1:0.00} s for all steps)",
                                      Zip.Name,
                                      ZipLength.TotalSeconds,
                                      FinalZipInfo.Length / (1024.0f * 1024.0f)));
        }
コード例 #4
0
        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);
        }
コード例 #5
0
        /// <summary>
        /// Constructs a MobileProvision from an xml blob extracted from the real ASN.1 file
        /// </summary>
        public MobileProvision(string EmbeddedPListText)
        {
            Data = new Utilities.PListHelper(EmbeddedPListText);

            // Now extract things

            // Key: ApplicationIdentifierPrefix, Array<String>
            List <string> PrefixList = Data.GetArray("ApplicationIdentifierPrefix", "string");

            if (PrefixList.Count > 1)
            {
                Program.Warning("Found more than one entry for ApplicationIdentifierPrefix in the .mobileprovision, using the first one found");
            }

            if (PrefixList.Count > 0)
            {
                ApplicationIdentifierPrefix = PrefixList[0];
            }

            // Example date string from the XML: "2014-06-30T20:45:55Z";
            DateTimeStyles AppleDateStyle = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;

            string CreationDateString;

            if (Data.GetDate("CreationDate", out CreationDateString))
            {
                CreationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
            }

            string ExpirationDateString;

            if (Data.GetDate("ExpirationDate", out ExpirationDateString))
            {
                ExpirationDate = DateTime.Parse(ExpirationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
            }

            // Key: DeveloperCertificates, Array<Data> (uuencoded)
            string        CertificatePassword = "";
            List <string> CertificateList     = Data.GetArray("DeveloperCertificates", "data");

            foreach (string EncodedCert in CertificateList)
            {
                byte[] RawCert = Convert.FromBase64String(EncodedCert);
                DeveloperCertificates.Add(new X509Certificate2(RawCert, CertificatePassword));
            }

            // Key: Name, String
            if (!Data.GetString("Name", out ProvisionName))
            {
                ProvisionName = "(unknown)";
            }

            // Key: ProvisionedDevices, Array<String>
            ProvisionedDeviceIDs = Data.GetArray("ProvisionedDevices", "string");

            // Key: application-identifier, Array<String>
            Utilities.PListHelper XCentPList = null;
            Data.ProcessValueForKey("Entitlements", "dict", delegate(XmlNode ValueNode)
            {
                XCentPList = Utilities.PListHelper.CloneDictionaryRootedAt(ValueNode);
            });

            // Modify the application-identifier to be fully qualified if needed
            if (!XCentPList.GetString("application-identifier", out ApplicationIdentifier))
            {
                ApplicationIdentifier = "(unknown)";
            }

            // check for get-task-allow
            bDebug = XCentPList.GetBool("get-task-allow");

            if (!Data.GetString("UUID", out UUID))
            {
                UUID = "(unkown)";
            }

            List <string> Platforms = Data.GetArray("Platform", "string");

            if (Platforms.Contains("iOS"))
            {
                Platform = "IOS";
            }
            else if (Platforms.Contains("tvOS"))
            {
                Platform = "TVOS";
            }
            else
            {
                Platform = "";
            }
        }
コード例 #6
0
        static int Main(string[] args)
        {
            // remember the working directory at start, as the game path could be relative to this path
            string InitialCurrentDirectory = Environment.CurrentDirectory;

            // set the working directory to the location of the application (so relative paths always work)
            Environment.CurrentDirectory = Path.GetDirectoryName(Application.ExecutablePath);

            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            // A simple, top-level try-catch block
            try
            {
                if (!ParseCommandLine(ref args))
                {
                    Log("Usage: iPhonePackager <Command> <GameName> [RPCCommand &| Switch]");
                    Log("");
                    Log("Common commands:");
                    Log(" ... RepackageIPA GameName");
                    Log(" ... PackageIPA GameName");
                    Log(" ... PackageApp GameName");
                    Log(" ... Deploy PathToIPA");
                    Log(" ... RepackageFromStage GameName");
                    Log(" ... Devices");
                    Log(" ... Validate");
                    Log(" ... Install");
                    Log("");
                    Log("Configuration switches:");
                    Log("	 -stagedir <path>		  sets the directory to copy staged files from (defaults to none)");
                    Log("	 -project <path>		  path to the project being packaged");
                    Log("	 -compress=fast|best|none  packaging compression level (defaults to none)");
                    Log("	 -strip					strip symbols during packaging");
                    Log("	 -config				   game configuration (e.g., Shipping, Development, etc...)");
                    Log("	 -distribution			 packaging for final distribution");
                    Log("	 -createstub			   packaging stub IPA for later repackaging");
                    Log("	 -mac <MacName>			overrides the machine to use for any Mac operations");
                    Log("	 -arch <Architecture>	  sets the architecture to use (blank for default, -simulator for simulator builds)");
                    Log("	 -device <DeviceID>		sets the device to install the IPA on");
                    Log("");
                    Log("Commands: RPC, Clean");
                    Log("  StageMacFiles, GetIPA, Deploy, Install, Uninstall");
                    Log("");
                    Log("RPC Commands: SetExec, InstallProvision, MakeApp, DeleteIPA, Copy, Kill, Strip, Zip, GenDSYM");
                    Log("");
                    Log("Sample commandlines:");
                    Log(" ... iPhonePackager Deploy UDKGame Release");
                    Log(" ... iPhonePackager RPC SwordGame Shipping MakeApp");
                    return((int)ErrorCodes.Error_Arguments);
                }

                Log("Executing iPhonePackager " + String.Join(" ", args));
                Log("CWD: " + Directory.GetCurrentDirectory());
                Log("Initial Dir: " + InitialCurrentDirectory);
                Log("Env CWD: " + Environment.CurrentDirectory);

                // Ensure shipping configuration for final distributions
                if (Config.bForDistribution && (GameConfiguration != "Shipping"))
                {
                    Program.Warning("Distribution builds should be made in the Shipping configuration!");
                }

                // process the GamePath (if could be ..\Samples\MyDemo\ or ..\Samples\MyDemo\MyDemo.uproject
                GameName = Path.GetFileNameWithoutExtension(GamePath);
                if (GameName.Equals("UE4", StringComparison.InvariantCultureIgnoreCase) || GameName.Equals("Engine", StringComparison.InvariantCultureIgnoreCase))
                {
                    GameName = "UE4Game";
                }

                // setup configuration
                if (!Config.Initialize(InitialCurrentDirectory, GamePath))
                {
                    return((int)ErrorCodes.Error_Arguments);
                }

                switch (MainCommand.ToLowerInvariant())
                {
                case "validate":
                    // check to see if iTunes is installed
                    string dllPath = "";
                    if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix)
                    {
                        dllPath = "/Applications/Xcode.app/Contents/MacOS/Xcode";
                    }
                    else
                    {
                        dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "iTunesMobileDeviceDLL", null) as string;
                        if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath))
                        {
                            dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "MobileDeviceDLL", null) as string;
                        }
                    }
                    if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath))
                    {
                        Error("iTunes Not Found!!", (int)ErrorCodes.Error_SDKNotFound);
                    }
                    else
                    {
                        // validate there is a useable provision and cert
                        MobileProvision  Provision;
                        X509Certificate2 Cert;
                        bool             bHasOverrides;
                        bool             bNameMatch;
                        bool             foundPlist = CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrides, out bNameMatch);
                        if (!foundPlist)
                        {
                            Error("Could not find a valid plist file!!", (int)ErrorCodes.Error_InfoPListNotFound);
                        }
                        else if (Provision == null && Cert == null)
                        {
                            Error("No Provision or cert found!!", (int)ErrorCodes.Error_ProvisionAndCertificateNotFound);
                        }
                        else if (Provision == null)
                        {
                            Error("No Provision found!!", (int)ErrorCodes.Error_ProvisionNotFound);
                        }
                        else if (Cert == null)
                        {
                            Error("No Signing Certificate found!!", (int)ErrorCodes.Error_CertificateNotFound);
                        }
                    }
                    break;

                case "packageapp":
                    if (CheckArguments())
                    {
                        if (Config.bCreateStubSet)
                        {
                            Error("packageapp cannot be used with the -createstub switch");
                            Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
                        }
                        else
                        {
                            // Create the .app on the Mac
                            CompileTime.CreateApplicationDirOnMac();
                        }
                    }
                    break;

                case "repackagefromstage":
                    if (CheckArguments())
                    {
                        if (Config.bCreateStubSet)
                        {
                            Error("repackagefromstage cannot be used with the -createstub switches");
                            Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
                        }
                        else
                        {
                            bool bProbablyCreatedStub = Utilities.GetEnvironmentVariable("ue.IOSCreateStubIPA", true);
                            if (!bProbablyCreatedStub)
                            {
                                Warning("ue.IOSCreateStubIPA is currently FALSE, which means you may be repackaging with an out of date stub IPA!");
                            }

                            CookTime.RepackageIPAFromStub();
                        }
                    }
                    break;

                // this is the "super fast just move executable" mode for quick programmer iteration
                case "dangerouslyfast":
                    if (CheckArguments())
                    {
                        CompileTime.DangerouslyFastMode();
                    }
                    break;

                case "packageipa":
                    if (CheckArguments())
                    {
                        CompileTime.PackageIPAOnMac();
                    }
                    break;

                case "install":
                    GameName = "";
                    if (Config.bProvision)
                    {
                        ToolsHub.TryInstallingMobileProvision(Config.Provision, false);
                    }
                    if (Config.bCert)
                    {
                        ToolsHub.TryInstallingCertificate_PromptForKey(Config.Certificate, false);
                    }
                    CodeSignatureBuilder.FindCertificates();
                    CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName);
                    break;

                case "certificates":
                {
                    CodeSignatureBuilder.FindCertificates();
                    CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName);
                }
                break;

                case "resigntool":
                    RunInVisualMode(delegate { return(new GraphicalResignTool()); });
                    break;

                case "certrequest":
                    RunInVisualMode(delegate { return(new GenerateSigningRequestDialog()); });
                    break;

                case "gui":
                    RunInVisualMode(delegate { return(ToolsHub.CreateShowingTools()); });
                    break;

                case "devices":
                    ListDevices();
                    break;

                default:
                    // Commands by themself default to packaging for the device
                    if (CheckArguments())
                    {
                        ExecuteCommand(MainCommand, MainRPCCommand);
                    }
                    break;
                }
            }
            catch (Exception Ex)
            {
                Error("Application exception: " + Ex.ToString());
                if (ReturnCode == 0)
                {
                    Program.ReturnCode = (int)ErrorCodes.Error_Unknown;
                }
            }
            finally
            {
                if (DeploymentHelper.DeploymentServerProcess != null)
                {
                    DeploymentHelper.DeploymentServerProcess.Close();
                }
            }

            Environment.ExitCode = ReturnCode;
            return(ReturnCode);
        }
コード例 #7
0
        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);
        }
コード例 #8
0
        /// <summary>
        /// Does the actual work of signing the application
        ///   Modifies the following files:
        ///	 Info.plist
        ///	 [Executable] (file name derived from CFBundleExecutable in the Info.plist, e.g., UDKGame)
        ///	 _CodeSignature/CodeResources
        ///	 [ResourceRules] (file name derived from CFBundleResourceSpecification, e.g., CustomResourceRules.plist)
        /// </summary>
        public void PerformSigning()
        {
            DateTime SigningTime = DateTime.Now;

            // Get the name of the executable file
            string CFBundleExecutable;

            if (!Info.GetString("CFBundleExecutable", out CFBundleExecutable))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleExecutable");
            }

            // Get the name of the bundle
            string CFBundleIdentifier;

            if (!Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleIdentifier");
            }

            // Verify there is a resource rules file and make a dummy one if needed.
            // If it's missing, CreateCodeResourceDirectory can't proceed (the Info.plist is already written to disk at that point)
            if (!Info.HasKey("CFBundleResourceSpecification"))
            {
                // Couldn't find the key, create a dummy one
                string CFBundleResourceSpecification = "CustomResourceRules.plist";
                Info.SetString("CFBundleResourceSpecification", CFBundleResourceSpecification);

                Program.Warning("Info.plist was missing the key CFBundleResourceSpecification, creating a new resource rules file '{0}'.", CFBundleResourceSpecification);
            }

            // Save the Info.plist out
            byte[] RawInfoPList = Encoding.UTF8.GetBytes(Info.SaveToString());
            Info.SetReadOnly(true);
            FileSystem.WriteAllBytes("Info.plist", RawInfoPList);
            Program.Log(" ... Writing updated Info.plist");

            // Create the code resources file and load it
            byte[] ResourceDirBytes = CreateCodeResourcesDirectory(CFBundleExecutable);

            // Open the executable
            Program.Log("Opening source executable...");
            byte[] SourceExeData = FileSystem.ReadAllBytes(CFBundleExecutable);

            FatBinaryFile FatBinary = new FatBinaryFile();

            FatBinary.LoadFromBytes(SourceExeData);

            //@TODO: Verify it's an executable (not an object file, etc...)
            ulong CurrentStreamOffset = 0;

            byte[] FinalExeData = new byte[SourceExeData.Length + 1024 * 1024];
            int    ArchIndex    = 0;

            foreach (MachObjectFile Exe in FatBinary.MachObjectFiles)
            {
                Program.Log("... Processing one mach object (binary is {0})", FatBinary.bIsFatBinary ? "fat" : "thin");

                // Pad the memory stream with extra room to handle any possible growth in the code signing data
                int          OverSize        = 1024 * 1024;
                int          ExeSize         = (FatBinary.bIsFatBinary ? (int)FatBinary.Archs[ArchIndex].Size : SourceExeData.Length);
                MemoryStream OutputExeStream = new MemoryStream(ExeSize + OverSize);

                // Copy the data up to the executable into the final stream
                if (FatBinary.bIsFatBinary)
                {
                    if (ArchIndex == 0)
                    {
                        OutputExeStream.Seek(0, SeekOrigin.Begin);
                        OutputExeStream.Write(SourceExeData, (int)CurrentStreamOffset, (int)FatBinary.Archs[ArchIndex].Offset - (int)CurrentStreamOffset);
                        OutputExeStream.Seek(0, SeekOrigin.Begin);

                        byte[] HeaderData = OutputExeStream.ToArray();
                        HeaderData.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                        CurrentStreamOffset += (ulong)HeaderData.Length;
                    }
                    else
                    {
                        byte[] ZeroData = new byte[(int)FatBinary.Archs[ArchIndex].Offset - (int)CurrentStreamOffset];
                        ZeroData.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                        CurrentStreamOffset += (ulong)ZeroData.Length;
                    }
                }

                // Copy the executable into the stream
                int ExeOffset = (FatBinary.bIsFatBinary ? (int)FatBinary.Archs[ArchIndex].Offset : 0);
                OutputExeStream.Seek(0, SeekOrigin.Begin);
                OutputExeStream.Write(SourceExeData, ExeOffset, ExeSize);
                OutputExeStream.Seek(0, SeekOrigin.Begin);
                long Length = OutputExeStream.Length;

                // Find out if there was an existing code sign blob and find the linkedit segment command
                MachLoadCommandCodeSignature CodeSigningBlobLC = null;
                MachLoadCommandSegment       LinkEditSegmentLC = null;
                foreach (MachLoadCommand Command in Exe.Commands)
                {
                    if (CodeSigningBlobLC == null)
                    {
                        CodeSigningBlobLC = Command as MachLoadCommandCodeSignature;
                    }

                    if (LinkEditSegmentLC == null)
                    {
                        LinkEditSegmentLC = Command as MachLoadCommandSegment;
                        if (LinkEditSegmentLC.SegmentName != "__LINKEDIT")
                        {
                            LinkEditSegmentLC = null;
                        }
                    }
                }

                if (LinkEditSegmentLC == null)
                {
                    throw new InvalidDataException("Did not find a Mach segment load command for the __LINKEDIT segment");
                }

                // If the existing code signing blob command is missing, make sure there is enough space to add it
                // Insert the code signing blob if it isn't present
                //@TODO: Insert the code signing blob if it isn't present
                if (CodeSigningBlobLC == null)
                {
                    throw new InvalidDataException("Did not find a Code Signing LC.  Injecting one into a fresh executable is not currently supported.");
                }

                // Verify that the code signing blob is at the end of the linkedit segment (and thus can be expanded if needed)
                if ((CodeSigningBlobLC.BlobFileOffset + CodeSigningBlobLC.BlobFileSize) != (LinkEditSegmentLC.FileOffset + LinkEditSegmentLC.FileSize))
                {
                    throw new InvalidDataException("Code Signing LC was present but not at the end of the __LINKEDIT segment, unable to replace it");
                }

                int SignedFileLength = (int)CodeSigningBlobLC.BlobFileOffset;

                // Create the code directory blob
                CodeDirectoryBlob FinalCodeDirectoryBlob = CodeDirectoryBlob.Create(CFBundleIdentifier, SignedFileLength);

                // Create the entitlements blob
                string           EntitlementsText      = BuildEntitlementString(CFBundleIdentifier);
                EntitlementsBlob FinalEntitlementsBlob = EntitlementsBlob.Create(EntitlementsText);

                // Create or preserve the requirements blob
                RequirementsBlob FinalRequirementsBlob = null;
                if ((CodeSigningBlobLC != null) && Config.bMaintainExistingRequirementsWhenCodeSigning)
                {
                    RequirementsBlob OldRequirements = CodeSigningBlobLC.Payload.GetBlobByMagic(AbstractBlob.CSMAGIC_REQUIREMENTS_TABLE) as RequirementsBlob;
                    FinalRequirementsBlob = OldRequirements;
                }

                if (FinalRequirementsBlob == null)
                {
                    FinalRequirementsBlob = RequirementsBlob.CreateEmpty();
                }

                // Create the code signature blob (which actually signs the code directory)
                CodeDirectorySignatureBlob CodeSignatureBlob = CodeDirectorySignatureBlob.Create();

                // Create the code signature superblob (which contains all of the other signature-related blobs)
                CodeSigningTableBlob CodeSignPayload = CodeSigningTableBlob.Create();
                CodeSignPayload.Add(0x00000, FinalCodeDirectoryBlob);
                CodeSignPayload.Add(0x00002, FinalRequirementsBlob);
                CodeSignPayload.Add(0x00005, FinalEntitlementsBlob);
                CodeSignPayload.Add(0x10000, CodeSignatureBlob);


                // The ordering of the following steps (and doing the signature twice below) must be preserved.
                // The reason is there are some chicken-and-egg issues here:
                //   The code directory stores a hash of the header, but
                //   The header stores the size of the __LINKEDIT section, which is where the signature blobs go, but
                //   The CMS signature blob signs the code directory
                //
                // So, we need to know the size of a signature blob in order to write a header that is itself hashed
                // and signed by the signature blob

                // Do an initial signature just to get the size
                Program.Log("... Initial signature step ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                CodeSignatureBlob.SignCodeDirectory(SigningCert, SigningTime, FinalCodeDirectoryBlob);

                // Compute the size of everything, and push it into the EXE header
                byte[] DummyPayload = CodeSignPayload.GetBlobBytes();

                // Adjust the header and load command to have the correct size for the code sign blob
                WritingContext OutputExeContext = new WritingContext(new BinaryWriter(OutputExeStream));

                long BlobLength = DummyPayload.Length;

                long NonCodeSigSize    = (long)LinkEditSegmentLC.FileSize - CodeSigningBlobLC.BlobFileSize;
                long BlobStartPosition = NonCodeSigSize + (long)LinkEditSegmentLC.FileOffset;

                LinkEditSegmentLC.PatchFileLength(OutputExeContext, (uint)(NonCodeSigSize + BlobLength));
                CodeSigningBlobLC.PatchPositionAndSize(OutputExeContext, (uint)BlobStartPosition, (uint)BlobLength);

                // Now that the executable loader command has been inserted and the appropriate section modified, compute all the hashes
                Program.Log("... Computing hashes ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                OutputExeContext.Flush();

                // Fill out the special hashes
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdInfoSlot, RawInfoPList);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdRequirementsSlot, FinalRequirementsBlob.GetBlobBytes());
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdResourceDirSlot, ResourceDirBytes);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdApplicationSlot);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdEntitlementSlot, FinalEntitlementsBlob.GetBlobBytes());

                // Fill out the regular hashes
                FinalCodeDirectoryBlob.ComputeImageHashes(OutputExeStream.ToArray());

                // And compute the final signature
                Program.Log("... Final signature step ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                CodeSignatureBlob.SignCodeDirectory(SigningCert, SigningTime, FinalCodeDirectoryBlob);

                // Generate the signing blob and place it in the output (verifying it didn't change in size)
                byte[] FinalPayload = CodeSignPayload.GetBlobBytes();

                if (DummyPayload.Length != FinalPayload.Length)
                {
                    throw new InvalidDataException("CMS signature blob changed size between practice run and final run, unable to create useful code signing data");
                }

                OutputExeContext.PushPositionAndJump(BlobStartPosition);
                OutputExeContext.Write(FinalPayload);
                OutputExeContext.PopPosition();

                // Truncate the data so the __LINKEDIT section extends right to the end
                Program.Log("... Committing all edits ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                OutputExeContext.CompleteWritingAndClose();

                Program.Log("... Truncating/copying final binary", DateTime.Now - SigningTime);
                ulong DesiredExecutableLength = LinkEditSegmentLC.FileSize + LinkEditSegmentLC.FileOffset;

                if ((ulong)Length < DesiredExecutableLength)
                {
                    throw new InvalidDataException("Data written is smaller than expected, unable to finish signing process");
                }
                byte[] Data = OutputExeStream.ToArray();
                Data.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                CurrentStreamOffset += DesiredExecutableLength;

                // update the header if it is a fat binary
                if (FatBinary.bIsFatBinary)
                {
                    FatBinary.Archs[ArchIndex].Size = (uint)DesiredExecutableLength;
                }

                // increment the architecture index
                ArchIndex++;
            }

            // re-write the header
            FatBinary.WriteHeader(ref FinalExeData, 0);

            // resize to the finale size
            Array.Resize(ref FinalExeData, (int)CurrentStreamOffset);             //@todo: Extend the file system interface so we don't have to copy 20 MB just to truncate a few hundred bytes

            // Save the patched and signed executable
            Program.Log("Saving signed executable... ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
            FileSystem.WriteAllBytes(CFBundleExecutable, FinalExeData);

            Program.Log("Finished code signing, which took {0:0.00} s", (DateTime.Now - SigningTime).TotalSeconds);
        }
コード例 #9
0
 public void Warning(string Line)
 {
     Program.Warning(Line);
 }
コード例 #10
0
        public static bool ExecuteDeployCommand(string Command, string GamePath, string RPCCommand)
        {
            switch (Command.ToLowerInvariant())
            {
            case "backup":
            {
                string ApplicationIdentifier = RPCCommand;
                if (ApplicationIdentifier == null)
                {
                    ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                }

                if (Config.FilesForBackup.Count > 0)
                {
                    if (!DeploymentHelper.Get().BackupFiles(ApplicationIdentifier, Config.FilesForBackup.ToArray()))
                    {
                        Program.Error("Failed to transfer manifest file from device to PC");
                        Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
                    }
                }
                else if (!DeploymentHelper.Get().BackupDocumentsDirectory(ApplicationIdentifier, Config.GetRootBackedUpDocumentsDirectory()))
                {
                    Program.Error("Failed to transfer documents directory from device to PC");
                    Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
                }
            }
            break;

            case "uninstall":
            {
                string ApplicationIdentifier = RPCCommand;
                if (ApplicationIdentifier == null)
                {
                    ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                }

                if (!DeploymentHelper.Get().UninstallIPAOnDevice(ApplicationIdentifier))
                {
                    Program.Error("Failed to uninstall IPA on device");
                    Program.ReturnCode = (int)ErrorCodes.Error_AppUninstallFailed;
                }
            }
            break;

            case "deploy":
            case "install":
            {
                string IPAPath = GamePath;
                string AdditionalCommandline = Program.AdditionalCommandline;

                if (!String.IsNullOrEmpty(AdditionalCommandline) && !Config.bIterate)
                {
                    // Read the mobile provision to check for issues
                    FileOperations.ReadOnlyZipFileSystem Zip = new FileOperations.ReadOnlyZipFileSystem(IPAPath);
                    try
                    {
                        // Compare the commandline embedded to prevent us from any unnecessary writing.
                        byte[] CommandlineBytes    = Zip.ReadAllBytes("ue4commandline.txt");
                        string ExistingCommandline = Encoding.UTF8.GetString(CommandlineBytes, 0, CommandlineBytes.Length);
                        if (ExistingCommandline != AdditionalCommandline)
                        {
                            // Ensure we have a temp dir to stage our temporary ipa
                            if (!Directory.Exists(Config.PCStagingRootDir))
                            {
                                Directory.CreateDirectory(Config.PCStagingRootDir);
                            }

                            string TmpFilePath = Path.Combine(Path.GetDirectoryName(Config.PCStagingRootDir), Path.GetFileNameWithoutExtension(IPAPath) + ".tmp.ipa");
                            if (File.Exists(TmpFilePath))
                            {
                                File.Delete(TmpFilePath);
                            }

                            File.Copy(IPAPath, TmpFilePath);

                            // Get the project name:
                            string ProjectFile = ExistingCommandline.Split(' ').FirstOrDefault();

                            // Write out the new commandline.
                            FileOperations.ZipFileSystem WritableZip = new FileOperations.ZipFileSystem(TmpFilePath);
                            byte[] NewCommandline = Encoding.UTF8.GetBytes(ProjectFile + " " + AdditionalCommandline);
                            WritableZip.WriteAllBytes("ue4commandline.txt", NewCommandline);

                            // We need to residn the application after the commandline file has changed.
                            CodeSignatureBuilder CodeSigner = new CodeSignatureBuilder();
                            CodeSigner.FileSystem = WritableZip;

                            CodeSigner.PrepareForSigning();
                            CodeSigner.PerformSigning();

                            WritableZip.Close();

                            // Set the deploying ipa path to our new ipa
                            IPAPath = TmpFilePath;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Program.Warning(String.Format("Failed to override the commandline.txt file: ({0})", ex.Message));
                    }
                    Zip.Close();
                }

                if (Config.bIterate)
                {
                    string ApplicationIdentifier = RPCCommand;
                    if (String.IsNullOrEmpty(ApplicationIdentifier))
                    {
                        ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                    }

                    DeploymentHelper.Get().DeviceId = Config.DeviceId;
                    if (!DeploymentHelper.Get().InstallFilesOnDevice(ApplicationIdentifier, Config.DeltaManifest))
                    {
                        Program.Error("Failed to install Files on device");
                        Program.ReturnCode = (int)ErrorCodes.Error_FilesInstallFailed;
                    }
                }
                else if (File.Exists(IPAPath))
                {
                    DeploymentHelper.Get().DeviceId = Config.DeviceId;
                    if (!DeploymentHelper.Get().InstallIPAOnDevice(IPAPath))
                    {
                        Program.Error("Failed to install IPA on device");
                        Program.ReturnCode = (int)ErrorCodes.Error_AppInstallFailed;
                    }
                }
                else
                {
                    Program.Error(String.Format("Failed to find IPA file: '{0}'", IPAPath));
                    Program.ReturnCode = (int)ErrorCodes.Error_AppNotFound;
                }
            }
            break;

            default:
                return(false);
            }

            return(true);
        }
コード例 #11
0
		/**
		 * Copy a single file (deleting the existing file at the destination if it exists)
		 */
		static public bool CopyFile(FileInfo SourceFileInfo, string DestFileName, bool bCopyMustSucceed, int MaxRetryCount)
		{
			int CopyTryCount = 0;
			bool bFileCopiedSuccessfully = false;

			// if the file doesn't exist, but isn't needed, then just early out
			if (!bCopyMustSucceed && !SourceFileInfo.Exists)
			{
				return false;
			}

			if (Config.bVerbose)
			{
				Program.Log(String.Format("      Copy: {0} -> {1}, last modified at {2}", SourceFileInfo.FullName, DestFileName, SourceFileInfo.LastWriteTime));
			}

			do
			{
				// If this isn't the first time through, sleep a little before trying again
				if (CopyTryCount > 0)
				{
					Thread.Sleep(1000);
				}

				CopyTryCount++;

				try
				{
					// Delete the destination file if it exists
					FileInfo DestInfo = new FileInfo(DestFileName);
					if (DestInfo.Exists)
					{
						DestInfo.IsReadOnly = false;
						DestInfo.Delete();
					}

					Directory.CreateDirectory(Path.GetDirectoryName(DestFileName));

					// Copy over the new file
					SourceFileInfo.CopyTo(DestFileName);

					// Make sure the destination file is writable
					DestInfo = new FileInfo(DestFileName);
					DestInfo.IsReadOnly = false;
					DestInfo.LastWriteTime = SourceFileInfo.LastWriteTime;

					// Sanity check
					if (DestInfo.Length != SourceFileInfo.Length)
					{
						throw new Exception("Error: File copy failed (source and destination are different sizes)");
					}

					// Success!
					bFileCopiedSuccessfully = true;
				}
				catch( Exception Ex )
				{
					Program.Warning("Failed to copy file to '" + DestFileName + "'");
					Program.Warning("    Exception: " + Ex.Message + " (going to retry copy)");
				}
			}
			while (!bFileCopiedSuccessfully && (CopyTryCount < MaxRetryCount));

			if (!bFileCopiedSuccessfully)
			{
				string Msg = "Failed to copy file to " + DestFileName + " and there are no more retries!";
				if (bCopyMustSucceed)
				{
					Program.Error(Msg);
					Program.ReturnCode = (int)ErrorCodes.Error_CopyFile;
				}
				else
				{
					Program.Warning(Msg);
				}
			}

			return bFileCopiedSuccessfully;
		}