2023-05-12 18:26:24 -05:00
|
|
|
//
|
|
|
|
|
// ALTAppPermission.swift
|
|
|
|
|
// AltStoreCore
|
|
|
|
|
//
|
|
|
|
|
// Created by Riley Testut on 5/3/23.
|
|
|
|
|
// Copyright © 2023 Riley Testut. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2023-10-10 15:29:34 -05:00
|
|
|
import RegexBuilder
|
|
|
|
|
|
2023-05-12 18:26:24 -05:00
|
|
|
import AltSign
|
|
|
|
|
|
2023-05-26 17:00:00 -05:00
|
|
|
extension ALTAppPermissionType
|
2023-05-12 18:26:24 -05:00
|
|
|
{
|
2023-05-26 17:00:00 -05:00
|
|
|
public var localizedName: String? {
|
2023-05-12 18:26:24 -05:00
|
|
|
switch self
|
|
|
|
|
{
|
|
|
|
|
case .unknown: return NSLocalizedString("Permission", comment: "")
|
|
|
|
|
case .entitlement: return NSLocalizedString("Entitlement", comment: "")
|
|
|
|
|
case .privacy: return NSLocalizedString("Privacy Permission", comment: "")
|
|
|
|
|
default: return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-26 17:00:00 -05:00
|
|
|
|
|
|
|
|
fileprivate var knownPermissionsKey: String? {
|
|
|
|
|
switch self
|
|
|
|
|
{
|
|
|
|
|
case .unknown: return nil
|
|
|
|
|
case .entitlement: return "entitlements"
|
|
|
|
|
case .privacy: return "privacy"
|
|
|
|
|
default: return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-12 18:26:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public protocol ALTAppPermission: RawRepresentable<String>, Hashable
|
|
|
|
|
{
|
|
|
|
|
var type: ALTAppPermissionType { get }
|
2023-05-25 18:03:21 -05:00
|
|
|
var synthesizedName: String? { get } // Kupo!
|
|
|
|
|
|
2023-05-26 17:00:00 -05:00
|
|
|
// Default implementations
|
|
|
|
|
var localizedName: String? { get }
|
2023-05-24 15:56:06 -05:00
|
|
|
var localizedDescription: String? { get }
|
2023-05-26 17:00:00 -05:00
|
|
|
var symbolName: String? { get }
|
2023-05-24 15:56:06 -05:00
|
|
|
|
2023-05-26 17:00:00 -05:00
|
|
|
// Convenience properties (also with default implementations).
|
2023-05-25 18:03:21 -05:00
|
|
|
// Would normally just be in extension, except that crashes Swift 5.8 compiler ¯\_(ツ)_/¯
|
|
|
|
|
var isKnown: Bool { get }
|
2023-05-24 15:56:06 -05:00
|
|
|
var effectiveSymbolName: String { get }
|
|
|
|
|
var localizedDisplayName: String { get }
|
2023-05-12 18:26:24 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 17:00:00 -05:00
|
|
|
private struct KnownPermission: Decodable
|
|
|
|
|
{
|
|
|
|
|
var localizedName: String
|
2023-05-31 15:01:13 -05:00
|
|
|
var localizedDescription: String?
|
2023-05-26 17:00:00 -05:00
|
|
|
var rawValue: String
|
|
|
|
|
var symbolName: String
|
|
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey
|
|
|
|
|
{
|
|
|
|
|
case localizedName = "name"
|
|
|
|
|
case localizedDescription = "description"
|
|
|
|
|
case rawValue = "key"
|
|
|
|
|
case symbolName = "symbol"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private let knownPermissions: [String: [String: KnownPermission]] = {
|
|
|
|
|
guard let fileURL = Bundle(for: DatabaseManager.self).url(forResource: "Permissions", withExtension: "plist"),
|
|
|
|
|
let data = try? Data(contentsOf: fileURL),
|
|
|
|
|
let propertyList = try? PropertyListDecoder().decode([String: [String: KnownPermission]].self, from: data)
|
|
|
|
|
else {
|
|
|
|
|
fatalError("Could not decode Permissions.plist.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return propertyList
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
public extension ALTAppPermission
|
|
|
|
|
{
|
|
|
|
|
private var knownPermission: KnownPermission? {
|
|
|
|
|
guard let key = self.type.knownPermissionsKey,
|
|
|
|
|
let permissions = knownPermissions[key]
|
|
|
|
|
else { return nil }
|
|
|
|
|
|
|
|
|
|
let knownPermission = permissions[self.rawValue]
|
|
|
|
|
return knownPermission
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var localizedName: String? { self.knownPermission?.localizedName }
|
|
|
|
|
var localizedDescription: String? { self.knownPermission?.localizedDescription }
|
|
|
|
|
var symbolName: String? { self.knownPermission?.symbolName }
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-12 18:26:24 -05:00
|
|
|
public extension ALTAppPermission
|
|
|
|
|
{
|
2023-05-25 18:03:21 -05:00
|
|
|
var isKnown: Bool {
|
2023-05-30 12:53:26 -05:00
|
|
|
// Assume all known permissions have non-nil localizedNames.
|
|
|
|
|
return self.localizedName != nil
|
2023-05-25 18:03:21 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 15:56:06 -05:00
|
|
|
var effectiveSymbolName: String { self.symbolName ?? "lock" }
|
|
|
|
|
|
2023-05-12 18:26:24 -05:00
|
|
|
var localizedDisplayName: String {
|
2023-05-25 18:03:21 -05:00
|
|
|
return self.localizedName ?? self.synthesizedName ?? self.rawValue
|
2023-05-12 18:26:24 -05:00
|
|
|
}
|
2023-05-26 17:00:00 -05:00
|
|
|
|
2023-05-12 18:26:24 -05:00
|
|
|
func isEqual(_ permission: any ALTAppPermission) -> Bool
|
|
|
|
|
{
|
|
|
|
|
guard let permission = permission as? Self else { return false }
|
|
|
|
|
return self == permission
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static func ==(lhs: Self, rhs: any ALTAppPermission) -> Bool
|
|
|
|
|
{
|
|
|
|
|
return lhs.isEqual(rhs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct UnknownAppPermission: ALTAppPermission
|
|
|
|
|
{
|
|
|
|
|
public var type: ALTAppPermissionType { .unknown }
|
2023-05-25 18:03:21 -05:00
|
|
|
public var synthesizedName: String? { nil }
|
|
|
|
|
|
2023-05-12 18:26:24 -05:00
|
|
|
public var rawValue: String
|
|
|
|
|
|
|
|
|
|
public init(rawValue: String)
|
|
|
|
|
{
|
|
|
|
|
self.rawValue = rawValue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension ALTEntitlement: ALTAppPermission
|
|
|
|
|
{
|
|
|
|
|
public var type: ALTAppPermissionType { .entitlement }
|
2023-05-25 18:03:21 -05:00
|
|
|
|
|
|
|
|
public var synthesizedName: String? {
|
|
|
|
|
// Attempt to convert last component of entitlement to human-readable string.
|
|
|
|
|
// e.g. com.apple.developer.kernel.increased-memory-limit -> "Increased Memory Limit"
|
|
|
|
|
let components = self.rawValue.components(separatedBy: ".")
|
|
|
|
|
guard let rawName = components.last else { return nil }
|
|
|
|
|
|
|
|
|
|
let words = rawName.components(separatedBy: "-").map { word in
|
|
|
|
|
switch word.lowercased()
|
|
|
|
|
{
|
|
|
|
|
case "carplay": return NSLocalizedString("CarPlay", comment: "")
|
|
|
|
|
default: return word.localizedCapitalized
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let synthesizedName = words.joined(separator: " ")
|
|
|
|
|
return synthesizedName
|
|
|
|
|
}
|
2023-05-12 18:26:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension ALTAppPrivacyPermission: ALTAppPermission
|
|
|
|
|
{
|
|
|
|
|
public var type: ALTAppPermissionType { .privacy }
|
|
|
|
|
|
2023-10-10 15:29:34 -05:00
|
|
|
public var synthesizedName: String? {
|
|
|
|
|
guard #available(iOS 16, *), let match = self.rawValue.wholeMatch(of: Regex.privacyPermission) else { return nil }
|
|
|
|
|
|
|
|
|
|
let synthesizedNamed = String(match.1)
|
|
|
|
|
return synthesizedNamed
|
|
|
|
|
}
|
2023-05-12 18:26:24 -05:00
|
|
|
}
|