Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit a5dcca9

Browse files
authored
Add better cert handling
1 parent 1a2fea6 commit a5dcca9

1 file changed

Lines changed: 85 additions & 17 deletions

File tree

Sources/prostore/certificates/OfficialCertificateManager.swift

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -125,48 +125,116 @@ class OfficialCertificateManager: ObservableObject {
125125
do {
126126
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: nil)
127127

128+
certs[index].status = "Downloading zip..."
129+
currentStatus = certs[index].status
128130
let (zipData, _) = try await URLSession.shared.data(from: cert.downloadURL)
129131
let tempZipURL = tempDir.appendingPathComponent("cert.zip")
130132
try zipData.write(to: tempZipURL)
131133

134+
certs[index].status = "Unzipping..."
135+
currentStatus = certs[index].status
132136
// Unzip using ZIPFoundation
133137
try FileManager.default.unzipItem(at: tempZipURL, to: tempDir)
134138

135-
// Find the extraction directory (root or single subdir)
136-
let contents = try FileManager.default.contentsOfDirectory(at: tempDir, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
137-
var searchDir = tempDir
138-
if contents.count == 1 {
139-
let firstItem = contents[0]
139+
// Recursively enumerate files and ignore any path that contains __MACOSX
140+
var p12Urls: [URL] = []
141+
var provUrls: [URL] = []
142+
var txtUrls: [URL] = []
143+
144+
let enumerator = FileManager.default.enumerator(at: tempDir, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles], errorHandler: nil)
145+
while let item = enumerator?.nextObject() as? URL {
146+
// skip anything in a __MACOSX folder
147+
if item.pathComponents.contains("__MACOSX") { continue }
148+
// only consider files
140149
var isDir: ObjCBool = false
141-
if FileManager.default.fileExists(atPath: firstItem.path, isDirectory: &isDir), isDir.boolValue {
142-
searchDir = firstItem
150+
if FileManager.default.fileExists(atPath: item.path, isDirectory: &isDir), isDir.boolValue { continue }
151+
152+
let ext = item.pathExtension.lowercased()
153+
if ext == "p12" {
154+
p12Urls.append(item)
155+
} else if ext == "mobileprovision" {
156+
provUrls.append(item)
157+
} else if ext == "txt" {
158+
txtUrls.append(item)
143159
}
144160
}
145161

146-
let fileContents = try FileManager.default.contentsOfDirectory(at: searchDir, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
147-
var p12URL: URL?
148-
var provURL: URL?
149-
var txtURL: URL?
162+
// Helper: extract the highest integer from a filename (returns nil if none)
163+
func highestNumber(in string: String) -> Int? {
164+
do {
165+
let regex = try NSRegularExpression(pattern: "\\d+", options: [])
166+
let ns = string as NSString
167+
let matches = regex.matches(in: string, options: [], range: NSRange(location: 0, length: ns.length))
168+
let ints = matches.compactMap { match -> Int? in
169+
let numStr = ns.substring(with: match.range)
170+
return Int(numStr)
171+
}
172+
return ints.max()
173+
} catch {
174+
return nil
175+
}
176+
}
150177

151-
for url in fileContents {
152-
let ext = url.pathExtension.lowercased()
153-
if ext == "p12" { p12URL = url }
154-
else if ext == "mobileprovision" { provURL = url }
155-
else if ext == "txt" { txtURL = url }
178+
// Choose mobile provision: if multiple, pick one with highest number in filename, otherwise random
179+
var chosenProvURL: URL?
180+
if provUrls.count == 1 {
181+
chosenProvURL = provUrls.first
182+
} else if provUrls.count > 1 {
183+
// Map each URL to its highest number (if any)
184+
var best: (url: URL, num: Int?)?
185+
for u in provUrls {
186+
let name = u.lastPathComponent
187+
let num = highestNumber(in: name)
188+
if best == nil {
189+
best = (u, num)
190+
} else {
191+
switch (best!.num, num) {
192+
case (nil, nil):
193+
// keep current best (we'll pick random fallback below if none have numbers)
194+
break
195+
case (nil, .some):
196+
best = (u, num)
197+
case (.some(let a), .some(let b)):
198+
if b > a { best = (u, num) }
199+
case (.some, nil):
200+
break
201+
}
202+
}
203+
}
204+
if let bestNum = best?.num {
205+
// There was at least one with a number — pick the one with max number
206+
let maxNumber = bestNum
207+
if let pick = provUrls.first(where: { highestNumber(in: $0.lastPathComponent) == maxNumber }) {
208+
chosenProvURL = pick
209+
}
210+
} else {
211+
// no numbers found in any filename — pick random
212+
chosenProvURL = provUrls.randomElement()
213+
}
156214
}
157215

158-
guard let p12U = p12URL, let provU = provURL, let txtU = txtURL else {
216+
// Basic selection for p12 and txt: pick the first found (could be improved if you want)
217+
let chosenP12URL = p12Urls.first
218+
let chosenTxtURL = txtUrls.first
219+
220+
// Validate that we have required files
221+
guard let p12U = chosenP12URL, let provU = chosenProvURL, let txtU = chosenTxtURL else {
159222
certs[index].status = "Error: Missing p12, mobileprovision or txt"
160223
currentStatus = certs[index].status
161224
throw NSError(domain: "MissingFiles", code: 1, userInfo: [NSLocalizedDescriptionKey: "Zip missing p12, provision, or txt"])
162225
}
163226

227+
certs[index].status = "Reading files..."
228+
currentStatus = certs[index].status
229+
164230
let txtData = try Data(contentsOf: txtU)
165231
let password = String(data: txtData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
166232

167233
let p12Data = try Data(contentsOf: p12U)
168234
let provData = try Data(contentsOf: provU)
169235

236+
certs[index].status = "Verifying certificate..."
237+
currentStatus = certs[index].status
170238
let result = CertificatesManager.check(p12Data: p12Data, password: password, mobileProvisionData: provData)
171239

172240
switch result {

0 commit comments

Comments
 (0)