diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 559ad0b..a79a2cc 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -543,7 +543,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 22; + CURRENT_PROJECT_VERSION = 23; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BK8443AXLU; @@ -561,7 +561,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.1.0; + MARKETING_VERSION = 3.1.1; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -578,7 +578,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 22; + CURRENT_PROJECT_VERSION = 23; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BK8443AXLU; @@ -596,7 +596,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.1.0; + MARKETING_VERSION = 3.1.1; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Pearcleaner/Logic/Logic.swift b/Pearcleaner/Logic/Logic.swift index f6e5223..efa88c4 100644 --- a/Pearcleaner/Logic/Logic.swift +++ b/Pearcleaner/Logic/Logic.swift @@ -329,6 +329,11 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L continue } + // Skip if not a regular file, directory or symlink + if !isSupportedFileType(at: itemURL.path) { + continue + } + // Catch web app plist files if appInfo.webApp { if itemL.contains(bundleIdentifierL) { @@ -553,7 +558,7 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e let dispatchGroup = DispatchGroup() let allPaths = appState.appInfoStore.flatMap { $0.files.map { $0.path.pearFormat() } } let allNames = appState.appInfoStore.map { $0.appName.pearFormat() } - let skipped = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google"] // Skip system folders + let skipped = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices"] // Skip system folders // Skip locations that might not exist @@ -582,6 +587,10 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e continue } + if !isSupportedFileType(at: itemURL.path) { + continue + } + collection.append(itemURL) } @@ -631,36 +640,37 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e // Move files to trash using applescript/Finder so it asks for user password if needed func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {}) { @AppStorage("settings.sentinel.enable") var sentinel: Bool = false - var filesFinder = fileURLs - var filesSudo: [URL] = [] - - for file in fileURLs { - if isSocketFile(at: file) { - if let index = filesFinder.firstIndex(of: file) { - filesFinder.remove(at: index) - filesSudo.insert(file, at: 0) - } - } - } - - if !filesSudo.isEmpty { - // Remove socket files with rm - let filesSudoPaths = filesSudo.map { $0.path } - do { - let fileHandler = try Authorization.executeWithPrivileges("/bin/rm -f \(filesSudoPaths.joined(separator: " "))").get() - printOS(String(bytes: fileHandler.readDataToEndOfFile(), encoding: .utf8)!) - } catch { - printOS("Failed to remove socket file/s with privileges: \(error)") - } - } +// var filesFinder = fileURLs +// var filesSudo: [URL] = [] +// +// for file in fileURLs { +// if isSocketFile(at: file) { +// if let index = filesFinder.firstIndex(of: file) { +// filesFinder.remove(at: index) +// filesSudo.insert(file, at: 0) +// } +// } +// } +// +// if !filesSudo.isEmpty { +// // Remove socket files with rm +// let filesSudoPaths = filesSudo.map { $0.path } +// do { +// let fileHandler = try Authorization.executeWithPrivileges("/bin/rm -f \(filesSudoPaths.joined(separator: " "))").get() +// printOS(String(bytes: fileHandler.readDataToEndOfFile(), encoding: .utf8)!) +// } catch { +// printOS("Failed to remove socket file/s with privileges: \(error)") +// } +// } updateOnBackground { - let posixFiles = filesFinder.map { "POSIX file \"\($0.path)\", " }.joined().dropLast(3) + let posixFiles = fileURLs.map { "POSIX file \"\($0.path)\", " }.joined().dropLast(3) let scriptSource = """ tell application \"Finder\" to delete { \(posixFiles)" } """ + var error: NSDictionary? if let scriptObject = NSAppleScript(source: scriptSource) { let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(&error) @@ -689,6 +699,26 @@ func isSocketFile(at url: URL) -> Bool { return false } +func isSupportedFileType(at path: String) -> Bool { + let fileManager = FileManager.default + do { + let attributes = try fileManager.attributesOfItem(atPath: path) + if let fileType = attributes[FileAttributeKey.type] as? FileAttributeType { + switch fileType { + case .typeRegular, .typeDirectory, .typeSymbolicLink: + // The file is a regular file, directory, or symbolic link + return true + default: + // The file is a socket, pipe, or another type not supported + return false + } + } + } catch { + print("Error getting file attributes: \(error)") + } + return false +} + // Undo trash action func undoTrash(appState: AppState, completion: @escaping () -> Void = {}) { diff --git a/Pearcleaner/Views/TopBar.swift b/Pearcleaner/Views/TopBar.swift index 392b8ca..922a1e4 100644 --- a/Pearcleaner/Views/TopBar.swift +++ b/Pearcleaner/Views/TopBar.swift @@ -47,6 +47,7 @@ struct TopBar: View { withAnimation(.easeInOut(duration: 0.5)) { updateOnMain { appState.appInfo = .empty + appState.selectedZombieItems = [] if appState.zombieFile.fileSize.keys.count == 0 { appState.currentView = .zombie appState.showProgress.toggle() diff --git a/Pearcleaner/Views/TopBarMini.swift b/Pearcleaner/Views/TopBarMini.swift index 5551d3c..14d15e1 100644 --- a/Pearcleaner/Views/TopBarMini.swift +++ b/Pearcleaner/Views/TopBarMini.swift @@ -72,6 +72,7 @@ struct TopBarMini: View { showPopover = false updateOnMain { appState.appInfo = .empty + appState.selectedZombieItems = [] if appState.zombieFile.fileSize.keys.count == 0 { appState.currentView = .zombie appState.showProgress.toggle()