107 lines
3.2 KiB
Swift
107 lines
3.2 KiB
Swift
import Foundation
|
|
|
|
public enum FileSystemPermissionFlags {
|
|
case none
|
|
case read
|
|
case write
|
|
}
|
|
|
|
public struct FileSystemUtilities {
|
|
@discardableResult
|
|
public static func ensureDirExists(at url: URL?) -> Bool {
|
|
guard let url else {
|
|
return false
|
|
}
|
|
let exists = FileManager.default.fileExists(atPath: url.path)
|
|
|
|
if !exists {
|
|
do {
|
|
try FileManager.default.createDirectory(atPath: url.path, withIntermediateDirectories: true)
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
public static func generatePathInCache(_ appContext: AppContext?, in directory: String, extension: String) -> String {
|
|
guard let appContext, let dirPath = appContext.config.cacheDirectory?.appendingPathComponent(directory) else {
|
|
return ""
|
|
}
|
|
let fileName = UUID().uuidString.appending(`extension`)
|
|
ensureDirExists(at: dirPath)
|
|
return dirPath.appendingPathComponent(fileName).path
|
|
}
|
|
|
|
public static func permissions(_ appContext: AppContext?, for uri: URL) -> [FileSystemPermissionFlags] {
|
|
guard let scheme = uri.scheme else {
|
|
return [.none]
|
|
}
|
|
let validSchemas = ["assets-library", "http", "https", "ph"]
|
|
|
|
if validSchemas.contains(scheme) {
|
|
return [.read]
|
|
}
|
|
|
|
if scheme == "file" {
|
|
return getPathPermissions(appContext, for: uri)
|
|
}
|
|
|
|
return [.none]
|
|
}
|
|
|
|
private static func getPathPermissions(_ appContext: AppContext?, for path: URL) -> [FileSystemPermissionFlags] {
|
|
let permissionForInternalDirs = getInternalPathPermissions(appContext, for: path)
|
|
if !permissionForInternalDirs.contains(.none) {
|
|
return permissionForInternalDirs
|
|
}
|
|
return getExternalPathPermissions(path, appContext)
|
|
}
|
|
|
|
public static func getInternalPathPermissions(_ appContext: AppContext?, for url: URL) -> [FileSystemPermissionFlags] {
|
|
guard let appContext else {
|
|
return [.none]
|
|
}
|
|
|
|
let scopedDirs = [appContext.config.cacheDirectory, appContext.config.documentDirectory] + appContext.config.appGroupSharedDirectories
|
|
let standardizedPath = url.standardized.path
|
|
|
|
for dir in scopedDirs {
|
|
guard let dir else {
|
|
continue
|
|
}
|
|
if standardizedPath.hasPrefix(dir.appendingPathComponent("/").path) || standardizedPath == dir.path {
|
|
return [.read, .write]
|
|
}
|
|
}
|
|
|
|
let bundleDirectory = Bundle.main.bundlePath
|
|
if url.path.hasPrefix(bundleDirectory + "/") {
|
|
return [.read]
|
|
}
|
|
|
|
return [.none]
|
|
}
|
|
|
|
private static func getExternalPathPermissions(_ url: URL, _ appContext: AppContext?) -> [FileSystemPermissionFlags] {
|
|
if appContext?.config.scoped ?? false && url.path.contains("ExponentExperienceData") {
|
|
return []
|
|
}
|
|
|
|
// Defer permission checks for external paths to the underlying system at the time of file operations
|
|
return [.read, .write]
|
|
}
|
|
|
|
private static func getAppGroupSharedDirectories(_ appContext: AppContext) -> [String] {
|
|
let appGroups = appContext.appCodeSignEntitlements.appGroups ?? []
|
|
var appGroupSharedDirectories: [String] = []
|
|
for appGroup in appGroups {
|
|
if let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) {
|
|
appGroupSharedDirectories.append(directory.standardized.path)
|
|
}
|
|
}
|
|
return appGroups
|
|
}
|
|
}
|