From 0683fa4fcab27d3a3a55bfa955066dcf083f9803 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 9 Aug 2024 12:01:31 -0400 Subject: [PATCH] Support loading profisioning profiles from both locations --- UnitTests/TestMobileProvisionIndex.cs | 6 +- Xamarin.MacDev/MobileProvision.cs | 75 ++++++----- Xamarin.MacDev/MobileProvisionIndex.cs | 173 +++++++++++-------------- 3 files changed, 114 insertions(+), 140 deletions(-) diff --git a/UnitTests/TestMobileProvisionIndex.cs b/UnitTests/TestMobileProvisionIndex.cs index 373d730..90b53ad 100644 --- a/UnitTests/TestMobileProvisionIndex.cs +++ b/UnitTests/TestMobileProvisionIndex.cs @@ -33,10 +33,12 @@ namespace UnitTests { [TestFixture] public class TestMobileProvisionIndex { + static readonly string[] ProfileDirectories = new string [] { "../../TestData/Provisioning Profiles" }; + [Test] public void TestCreateIndex () { - var index = MobileProvisionIndex.CreateIndex ("../../TestData/Provisioning Profiles", "profiles.index"); + var index = MobileProvisionIndex.CreateIndex (ProfileDirectories, "profiles.index"); Assert.AreEqual (2, index.ProvisioningProfiles.Count); @@ -77,7 +79,7 @@ public void TestCreateIndex () [Test] public void TestOpenIndex () { - var index = MobileProvisionIndex.OpenIndex ("../../TestData/Provisioning Profiles", "profiles.index"); + var index = MobileProvisionIndex.OpenIndex (ProfileDirectories, "profiles.index"); Assert.AreEqual (2, index.ProvisioningProfiles.Count); diff --git a/Xamarin.MacDev/MobileProvision.cs b/Xamarin.MacDev/MobileProvision.cs index 3e85037..2116c24 100644 --- a/Xamarin.MacDev/MobileProvision.cs +++ b/Xamarin.MacDev/MobileProvision.cs @@ -50,7 +50,7 @@ public class MobileProvision { public const string AutomaticAppStore = "Automatic:AppStore"; public const string AutomaticInHouse = "Automatic:InHouse"; public const string AutomaticAdHoc = "Automatic:AdHoc"; - public static readonly string ProfileDirectory; + public static readonly string[] ProfileDirectories; static MobileProvision () { @@ -58,22 +58,19 @@ static MobileProvision () || Environment.OSVersion.Platform == PlatformID.Unix) { string personal = Environment.GetFolderPath (Environment.SpecialFolder.UserProfile); - // Xcode >= 16.x uses ~/Library/Developer/Xcode/UserData/Provisioning Profiles - string profileDir = Path.Combine (personal, "Library", "Developer", "Xcode", "UserData", "Provisioning Profiles"); + ProfileDirectories = new string[] { + // Xcode >= 16.x uses ~/Library/Developer/Xcode/UserData/Provisioning Profiles + Path.Combine (personal, "Library", "Developer", "Xcode", "UserData", "Provisioning Profiles"), - if (!Directory.Exists (profileDir)) { // Xcode < 16.x uses ~/Library/MobileDevice/Provisioning Profiles - profileDir = Path.Combine (personal, "Library", "MobileDevice", "Provisioning Profiles"); - } - - ProfileDirectory = profileDir; + Path.Combine (personal, "Library", "MobileDevice", "Provisioning Profiles"), + }; } else { - ProfileDirectory = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), - "Xamarin", - "iOS", - "Provisioning", - "Profiles"); + var appDataLocal = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData); + + ProfileDirectories = new string [] { + Path.Combine (appDataLocal, "Xamarin", "iOS", "Provisioning", "Profiles") + }; } } @@ -154,9 +151,6 @@ public static IList GetAllInstalledProvisions (MobileProvisionP /// public static IList GetAllInstalledProvisions (MobileProvisionPlatform platform, bool includeExpired) { - if (!Directory.Exists (ProfileDirectory)) - return new MobileProvision [0]; - var uuids = new Dictionary (); var list = new List (); var now = DateTime.Now; @@ -174,33 +168,38 @@ public static IList GetAllInstalledProvisions (MobileProvisionP throw new ArgumentOutOfRangeException (nameof (platform)); } - foreach (var file in Directory.EnumerateFiles (ProfileDirectory, pattern)) { - try { - var data = File.ReadAllBytes (file); - - var m = new MobileProvision (); - m.Load (PDictionary.FromBinaryXml (data)); - m.Data = data; - - if (includeExpired || m.ExpirationDate > now) { - if (uuids.ContainsKey (m.Uuid)) { - // we always want the most recently created/updated provision - if (m.CreationDate > uuids [m.Uuid].CreationDate) { - int index = list.IndexOf (uuids [m.Uuid]); - uuids [m.Uuid] = m; - list [index] = m; + foreach (var profileDir in ProfileDirectories) { + if (!Directory.Exists (profileDir)) + continue; + + foreach (var file in Directory.EnumerateFiles (profileDir, pattern)) { + try { + var data = File.ReadAllBytes (file); + + var m = new MobileProvision (); + m.Load (PDictionary.FromBinaryXml (data)); + m.Data = data; + + if (includeExpired || m.ExpirationDate > now) { + if (uuids.ContainsKey (m.Uuid)) { + // we always want the most recently created/updated provision + if (m.CreationDate > uuids [m.Uuid].CreationDate) { + int index = list.IndexOf (uuids [m.Uuid]); + uuids [m.Uuid] = m; + list [index] = m; + } + } else { + uuids.Add (m.Uuid, m); + list.Add (m); } - } else { - uuids.Add (m.Uuid, m); - list.Add (m); } + } catch (Exception ex) { + LoggingService.LogWarning ("Error reading " + platform + " provision file '" + file + "'", ex); } - } catch (Exception ex) { - LoggingService.LogWarning ("Error reading " + platform + " provision file '" + file + "'", ex); } } - //newest first + // newest first list.Sort ((x, y) => y.CreationDate.CompareTo (x.CreationDate)); return list; diff --git a/Xamarin.MacDev/MobileProvisionIndex.cs b/Xamarin.MacDev/MobileProvisionIndex.cs index 62f5bc3..0758a50 100644 --- a/Xamarin.MacDev/MobileProvisionIndex.cs +++ b/Xamarin.MacDev/MobileProvisionIndex.cs @@ -263,102 +263,72 @@ public void Save (string fileName) } } - public static MobileProvisionIndex CreateIndex (string profilesDir, string indexName) + public static MobileProvisionIndex CreateIndex (string[] profileDirs, string indexName) { var index = new MobileProvisionIndex (); + var mtime = DateTime.MinValue; - if (Directory.Exists (profilesDir)) { - foreach (var fileName in Directory.EnumerateFiles (profilesDir)) { - if (!fileName.EndsWith (".mobileprovision", StringComparison.Ordinal) && !fileName.EndsWith (".provisionprofile", StringComparison.Ordinal)) - continue; + foreach (var profileDir in profileDirs) { + if (Directory.Exists (profileDir)) { + var mtime2 = Directory.GetLastWriteTimeUtc (profileDir); - try { - var profile = ProvisioningProfile.Load (fileName); - index.ProvisioningProfiles.Add (profile); - } catch (Exception ex) { - LoggingService.LogWarning ("Error reading provisioning profile '{0}': {1}", fileName, ex); + if (mtime2 > mtime) + mtime = mtime2; + + foreach (var fileName in Directory.EnumerateFiles (profileDir)) { + if (!fileName.EndsWith (".mobileprovision", StringComparison.Ordinal) && !fileName.EndsWith (".provisionprofile", StringComparison.Ordinal)) + continue; + + try { + var profile = ProvisioningProfile.Load (fileName); + index.ProvisioningProfiles.Add (profile); + } catch (Exception ex) { + LoggingService.LogWarning ("Error reading provisioning profile '{0}': {1}", fileName, ex); + } } + } else { + Directory.CreateDirectory (profileDir); } - - index.ProvisioningProfiles.Sort (CreationDateComparer); - } else { - Directory.CreateDirectory (profilesDir); } + index.ProvisioningProfiles.Sort (CreationDateComparer); index.Version = IndexVersion; - index.LastModified = Directory.GetLastWriteTimeUtc (profilesDir); + index.LastModified = mtime; index.Save (indexName); return index; } - public static MobileProvisionIndex OpenIndex (string profilesDir, string indexName) + static bool AnyProfileDirModifiedSince (string[] dirs, DateTime mtime) { - MobileProvisionIndex index; + foreach (var dir in dirs) { + if (!Directory.Exists (dir)) { + // Note: When creating the index, we make sure to create each profile dir which means if any + // are deleted, then we likely need to reindex. + return true; + } - try { - index = Load (indexName); + var mtime2 = Directory.GetLastWriteTimeUtc (dir); - if (Directory.Exists (profilesDir)) { - var mtime = Directory.GetLastWriteTimeUtc (profilesDir); - - if (index.Version != IndexVersion) { - index = CreateIndex (profilesDir, indexName); - } else if (index.LastModified < mtime) { - var table = new Dictionary (); - - foreach (var profile in index.ProvisioningProfiles) - table [profile.FileName] = profile; - - foreach (var fileName in Directory.EnumerateFiles (profilesDir)) { - if (!fileName.EndsWith (".mobileprovision", StringComparison.Ordinal) && !fileName.EndsWith (".provisionprofile", StringComparison.Ordinal)) - continue; - - ProvisioningProfile profile; - bool unknown = false; - - if (table.TryGetValue (Path.GetFileName (fileName), out profile)) { - // remove from our lookup table (any leftover key/valie pairs will be used to determine deleted files) - table.Remove (Path.GetFileName (fileName)); - - // check if the file has changed since our last resync - mtime = File.GetLastWriteTimeUtc (fileName); - - if (profile.LastModified < mtime) { - // remove the old record - index.ProvisioningProfiles.Remove (profile); - - // treat this provisioning profile as if it is unknown - unknown = true; - } - } else { - unknown = true; - } - - if (unknown) { - // unknown provisioning profile; add it to our ProvisioningProfiles array - try { - profile = ProvisioningProfile.Load (fileName); - index.ProvisioningProfiles.Add (profile); - } catch (Exception ex) { - LoggingService.LogWarning ("Error reading provisioning profile '{0}': {1}", fileName, ex); - } - } - } + if (mtime2 > mtime) + return true; + } - // remove provisioning profiles which have been deleted from the file system - foreach (var item in table) - index.ProvisioningProfiles.Remove (item.Value); + return false; + } - index.LastModified = Directory.GetLastWriteTimeUtc (profilesDir); - index.Version = IndexVersion; + public static MobileProvisionIndex OpenIndex (string[] profileDirs, string indexName) + { + MobileProvisionIndex index; - index.ProvisioningProfiles.Sort (CreationDateComparer); + try { + index = Load (indexName); - index.Save (indexName); - } - } else { + if (index.Version != IndexVersion || AnyProfileDirModifiedSince (profileDirs, index.LastModified)) + index = CreateIndex (profileDirs, indexName); + + if (index.ProvisioningProfiles.Count == 0) { try { File.Delete (indexName); } catch (Exception ex) { @@ -368,7 +338,7 @@ public static MobileProvisionIndex OpenIndex (string profilesDir, string indexNa index.ProvisioningProfiles.Clear (); } } catch { - index = CreateIndex (profilesDir, indexName); + index = CreateIndex (profileDirs, indexName); } return index; @@ -377,12 +347,15 @@ public static MobileProvisionIndex OpenIndex (string profilesDir, string indexNa public static MobileProvision GetMobileProvision (MobileProvisionPlatform platform, string name, List failures = null) { var extension = MobileProvision.GetFileExtension (platform); - var path = Path.Combine (MobileProvision.ProfileDirectory, name + extension); - if (File.Exists (path)) - return MobileProvision.LoadFromFile (path); + foreach (var profileDir in MobileProvision.ProfileDirectories) { + var path = Path.Combine (profileDir, name + extension); + + if (File.Exists (path)) + return MobileProvision.LoadFromFile (path); + } - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var latestCreationDate = DateTime.MinValue; if (index.ProvisioningProfiles.Count == 0) { @@ -402,7 +375,7 @@ public static MobileProvision GetMobileProvision (MobileProvisionPlatform platfo } if (name == profile.Name || name == profile.Uuid) - return MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + return MobileProvision.LoadFromFile (profile.FileName); failures?.Add ($"The profile '{profile.Name}' is not applicable because its Name and Uuid ({profile.Uuid}) do not match '{name}'."); } @@ -412,7 +385,7 @@ public static MobileProvision GetMobileProvision (MobileProvisionPlatform platfo public static IList GetMobileProvisions (MobileProvisionPlatform platform, bool includeExpired = false, bool unique = false, List failures = null) { - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var extension = MobileProvision.GetFileExtension (platform); var dictionary = new Dictionary (); var list = new List (); @@ -447,14 +420,14 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor if (dictionary.TryGetValue (profile.Name, out idx)) { if (profile.CreationDate > list [idx].CreationDate) - list [idx] = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + list [idx] = MobileProvision.LoadFromFile (profile.FileName); } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); dictionary.Add (profile.Name, list.Count); list.Add (provision); } } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); list.Add (provision); } } @@ -464,7 +437,7 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor public static IList GetMobileProvisions (MobileProvisionPlatform platform, MobileProvisionDistributionType type, bool includeExpired = false, bool unique = false, List failures = null) { - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var extension = MobileProvision.GetFileExtension (platform); var dictionary = new Dictionary (); var list = new List (); @@ -504,14 +477,14 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor if (dictionary.TryGetValue (profile.Name, out idx)) { if (profile.CreationDate > list [idx].CreationDate) - list [idx] = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + list [idx] = MobileProvision.LoadFromFile (profile.FileName); } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); dictionary.Add (profile.Name, list.Count); list.Add (provision); } } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); list.Add (provision); } } @@ -521,7 +494,7 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor public static IList GetMobileProvisions (MobileProvisionPlatform platform, MobileProvisionDistributionType type, IList developerCertificates, bool includeExpired = false, bool unique = false, List failures = null) { - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var extension = MobileProvision.GetFileExtension (platform); var dictionary = new Dictionary (); var thumbprints = new HashSet (); @@ -579,14 +552,14 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor if (dictionary.TryGetValue (profile.Name, out idx)) { if (profile.CreationDate > list [idx].CreationDate) - list [idx] = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + list [idx] = MobileProvision.LoadFromFile (profile.FileName); } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); dictionary.Add (profile.Name, list.Count); list.Add (provision); } } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); list.Add (provision); } break; @@ -598,7 +571,7 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor public static IList GetMobileProvisions (MobileProvisionPlatform platform, string bundleIdentifier, MobileProvisionDistributionType type, bool includeExpired = false, bool unique = false, List failures = null) { - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var extension = MobileProvision.GetFileExtension (platform); var dictionary = new Dictionary (); var list = new List (); @@ -662,14 +635,14 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor if (dictionary.TryGetValue (profile.Name, out idx)) { if (profile.CreationDate > list [idx].CreationDate) - list [idx] = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + list [idx] = MobileProvision.LoadFromFile (profile.FileName); } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); dictionary.Add (profile.Name, list.Count); list.Add (provision); } } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); list.Add (provision); } } @@ -679,7 +652,7 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor public static IList GetMobileProvisions (MobileProvisionPlatform platform, string bundleIdentifier, MobileProvisionDistributionType type, IList developerCertificates, bool includeExpired = false, bool unique = false, List failures = null) { - var index = OpenIndex (MobileProvision.ProfileDirectory, IndexFileName); + var index = OpenIndex (MobileProvision.ProfileDirectories, IndexFileName); var extension = MobileProvision.GetFileExtension (platform); var dictionary = new Dictionary (); var thumbprints = new HashSet (); @@ -761,14 +734,14 @@ public static IList GetMobileProvisions (MobileProvisionPlatfor if (dictionary.TryGetValue (profile.Name, out idx)) { if (profile.CreationDate > list [idx].CreationDate) - list [idx] = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + list [idx] = MobileProvision.LoadFromFile (profile.FileName); } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); dictionary.Add (profile.Name, list.Count); list.Add (provision); } } else { - var provision = MobileProvision.LoadFromFile (Path.Combine (MobileProvision.ProfileDirectory, profile.FileName)); + var provision = MobileProvision.LoadFromFile (profile.FileName); list.Add (provision); } break;