From 30843a21c1d0219cac358b7bbe3406222dca49d0 Mon Sep 17 00:00:00 2001 From: Alin Date: Tue, 23 Apr 2024 14:38:36 -0600 Subject: [PATCH] v3.5.2 --- Pearcleaner.xcodeproj/project.pbxproj | 12 ++- Pearcleaner/Logic/AppInfoFetch.swift | 105 +------------------------- Pearcleaner/Logic/AppPathsFetch.swift | 80 ++++++++++++++------ 3 files changed, 69 insertions(+), 128 deletions(-) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 825deaa..0813eca 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -560,8 +560,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 35; - APP_VERSION = 3.5.1; + APP_BUILD = 36; + APP_VERSION = 3.5.2; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -630,8 +630,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 35; - APP_VERSION = 3.5.1; + APP_BUILD = 36; + APP_VERSION = 3.5.2; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -692,6 +692,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + APP_BUILD = 36; + APP_VERSION = 3.5.2; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; @@ -728,6 +730,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + APP_BUILD = 36; + APP_VERSION = 3.5.2; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; diff --git a/Pearcleaner/Logic/AppInfoFetch.swift b/Pearcleaner/Logic/AppInfoFetch.swift index d7a277a..bf82c5c 100644 --- a/Pearcleaner/Logic/AppInfoFetch.swift +++ b/Pearcleaner/Logic/AppInfoFetch.swift @@ -46,11 +46,10 @@ class AppInfoFetcher { return nil } -// let appName = bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String -// ?? bundle.infoDictionary?["CFBundleName"] as? String -// ?? path.deletingPathExtension().lastPathComponent +// let appName = bundle.infoDictionary?["CFBundleDisplayName"] as? String ?? bundle.infoDictionary?["CFBundleName"] as? String +// ?? bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String ?? path.deletingPathExtension().lastPathComponent - let appName = path.deletingPathExtension().lastPathComponent.capitalizingFirstLetter() + let appName = wrapped ? path.deletingLastPathComponent().deletingLastPathComponent().deletingPathExtension().lastPathComponent.capitalizingFirstLetter() : path.deletingPathExtension().lastPathComponent.capitalizingFirstLetter() let appVersion = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? bundle.infoDictionary?["CFBundleVersion"] as? String @@ -76,101 +75,3 @@ class AppInfoFetcher { } } - - - -// OLD FUNCTION ============================================================ - -//func getAppInfo(atPath path: URL, wrapped: Bool = false) -> AppInfo? { -// let filemanager = FileManager.default -// let wrapperURL = path.appendingPathComponent("Wrapper") -// if filemanager.fileExists(atPath: wrapperURL.path) { -// do { -// let contents = try filemanager.contentsOfDirectory(at: wrapperURL, includingPropertiesForKeys: nil, options: []) -// let appFiles = contents.filter { $0.pathExtension == "app" } -// -// if let firstAppFile = appFiles.first { -// let fullPath = wrapperURL.appendingPathComponent(firstAppFile.lastPathComponent) -// if let wrappedAppInfo = getAppInfo(atPath: fullPath, wrapped: true) { -// return wrappedAppInfo -// } -// } else { -// printOS("No .app files found in the 'Wrapper' directory: \(wrapperURL)") -// } -// } catch { -// printOS("Error reading contents of 'Wrapper' directory: \(error.localizedDescription)\n\(wrapperURL)") -// } -// } else { -// if let bundle = Bundle(url: path) { -// if let bundleIdentifier = bundle.bundleIdentifier { -// var appVersion: String? -// var appIcon: NSImage? -// var appName: String? -// var webApp: Bool? -// var wrappedApp: Bool? -// var system: Bool? -// -// if let shortVersion = bundle.infoDictionary?["CFBundleShortVersionString"] as? String, !shortVersion.isEmpty { -// appVersion = shortVersion -// } else { -// if let bundleVersion = bundle.infoDictionary?["CFBundleVersion"] as? String, !bundleVersion.isEmpty { -// appVersion = bundleVersion -// } else { -// printOS("Failed to retrieve bundle version") -// } -// } -// -// if let localizedName = bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String { -// appName = localizedName -// } else if let bundleName = bundle.infoDictionary?["CFBundleName"] as? String { -// appName = bundleName -// } else { -// appName = path.deletingPathExtension().lastPathComponent -// } -// -// // Get icon from app -// if path.absoluteString.contains("Wrapper") { -// appIcon = getIconForFileOrFolderNS(atPath: path.deletingLastPathComponent().deletingLastPathComponent()) -// } else { -// appIcon = getIconForFileOrFolderNS(atPath: path) -// } -// -// // Convert the icon to a 100x100 PNG image -// if let pngIcon = appIcon.flatMap({ convertICNSToPNG(icon: $0, size: NSSize(width: 100, height: 100)) }) { -// appIcon = pngIcon -// } -// -// if appIcon == nil { -// printOS("App Icon not found for app at path: \(path)") -// } -// -// if bundle.infoDictionary?["LSTemplateApplication"] is Bool { -// webApp = true -// } else { -// webApp = false -// } -// -// if wrapped { -// wrappedApp = true -// } else { -// wrappedApp = false -// } -// -// if !path.path.contains(home) { -// system = true -// } else { -// system = false -// } -// -// -// return AppInfo(id: UUID(), path: path, bundleIdentifier: bundleIdentifier, appName: appName ?? "", appVersion: appVersion ?? "", appIcon: appIcon, webApp: webApp ?? false, wrapped: wrappedApp ?? false, system: system ?? false, files: [], fileSize: [:], fileIcon: [:]) -// -// } else { -// printOS("Bundle identifier not found at path: \(path)") -// } -// } else { -// printOS("Bundle not found at path: \(path)") -// } -// } -// return nil -//} diff --git a/Pearcleaner/Logic/AppPathsFetch.swift b/Pearcleaner/Logic/AppPathsFetch.swift index b339891..0e1852e 100644 --- a/Pearcleaner/Logic/AppPathsFetch.swift +++ b/Pearcleaner/Logic/AppPathsFetch.swift @@ -176,34 +176,70 @@ class AppPathFinder { } - private func getGroupContainers(bundleURL: URL) -> [URL] { - // Get group containers - var staticCode: SecStaticCode? + private func getAllContainers(bundleURL: URL) -> [URL] { + var containers = [URL]() - guard SecStaticCodeCreateWithPath(bundleURL as CFURL, [], &staticCode) == errSecSuccess else { - return [] + // Extract bundle identifier from bundleURL + let bundleIdentifier = Bundle(url: bundleURL)?.bundleIdentifier + + // Ensure the bundleIdentifier is not nil + guard let containerBundleIdentifier = bundleIdentifier else { + printOS("Get Containers: No bundle identifier found for the given bundle URL.") + return containers // Returns whatever was found so far, possibly empty } - var signingInformation: CFDictionary? - - let status = SecCodeCopySigningInformation(staticCode!, SecCSFlags(), &signingInformation) - - if status != errSecSuccess { - printOS("Failed to copy signing information. Status: \(status)") - return [] + // Get the regular container URL for the extracted bundle identifier + if let groupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: containerBundleIdentifier) { + containers.append(groupContainer) + } else { + printOS("Get Containers: Failed to retrieve container URL for bundle identifier: \(containerBundleIdentifier)") } - guard let topDict = signingInformation as? [String: Any], - let entitlementsDict = topDict["entitlements-dict"] as? [String: Any], - let appGroups = entitlementsDict["com.apple.security.application-groups"] as? [String] else { - // printOS("No application groups to extract from entitlements for this app.") - return [] + let containersPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first?.appendingPathComponent("Containers") + + do { + let containerDirectories = try FileManager.default.contentsOfDirectory(at: containersPath!, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) + + // Define a regular expression to match UUID format + let uuidRegex = try NSRegularExpression(pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$", options: .caseInsensitive) + + for directory in containerDirectories { + let directoryName = directory.lastPathComponent + + // Check if the directory name matches the UUID pattern + if uuidRegex.firstMatch(in: directoryName, options: [], range: NSRange(location: 0, length: directoryName.utf16.count)) != nil { + // Attempt to read the metadata plist file + let metadataPlistURL = directory.appendingPathComponent(".com.apple.containermanagerd.metadata.plist") + if let metadataDict = NSDictionary(contentsOf: metadataPlistURL), let applicationBundleID = metadataDict["MCMMetadataIdentifier"] as? String { + if applicationBundleID == self.appInfo.bundleIdentifier { + containers.append(directory) + } + } + } + } + } catch { + printOS("Error accessing the Containers directory: \(error)") } - let groupContainersPath = appGroups.map { URL(fileURLWithPath: "\(home)/Library/Group Containers/" + $0) } - let existingGroupContainers = groupContainersPath.filter { FileManager.default.fileExists(atPath: $0.path) } - return existingGroupContainers +// let fileURL = URL(fileURLWithPath: "/Users/alin/Library/Containers/2792352D-95FE-43AE-947D-FF4BF31DE4E6") +// +// do { +// // Retrieve the localized name +// let resourceValues = try fileURL.resourceValues(forKeys: [.localizedNameKey]) +// if let localizedName = resourceValues.localizedName { +// print("Localized Name: \(localizedName)") +// } else { +// print("Localized name not available.") +// } +// } catch { +// print("Error retrieving localized name: \(error)") +// } + + + + // Return all found containers + return containers } @@ -231,13 +267,13 @@ class AppPathFinder { private func finalizeCollection() { DispatchQueue.global(qos: .userInitiated).async { -// let groupContainers = self.getGroupContainers(bundleURL: self.appInfo.path) + let allContainers = self.getAllContainers(bundleURL: self.appInfo.path) let outliers = self.handleOutliers() var tempCollection: [URL] = [] self.collectionAccessQueue.sync { tempCollection = self.collection } -// tempCollection.append(contentsOf: groupContainers) + tempCollection.append(contentsOf: allContainers) tempCollection.append(contentsOf: outliers) // Sort and standardize URLs to ensure consistent comparisons