void Load (string fileName) { this.FileName = fileName; string fileText = File.ReadAllText (fileName); //find the raw plist within the .mobileprovision file int start = fileText.IndexOf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); int length; if (start < 0 || (length = (fileText.IndexOf ("</plist>", start) - start)) < 1) throw new Exception ("Did not find XML plist in '" + fileName + "'"); length += "</plist>".Length; string rawPlist = fileText.Substring (start, length); var doc = new PlistDocument (); doc.LoadFromXml (rawPlist); var rootDict = (PlistDictionary) doc.Root; var prefixes = rootDict["ApplicationIdentifierPrefix"] as PlistArray; if (prefixes != null) this.ApplicationIdentifierPrefix = prefixes.OfType<PlistString> ().Select (x => x.Value).ToArray (); var creationDate = rootDict ["CreationDate"] as PlistDate; if (creationDate != null) this.CreationDate = creationDate.Value; var devCerts = rootDict ["DeveloperCertificates"] as PlistArray; if (devCerts != null) this.DeveloperCertificates = devCerts.OfType<PlistData> ().Select (x => new X509Certificate2 (x.Value)).ToArray (); var entl = rootDict ["Entitlements"] as PlistDictionary; if (entl != null) this.Entitlements = entl; //string application-identifier, bool get-task-allow, string[] keychain-access-groups var expirationDate = rootDict ["ExpirationDate"] as PlistDate; if (expirationDate != null) this.ExpirationDate = expirationDate.Value; var name = rootDict ["Name"] as PlistString; if (name != null) this.Name = name.Value; var provDevs = rootDict.TryGetValue ("ProvisionedDevices") as PlistArray; if (provDevs != null) this.ProvisionedDevices = provDevs.OfType<PlistString> ().Select (x => x.Value).ToArray (); var ttl = rootDict ["TimeToLive"] as PlistInteger; if (ttl != null) this.TimeToLive = ttl.Value; var uuid = rootDict ["UUID"] as PlistString; if (uuid != null) this.Uuid = uuid.Value; var version = rootDict ["Version"] as PlistInteger; if (version != null) this.Version = version.Value; }
protected override BuildResult Build(IProgressMonitor monitor, SolutionEntityItem item, ConfigurationSelector configuration) { MonobjcProject proj = item as MonobjcProject; if (proj == null || proj.CompileTarget != CompileTarget.Exe) return base.Build(monitor, item, configuration); BuildResult result = base.Build(monitor, item, configuration); if (result.ErrorCount > 0) return result; var conf = (MonobjcProjectConfiguration) proj.GetConfiguration(configuration); // Create directories monitor.BeginTask("Creating application bundle", 0); foreach (var path in new [] { conf.ResourcesDirectory, conf.MacOSDirectory }) { if (!Directory.Exists(path)) Directory.CreateDirectory(path); } monitor.EndTask(); string exeName = Path.GetFileNameWithoutExtension(conf.CompiledOutputName); // Write Info.plist into 'Contents' directory monitor.BeginTask("Updating application manifest", 0); var doc = new PlistDocument(); var docRoot = new PlistDictionary(); docRoot["CFBundleExecutable"] = exeName; docRoot["CFBundleInfoDictionaryVersion"] = "6.0"; docRoot["CFBundlePackageType"] = "APPL"; docRoot["CFBundleName"] = proj.BundleDisplayName ?? proj.Name; docRoot["CFBundleDisplayName"] = proj.BundleDisplayName ?? proj.Name; docRoot["CFBundleIdentifier"] = proj.BundleIdentifier ?? String.Format("com.yourcompany.{0}", proj.Name); docRoot["CFBundleVersion"] = proj.BundleVersion ?? "1.0"; docRoot["CFBundleDevelopmentRegion"] = proj.BundleDevelopmentRegion ?? "English"; FilePath icon = proj.BundleIcon.ToRelative (proj.BaseDirectory); if (!(icon.IsNullOrEmpty || icon.ToString () == ".")) docRoot["CFBundleIconFile"] = icon.FileName; if (!String.IsNullOrEmpty (proj.MainNibFile.ToString ())) docRoot["NSMainNibFile"] = Path.GetFileNameWithoutExtension(proj.MainNibFile.ToString ()); doc.Root = docRoot; var plistOut = conf.ContentsDirectory.Combine("Info.plist"); using (XmlTextWriter writer = new XmlTextWriter(plistOut, Encoding.UTF8)) { doc.Write(writer); } monitor.EndTask(); // Copy project binary into 'Resources' directory monitor.BeginTask("Copying application binary", 0); File.Copy(conf.CompiledOutputName, conf.ResourcesDirectory.Combine(conf.CompiledOutputName.FileName), true); if (File.Exists(conf.CompiledOutputName + ".mdb")) File.Copy(conf.CompiledOutputName + ".mdb", conf.ResourcesDirectory.Combine(conf.CompiledOutputName.FileName + ".mdb"), true); monitor.EndTask(); // Copy references into 'Resources' directory var references = BuildUtils.GetReferencesFilePairs(proj, configuration); foreach (var pair in references) { pair.EnsureOutputDirectory(); monitor.Log.WriteLine("Copying '{0}' to '{1}'", pair.Input, pair.Output); File.Copy(pair.Input, pair.Output, true); monitor.Step(1); } monitor.EndTask(); // Write launcher script into 'MacOS' directory monitor.BeginTask("Writing launcher script", 0); string scriptPath = conf.MacOSDirectory.Combine(exeName); var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("LaunchScript.sh"); using (StreamReader reader = new StreamReader(stream)) { using (StreamWriter writer = new StreamWriter(scriptPath)) { writer.Write(reader.ReadToEnd()); } } // Make executable (755) new UnixFileInfo(scriptPath).FileAccessPermissions = FileAccessPermissions.UserReadWriteExecute | FileAccessPermissions.GroupRead | FileAccessPermissions.GroupExecute | FileAccessPermissions.OtherRead | FileAccessPermissions.OtherExecute; monitor.EndTask(); return result; }
public override bool Execute() { string displayName = DisplayName ?? Name; string identifier = Identifier ?? String.Format("com.yourcompany.{0}", Name); string version = Version ?? "1.0"; string region = DevelopmentRegion ?? "English"; var doc = new PlistDocument(); var docRoot = new PlistDictionary(); docRoot["CFBundleExecutable"] = ExeName; docRoot["CFBundleInfoDictionaryVersion"] = "6.0"; docRoot["CFBundlePackageType"] = "APPL"; docRoot["CFBundleName"] = displayName; docRoot["CFBundleDisplayName"] = displayName; docRoot["CFBundleIdentifier"] = identifier; docRoot["CFBundleVersion"] = version; docRoot["CFBundleDevelopmentRegion"] = region; if (!String.IsNullOrEmpty(Icon)) docRoot["CFBundleIconFile"] = Icon; if (!String.IsNullOrEmpty(MainNibFile)) docRoot["NSMainNibFile"] = Path.GetFileNameWithoutExtension(MainNibFile); doc.Root = docRoot; using (XmlTextWriter writer = new XmlTextWriter(FileName, Encoding.UTF8)) { doc.Write(writer); } Log.LogMessage("Wrote {0}", FileName); return true; }
static void WriteXcent (PlistDocument doc, string file) { //write the plist to a byte[] as UTF8 without a BOM var ms = new MemoryStream (); var xmlSettings = new XmlWriterSettings () { Encoding = new UTF8Encoding (false), //no BOM CloseOutput = false, Indent = true, IndentChars = "\t", NewLineChars = "\n", }; using (var writer = XmlTextWriter.Create (ms, xmlSettings)) doc.Write (writer); //HACK: workaround for bug in Apple's entitlements XML parser //having written to a UTF8 stream to convince the xmlwriter to do the right thing, //we now convert to string and back to do some substitutions to work around bugs //in Apple's braindead entitlements XML parser. //Specifically, it chokes on "<true />" but accepts "<true"> //Hence, to be on the safe side, we produce EXACTLY the same format var sb = new StringBuilder (Encoding.UTF8.GetString (ms.GetBuffer ())); sb.Replace ("-//Apple Computer//DTD PLIST 1.0//EN", "-//Apple//DTD PLIST 1.0//EN"); sb.Replace ("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.Replace ("\n\t", "\n"); sb.Replace (" />\n", "/>\n"); sb.Append ("\n"); var buf = Encoding.UTF8.GetBytes (sb.ToString ()); //write the xcent file with the magic header, length, and the plist byte[] magic = new byte[] { 0xfa, 0xde, 0x71, 0x71 }; byte[] fileLen = Mono.DataConverter.BigEndian.GetBytes ((uint)buf.Length + 8); // 8 = magic.length + magicLen.Length using (var fs = File.Open (file, FileMode.Create)) { fs.Write (magic, 0, magic.Length); fs.Write (fileLen, 0, fileLen.Length); fs.Write (buf, 0, (int)buf.Length); } }
static BuildResult GenXcent (IProgressMonitor monitor, IPhoneProject proj, IPhoneProjectConfiguration conf, IPhoneAppIdentity identity, out string xcentName) { xcentName = conf.CompiledOutputName.ChangeExtension (".xcent"); monitor.BeginTask (GettextCatalog.GetString ("Processing entitlements file"), 0); string srcFile; if (!string.IsNullOrEmpty (conf.CodesignEntitlements)) { if (!File.Exists (conf.CodesignEntitlements)) return BuildError ("Entitlements file \"" + conf.CodesignEntitlements + "\" not found."); srcFile = conf.CodesignEntitlements; } else { srcFile = "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS" + conf.MtouchSdkVersion + ".sdk/Entitlements.plist"; } var doc = new PlistDocument (); try { doc.LoadFromXmlFile (srcFile); } catch (Exception ex) { monitor.Log.WriteLine (ex.ToString ()); return BuildError ("Error loading entitlements source file '" + srcFile +"'."); } //insert the app ID into the plist at the beginning var oldDict = doc.Root as PlistDictionary; var newDict = new PlistDictionary (); doc.Root = newDict; newDict["application-identifier"] = identity.AppID; var keychainGroups = new PlistArray (new [] { identity.AppID } ); newDict["keychain-access-groups"] = keychainGroups; //merge in the user's values foreach (var item in oldDict) { //FIXME: we currently ignore these items, and write our own, but maybe we should do substitutes //i.e. $(AppIdentifierPrefix)$(CFBundleIdentifier) if (item.Key == "application-identifier") { var str = item.Value as PlistString; if (str == null || string.IsNullOrEmpty (str.Value) || str.Value.Contains ('$')) continue; } else if (item.Key == "keychain-access-groups") { //special handling, merge into the array var keyArr = item.Value as PlistArray; foreach (var key in keyArr) { var str = key as PlistString; if (str != null && !string.IsNullOrEmpty (str.Value) && !str.Value.Contains ('$')) { keychainGroups.Add (str.Value); } } continue; } newDict[item.Key] = item.Value; } //merge in the settings from the provisioning profile, skipping some foreach (var item in identity.Profile.Entitlements) if (item.Key != "application-identifier" && item.Key != "keychain-access-groups") newDict[item.Key] = item.Value; try { WriteXcent (doc, xcentName); } catch (Exception ex) { monitor.Log.WriteLine (ex.ToString ()); return BuildError ("Error writing entitlements file '" + xcentName +"'."); } monitor.EndTask (); return null; }
static BuildResult CreateMergedPlist (IProgressMonitor monitor, IPhoneProjectConfiguration conf, ProjectFile template, string outPath, Func<IPhoneProjectConfiguration, PlistDocument,BuildResult> merge) { var result = new BuildResult (); var doc = new PlistDocument (); if (template != null) { try { doc.LoadFromXmlFile (template.FilePath); } catch (Exception ex) { if (ex is XmlException) result.AddError (template.FilePath, ((XmlException)ex).LineNumber, ((XmlException)ex).LinePosition, null, ex.Message); else result.AddError (template.FilePath, 0, 0, null, ex.Message); monitor.ReportError (GettextCatalog.GetString ("Could not load file '{0}': {1}", template.FilePath, ex.Message), null); return result; } } if (result.Append (merge (conf, doc)).ErrorCount > 0) return result; try { EnsureDirectoryForFile (outPath); using (XmlTextWriter writer = new XmlTextWriter (outPath, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; doc.Write (writer); } } catch (Exception ex) { result.AddError (outPath, 0, 0, null, ex.Message); monitor.ReportError (GettextCatalog.GetString ("Could not write file '{0}'", outPath), ex); } return result; }