diff --git a/RootHelper/main.m b/RootHelper/main.m index 2fe6922..c83536d 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -123,6 +123,33 @@ NSString* appPathForAppId(NSString* appId) return nil; } +NSString* findAppNameInBundlePath(NSString* bundlePath) +{ + NSArray* bundleItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:nil]; + for(NSString* bundleItem in bundleItems) + { + if([bundleItem.pathExtension isEqualToString:@"app"]) + { + return bundleItem; + } + } + return nil; +} + +NSString* findAppPathInBundlePath(NSString* bundlePath) +{ + NSString* appName = findAppNameInBundlePath(bundlePath); + if(!appName) return nil; + return [bundlePath stringByAppendingPathComponent:appName]; +} + +NSURL* findAppURLInBundleURL(NSURL* bundleURL) +{ + NSString* appName = findAppNameInBundlePath(bundleURL.path); + if(!appName) return nil; + return [bundleURL URLByAppendingPathComponent:appName]; +} + BOOL isMachoFile(NSString* filePath) { FILE* file = fopen(filePath.fileSystemRepresentation, "r"); @@ -205,12 +232,13 @@ void setTSURLSchemeState(BOOL newState, NSString* customAppPath) } } - -void installLdid(NSString* ldidToCopyPath) +void installLdid(NSString* ldidToCopyPath, NSString* ldidVersion) { if(![[NSFileManager defaultManager] fileExistsAtPath:ldidToCopyPath]) return; NSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"]; + NSString* ldidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid.version"]; + if([[NSFileManager defaultManager] fileExistsAtPath:ldidPath]) { [[NSFileManager defaultManager] removeItemAtPath:ldidPath error:nil]; @@ -218,8 +246,11 @@ void installLdid(NSString* ldidToCopyPath) [[NSFileManager defaultManager] copyItemAtPath:ldidToCopyPath toPath:ldidPath error:nil]; + NSData* ldidVersionData = [ldidVersion dataUsingEncoding:NSUTF8StringEncoding]; + [ldidVersionData writeToFile:ldidVersionPath atomically:YES]; + chmod(ldidPath.fileSystemRepresentation, 0755); - chown(ldidPath.fileSystemRepresentation, 0, 0); + chmod(ldidVersionPath.fileSystemRepresentation, 0644); } BOOL isLdidInstalled(void) @@ -433,7 +464,6 @@ int signApp(NSString* appPath) NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"]; if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]]) { - // if TSBundlePreSigned = YES, this bundle has been externally signed so we can skip over signing it now NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned; if([tsBundleIsPreSignedNum boolValue] == YES) @@ -442,7 +472,7 @@ int signApp(NSString* appPath) return 0; } } - + SecStaticCodeRef codeRef = getStaticCodeRef(executablePath); if(codeRef != NULL) { @@ -478,34 +508,8 @@ int signApp(NSString* appPath) } else { - // Work around an ldid bug where it doesn't keep entitlements on stray binaries - NSMutableDictionary* storedEntitlements = [NSMutableDictionary new]; - NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil]; - NSURL* fileURL; - while(fileURL = [enumerator nextObject]) - { - NSString* filePath = fileURL.path; - if(isMachoFile(filePath)) - { - storedEntitlements[filePath] = dumpEntitlementsFromBinaryAtPath(filePath); - } - } - // app has entitlements, keep them ldidRet = runLdid(@[@"-s", certArg, appPath], nil, &errorOutput); - - [storedEntitlements enumerateKeysAndObjectsUsingBlock:^(NSString* binaryPath, NSDictionary* entitlements, BOOL* stop) - { - NSDictionary* newEntitlements = dumpEntitlementsFromBinaryAtPath(binaryPath); - if(!newEntitlements || ![newEntitlements isEqualToDictionary:entitlements]) - { - NSString* tmpEntitlementPlistPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"ent.xml"]; - [entitlements writeToURL:[NSURL fileURLWithPath:tmpEntitlementPlistPath] error:nil]; - NSString* tmpEntitlementArg = [@"-S" stringByAppendingString:tmpEntitlementPlistPath]; - runLdid(@[tmpEntitlementArg, certArg, binaryPath], nil, nil); - [[NSFileManager defaultManager] removeItemAtPath:tmpEntitlementPlistPath error:nil]; - } - }]; } NSLog(@"ldid exited with status %d", ldidRet); @@ -581,7 +585,7 @@ void applyPatchesToInfoDictionary(NSString* appPath) // 172: no info.plist found in app // 173: app is not signed and cannot be signed because ldid not installed or didn't work // 174: -int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate) +int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, BOOL useInstalldMethod) { NSLog(@"[installApp force = %d]", force); @@ -590,18 +594,18 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate) NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPayloadPath error:nil]; if(!items) return 167; - NSString* appBundlePath; + NSString* appBundleToInstallPath; for(NSString* item in items) { if([item.pathExtension isEqualToString:@"app"]) { - appBundlePath = [appPayloadPath stringByAppendingPathComponent:item]; + appBundleToInstallPath = [appPayloadPath stringByAppendingPathComponent:item]; break; } } - if(!appBundlePath) return 167; + if(!appBundleToInstallPath) return 167; - NSString* appId = appIdForAppPath(appBundlePath); + NSString* appId = appIdForAppPath(appBundleToInstallPath); if(!appId) return 176; if(([appId.lowercaseString isEqualToString:@"com.opa334.trollstore"] && !isTSUpdate) || [immutableAppBundleIdentifiers() containsObject:appId.lowercaseString]) @@ -609,115 +613,151 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate) return 179; } - if(!infoDictionaryForAppPath(appBundlePath)) return 172; + if(!infoDictionaryForAppPath(appBundleToInstallPath)) return 172; if(!isTSUpdate) { - applyPatchesToInfoDictionary(appBundlePath); + applyPatchesToInfoDictionary(appBundleToInstallPath); } if(sign) { - int signRet = signApp(appBundlePath); + int signRet = signApp(appBundleToInstallPath); if(signRet != 0) return signRet; } - loadMCMFramework(); + MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; + if(appContainer) + { + // App update + // Replace existing bundle with new version - BOOL existed; - NSError* mcmError; - MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError]; - if(!appContainer || mcmError) - { - NSLog(@"[installApp] failed to create app container for %@: %@", appId, mcmError); - return 170; - } + // Check if the existing app bundle is empty + NSURL* bundleContainerURL = appContainer.url; + NSURL* appBundleURL = findAppURLInBundleURL(bundleContainerURL); - if(existed) - { - NSLog(@"[installApp] got existing app container: %@", appContainer); - } - else - { - NSLog(@"[installApp] created app container: %@", appContainer); - } - - // check if the bundle is empty - BOOL isEmpty = YES; - NSArray* bundleItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appContainer.url.path error:nil]; - for(NSString* bundleItem in bundleItems) - { - if([bundleItem.pathExtension isEqualToString:@"app"]) + // Make sure the installed app is a TrollStore app or the container is empty (or the force flag is set) + NSURL* trollStoreMarkURL = [bundleContainerURL URLByAppendingPathComponent:@"_TrollStore"]; + if(!appBundleURL && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force) { - isEmpty = NO; - break; + NSLog(@"[installApp] already installed and not a TrollStore app... bailing out"); + return 171; } - } - NSLog(@"[installApp] container is empty? %d", isEmpty); - - // Make sure there isn't already an app store app installed with the same identifier - NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"]; - if(existed && !isEmpty && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force) - { - NSLog(@"[installApp] already installed and not a TrollStore app... bailing out"); - return 171; - } - - // Mark app as TrollStore app - BOOL marked = [[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil]; - if(!marked) - { - NSLog(@"[installApp] failed to mark %@ as TrollStore app", appId); - return 177; - } - - fixPermissionsOfAppBundle(appBundlePath); - - // Wipe old version if needed - if(existed) - { - if(![appId isEqualToString:@"com.opa334.TrollStore"]) + // Terminate app if it's still running + if(!isTSUpdate) { BKSTerminateApplicationForReasonAndReportWithDescription(appId, 5, false, @"TrollStore - App updated"); } - NSLog(@"[installApp] found existing TrollStore app, cleaning directory"); - NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:appContainer.url includingPropertiesForKeys:nil options:0 errorHandler:nil]; - NSURL* fileURL; - while(fileURL = [enumerator nextObject]) + NSLog(@"[installApp] replacing existing app with new version"); + + // Delete existing .app directory if it exists + if(appBundleURL) { - // do not under any circumstance delete this file as it makes iOS loose the app registration - if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"] || [fileURL.lastPathComponent isEqualToString:@"_TrollStore"]) - { - NSLog(@"[installApp] skipping removal of %@", fileURL); - continue; - } - - [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil]; + [[NSFileManager defaultManager] removeItemAtURL:appBundleURL error:nil]; } - } - // Install app - NSString* newAppBundlePath = [appContainer.url.path stringByAppendingPathComponent:appBundlePath.lastPathComponent]; - NSLog(@"[installApp] new app path: %@", newAppBundlePath); - - NSError* copyError; - BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundlePath toPath:newAppBundlePath error:©Error]; - if(suc) - { - NSLog(@"[installApp] App %@ installed, adding to icon cache now...", appId); - registerPath(newAppBundlePath, NO, YES); - return 0; + NSString* newAppBundlePath = [bundleContainerURL.path stringByAppendingPathComponent:appBundleToInstallPath.lastPathComponent]; + NSLog(@"[installApp] new app path: %@", newAppBundlePath); + + // Install new version into existing app bundle + NSError* copyError; + BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundleToInstallPath toPath:newAppBundlePath error:©Error]; + if(!suc) + { + NSLog(@"[installApp] Error copying new version during update: %@", copyError); + return 178; + } } else { - NSLog(@"[installApp] Failed to copy app bundle for app %@, error: %@", appId, copyError); - return 178; + // Initial app install + BOOL systemMethodSuccessful = NO; + if(useInstalldMethod) + { + // System method + // Do initial placeholder installation using LSApplicationWorkspace + NSLog(@"[installApp] doing placeholder installation using LSApplicationWorkspace"); + + NSError* installError; + @try + { + systemMethodSuccessful = [[LSApplicationWorkspace defaultWorkspace] installApplication:[NSURL fileURLWithPath:appPackagePath] withOptions:@{ + LSInstallTypeKey : @1, + @"PackageType" : @"Placeholder" + } error:&installError]; + } + @catch(NSException* e) + { + NSLog(@"[installApp] encountered expection %@ while trying to do placeholder install", e); + systemMethodSuccessful = NO; + } + + if(!systemMethodSuccessful) + { + NSLog(@"[installApp] encountered error %@ while trying to do placeholder install", installError); + } + } + + if(!systemMethodSuccessful) + { + // Custom method + // Manually create app bundle via MCM apis and move app there + NSLog(@"[installApp] doing custom installation using MCMAppContainer"); + + NSError* mcmError; + appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:nil error:&mcmError]; + + if(!appContainer || mcmError) + { + NSLog(@"[installApp] failed to create app container for %@: %@", appId, mcmError); + return 170; + } + else + { + NSLog(@"[installApp] created app container: %@", appContainer); + } + + NSString* newAppBundlePath = [appContainer.url.path stringByAppendingPathComponent:appBundleToInstallPath.lastPathComponent]; + NSLog(@"[installApp] new app path: %@", newAppBundlePath); + + NSError* copyError; + BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundleToInstallPath toPath:newAppBundlePath error:©Error]; + if(!suc) + { + + NSLog(@"[installApp] Failed to copy app bundle for app %@, error: %@", appId, copyError); + return 178; + } + } } + + appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; + + // Mark app as TrollStore app + NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"]; + if(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreMarkURL.path]) + { + NSError* creationError; + NSData* emptyData = [NSData data]; + BOOL marked = [emptyData writeToURL:trollStoreMarkURL options:0 error:&creationError]; + if(!marked) + { + NSLog(@"[installApp] failed to mark %@ as TrollStore app by creating %@, error: %@", appId, trollStoreMarkURL.path, creationError); + return 177; + } + } + + // At this point the (new version of the) app is installed but still needs to be registered + // Also permissions need to be fixed + NSURL* updatedAppURL = findAppURLInBundleURL(appContainer.url); + fixPermissionsOfAppBundle(updatedAppURL.path); + registerPath(updatedAppURL.path, 0, YES); + return 0; } -int uninstallApp(NSString* appPath, NSString* appId) +int uninstallApp(NSString* appPath, NSString* appId, BOOL useCustomMethod) { BOOL deleteSuc = NO; if(!appId && appPath) @@ -760,7 +800,21 @@ int uninstallApp(NSString* appPath, NSString* appId) } } - deleteSuc = [[LSApplicationWorkspace defaultWorkspace] uninstallApplication:appId withOptions:nil]; + BOOL systemMethodSuccessful = NO; + if(!useCustomMethod) + { + systemMethodSuccessful = [[LSApplicationWorkspace defaultWorkspace] uninstallApplication:appId withOptions:nil]; + } + + if(!systemMethodSuccessful) + { + deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil]; + registerPath(appPath, YES, YES); + } + else + { + deleteSuc = systemMethodSuccessful; + } } if(deleteSuc) @@ -774,7 +828,7 @@ int uninstallApp(NSString* appPath, NSString* appId) } } -int uninstallAppByPath(NSString* appPath) +int uninstallAppByPath(NSString* appPath, BOOL useCustomMethod) { if(!appPath) return 1; @@ -786,21 +840,20 @@ int uninstallAppByPath(NSString* appPath) } NSString* appId = appIdForAppPath(standardizedAppPath); - return uninstallApp(appPath, appId); + return uninstallApp(appPath, appId, useCustomMethod); } -int uninstallAppById(NSString* appId) +int uninstallAppById(NSString* appId, BOOL useCustomMethod) { if(!appId) return 1; NSString* appPath = appPathForAppId(appId); if(!appPath) return 1; - return uninstallApp(appPath, appId); + return uninstallApp(appPath, appId, useCustomMethod); } // 166: IPA does not exist or is not accessible // 167: IPA does not appear to contain an app - -int installIpa(NSString* ipaPath, BOOL force) +int installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod) { cleanRestrictions(); @@ -819,18 +872,18 @@ int installIpa(NSString* ipaPath, BOOL force) return 168; } - int ret = installApp(tmpPackagePath, YES, force, NO); + int ret = installApp(tmpPackagePath, YES, force, NO, useInstalldMethod); [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; return ret; } -void uninstallAllApps(void) +void uninstallAllApps(BOOL useCustomMethod) { for(NSString* appPath in trollStoreInstalledAppBundlePaths()) { - uninstallAppById(appIdForAppPath(appPath)); + uninstallAppById(appIdForAppPath(appPath), useCustomMethod); } } @@ -872,14 +925,36 @@ int installTrollStore(NSString* pathToTar) NSString* tmpTrollStorePath = [tmpPayloadPath stringByAppendingPathComponent:@"TrollStore.app"]; if(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStorePath]) return 1; - // Save existing ldid installation if it exists - NSString* existingLdidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"]; - if([[NSFileManager defaultManager] fileExistsAtPath:existingLdidPath]) + // Transfer existing ldid installation if it exists + // But only if the to-be-installed version of TrollStore is 1.5.0 or above + // This is to make it possible to downgrade to older versions still + + NSString* toInstallInfoPlistPath = [tmpTrollStorePath stringByAppendingPathComponent:@"Info.plist"]; + if(![[NSFileManager defaultManager] fileExistsAtPath:toInstallInfoPlistPath]) return 1; + + NSDictionary* toInstallInfoDict = [NSDictionary dictionaryWithContentsOfFile:toInstallInfoPlistPath]; + NSString* toInstallVersion = toInstallInfoDict[@"CFBundleVersion"]; + + NSComparisonResult result = [@"1.5.0" compare:toInstallVersion options:NSNumericSearch]; + if(result != NSOrderedDescending) { - NSString* tmpLdidPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid"]; - if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidPath]) + NSString* existingLdidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"]; + NSString* existingLdidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid.version"]; + if([[NSFileManager defaultManager] fileExistsAtPath:existingLdidPath]) { - [[NSFileManager defaultManager] copyItemAtPath:existingLdidPath toPath:tmpLdidPath error:nil]; + NSString* tmpLdidPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid"]; + if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidPath]) + { + [[NSFileManager defaultManager] copyItemAtPath:existingLdidPath toPath:tmpLdidPath error:nil]; + } + } + if([[NSFileManager defaultManager] fileExistsAtPath:existingLdidVersionPath]) + { + NSString* tmpLdidVersionPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid.version"]; + if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidVersionPath]) + { + [[NSFileManager defaultManager] copyItemAtPath:existingLdidVersionPath toPath:tmpLdidVersionPath error:nil]; + } } } @@ -898,7 +973,7 @@ int installTrollStore(NSString* pathToTar) _installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper); } - int ret = installApp(tmpPackagePath, NO, YES, YES); + int ret = installApp(tmpPackagePath, NO, YES, YES, YES); NSLog(@"[installTrollStore] installApp => %d", ret); [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; return ret; @@ -1106,90 +1181,114 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) @autoreleasepool { if(argc <= 1) return -1; - NSLog(@"trollstore helper go, uid: %d, gid: %d", getuid(), getgid()); + if(getuid() != 0) + { + NSLog(@"ERROR: trollstorehelper has to be run as root."); + return -1; + } + + NSMutableArray* args = [NSMutableArray new]; + for (int i = 1; i < argc; i++) + { + [args addObject:[NSString stringWithUTF8String:argv[i]]]; + } + + NSLog(@"trollstorehelper invoked with arguments: %@", args); int ret = 0; - - NSString* cmd = [NSString stringWithUTF8String:argv[1]]; + NSString* cmd = args.firstObject; if([cmd isEqualToString:@"install"]) { - BOOL force = NO; - if(argc <= 2) return -3; - if(argc > 3) - { - if(!strcmp(argv[3], "force")) - { - force = YES; - } - } - NSString* ipaPath = [NSString stringWithUTF8String:argv[2]]; - ret = installIpa(ipaPath, force); - } else if([cmd isEqualToString:@"uninstall"]) + if(args.count < 2) return -3; + // use system method when specified, otherwise use custom method + BOOL useInstalldMethod = [args containsObject:@"installd"]; + BOOL force = [args containsObject:@"force"]; + NSString* ipaPath = args.lastObject; + ret = installIpa(ipaPath, force, useInstalldMethod); + } + else if([cmd isEqualToString:@"uninstall"]) { - if(argc <= 2) return -3; - NSString* appId = [NSString stringWithUTF8String:argv[2]]; - ret = uninstallAppById(appId); - } else if([cmd isEqualToString:@"uninstall-path"]) + if(args.count < 2) return -3; + // use custom method when specified, otherwise use system method + BOOL useCustomMethod = [args containsObject:@"custom"]; + NSString* appId = args.lastObject; + ret = uninstallAppById(appId, useCustomMethod); + } + else if([cmd isEqualToString:@"uninstall-path"]) { - if(argc <= 2) return -3; - NSString* appPath = [NSString stringWithUTF8String:argv[2]]; - ret = uninstallAppByPath(appPath); - }else if([cmd isEqualToString:@"install-trollstore"]) + if(args.count < 2) return -3; + // use custom method when specified, otherwise use system method + BOOL useCustomMethod = [args containsObject:@"custom"]; + NSString* appPath = args.lastObject; + ret = uninstallAppByPath(appPath, useCustomMethod); + } + else if([cmd isEqualToString:@"install-trollstore"]) { - if(argc <= 2) return -3; - NSString* tsTar = [NSString stringWithUTF8String:argv[2]]; + if(args.count < 2) return -3; + NSString* tsTar = args.lastObject; ret = installTrollStore(tsTar); NSLog(@"installed troll store? %d", ret==0); - } else if([cmd isEqualToString:@"uninstall-trollstore"]) + } + else if([cmd isEqualToString:@"uninstall-trollstore"]) { - uninstallAllApps(); + if(![args containsObject:@"preserve-apps"]) + { + uninstallAllApps([args containsObject:@"custom"]); + } uninstallTrollStore(YES); - } else if([cmd isEqualToString:@"uninstall-trollstore-preserve-apps"]) + } + else if([cmd isEqualToString:@"install-ldid"]) { - uninstallTrollStore(YES); - }else if([cmd isEqualToString:@"install-ldid"]) - { - if(argc <= 2) return -3; - NSString* ldidPath = [NSString stringWithUTF8String:argv[2]]; - installLdid(ldidPath); - } else if([cmd isEqualToString:@"refresh"]) + if(args.count < 3) return -3; + NSString* ldidPath = args[1]; + NSString* ldidVersion = args[2]; + installLdid(ldidPath, ldidVersion); + } + else if([cmd isEqualToString:@"refresh"]) { refreshAppRegistrations(YES); - } else if([cmd isEqualToString:@"refresh-all"]) + } + else if([cmd isEqualToString:@"refresh-all"]) { cleanRestrictions(); //refreshAppRegistrations(NO); // <- fixes app permissions resetting, causes apps to move around on home screen, so I had to disable it + [[NSFileManager defaultManager] removeItemAtPath:@"/var/containers/Shared/SystemGroup/systemgroup.com.apple.lsd.iconscache/Library/Caches/com.apple.IconsCache" error:nil]; [[LSApplicationWorkspace defaultWorkspace] _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:YES]; refreshAppRegistrations(YES); killall(@"backboardd", YES); - } else if([cmd isEqualToString:@"install-persistence-helper"]) + } + else if([cmd isEqualToString:@"install-persistence-helper"]) { - if(argc <= 2) return -3; - NSString* systemAppId = [NSString stringWithUTF8String:argv[2]]; + if(args.count < 2) return -3; + NSString* systemAppId = args.lastObject; installPersistenceHelper(systemAppId); - } else if([cmd isEqualToString:@"uninstall-persistence-helper"]) + } + else if([cmd isEqualToString:@"uninstall-persistence-helper"]) { uninstallPersistenceHelper(); - } else if([cmd isEqualToString:@"register-user-persistence-helper"]) + } + else if([cmd isEqualToString:@"register-user-persistence-helper"]) { - if(argc <= 2) return -3; - NSString* userAppId = [NSString stringWithUTF8String:argv[2]]; + if(args.count < 2) return -3; + NSString* userAppId = args.lastObject; registerUserPersistenceHelper(userAppId); - } else if([cmd isEqualToString:@"modify-registration"]) + } + else if([cmd isEqualToString:@"modify-registration"]) { - if(argc <= 3) return -3; - NSString* appPath = [NSString stringWithUTF8String:argv[2]]; - NSString* newRegistration = [NSString stringWithUTF8String:argv[3]]; + if(args.count < 3) return -3; + NSString* appPath = args[1]; + NSString* newRegistration = args[2]; NSString* trollStoreMark = [[appPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"_TrollStore"]; if([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark]) { registerPath(appPath, NO, [newRegistration isEqualToString:@"System"]); } - } else if([cmd isEqualToString:@"url-scheme"]) + } + else if([cmd isEqualToString:@"url-scheme"]) { - if(argc <= 2) return -3; - NSString* modifyArg = [NSString stringWithUTF8String:argv[2]]; + if(args.count < 2) return -3; + NSString* modifyArg = args.lastObject; BOOL newState = [modifyArg isEqualToString:@"enable"]; if(newState == YES || [modifyArg isEqualToString:@"disable"]) { @@ -1197,8 +1296,7 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) } } - NSLog(@"returning %d", ret); - + NSLog(@"trollstorehelper returning %d", ret); return ret; } } diff --git a/Shared/TSListControllerShared.h b/Shared/TSListControllerShared.h index cb9612d..f87220f 100644 --- a/Shared/TSListControllerShared.h +++ b/Shared/TSListControllerShared.h @@ -12,5 +12,6 @@ - (void)refreshAppRegistrationsPressed; - (void)uninstallPersistenceHelperPressed; - (void)handleUninstallation; +- (NSMutableArray*)argsForUninstallingTrollStore; - (void)uninstallTrollStorePressed; @end \ No newline at end of file diff --git a/Shared/TSListControllerShared.m b/Shared/TSListControllerShared.m index f57a1be..b6d827e 100644 --- a/Shared/TSListControllerShared.m +++ b/Shared/TSListControllerShared.m @@ -188,20 +188,28 @@ } } +- (NSMutableArray*)argsForUninstallingTrollStore +{ + return @[@"uninstall-trollstore"].mutableCopy; +} + - (void)uninstallTrollStorePressed { UIAlertController* uninstallAlert = [UIAlertController alertControllerWithTitle:@"Uninstall" message:@"You are about to uninstall TrollStore, do you want to preserve the apps installed by it?" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* uninstallAllAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Uninstall Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { - spawnRoot(rootHelperPath(), @[@"uninstall-trollstore"], nil, nil); + NSMutableArray* args = [self argsForUninstallingTrollStore]; + spawnRoot(rootHelperPath(), @[args], nil, nil); [self handleUninstallation]; }]; [uninstallAlert addAction:uninstallAllAction]; UIAlertAction* preserveAppsAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Preserve Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { - spawnRoot(rootHelperPath(), @[@"uninstall-trollstore-preserve-apps"], nil, nil); + NSMutableArray* args = [self argsForUninstallingTrollStore]; + [args addObject:@"preserve-apps"]; + spawnRoot(rootHelperPath(), args, nil, nil); [self handleUninstallation]; }]; [uninstallAlert addAction:preserveAppsAction]; diff --git a/Shared/TSUtil.h b/Shared/TSUtil.h index 38dede7..5871fef 100644 --- a/Shared/TSUtil.h +++ b/Shared/TSUtil.h @@ -13,6 +13,7 @@ extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString* extern void killall(NSString* processName, BOOL softly); extern void respring(void); extern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)); +extern void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion)); extern NSArray* trollStoreInstalledAppBundlePaths(); extern NSArray* trollStoreInstalledAppContainerPaths(); diff --git a/Shared/TSUtil.m b/Shared/TSUtil.m index 5c38262..5b116ca 100644 --- a/Shared/TSUtil.m +++ b/Shared/TSUtil.m @@ -287,9 +287,10 @@ void respring(void) exit(0); } -void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)) +void github_fetchLatestVersion(NSString* repo, void (^completionHandler)(NSString* latestVersion)) { - NSURL* githubLatestAPIURL = [NSURL URLWithString:@"https://api.github.com/repos/opa334/TrollStore/releases/latest"]; + NSString* urlString = [NSString stringWithFormat:@"https://api.github.com/repos/%@/releases/latest", repo]; + NSURL* githubLatestAPIURL = [NSURL URLWithString:urlString]; NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:githubLatestAPIURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { @@ -311,6 +312,16 @@ void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVers [task resume]; } +void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)) +{ + github_fetchLatestVersion(@"opa334/TrollStore", completionHandler); +} + +void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion)) +{ + github_fetchLatestVersion(@"opa334/ldid", completionHandler); +} + NSArray* trollStoreInstalledAppContainerPaths() { NSMutableArray* appContainerPaths = [NSMutableArray new]; diff --git a/TrollStore/TSApplicationsManager.m b/TrollStore/TSApplicationsManager.m index 14206b6..20d4357 100644 --- a/TrollStore/TSApplicationsManager.m +++ b/TrollStore/TSApplicationsManager.m @@ -1,5 +1,6 @@ #import "TSApplicationsManager.h" #import +extern NSUserDefaults* trollStoreUserDefaults(); @implementation TSApplicationsManager @@ -75,15 +76,25 @@ - (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut { - int ret; + NSMutableArray* args = [NSMutableArray new]; + [args addObject:@"install"]; if(force) { - ret = spawnRoot(rootHelperPath(), @[@"install", pathToIpa, @"force"], nil, logOut); + [args addObject:@"force"]; + } + NSNumber* installationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"installationMethod"]; + int installationMethodToUse = installationMethodToUseNum ? installationMethodToUseNum.intValue : 1; + if(installationMethodToUse == 1) + { + [args addObject:@"custom"]; } else { - ret = spawnRoot(rootHelperPath(), @[@"install", pathToIpa], nil, logOut); + [args addObject:@"installd"]; } + [args addObject:pathToIpa]; + + int ret = spawnRoot(rootHelperPath(), args, nil, logOut); [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; return ret; } @@ -96,7 +107,24 @@ - (int)uninstallApp:(NSString*)appId { if(!appId) return -200; - int ret = spawnRoot(rootHelperPath(), @[@"uninstall", appId], nil, nil); + + NSMutableArray* args = [NSMutableArray new]; + [args addObject:@"uninstall"]; + + NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"uninstallationMethod"]; + int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0; + if(uninstallationMethodToUse == 1) + { + [args addObject:@"custom"]; + } + else + { + [args addObject:@"installd"]; + } + + [args addObject:appId]; + + int ret = spawnRoot(rootHelperPath(), args, nil, nil); [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; return ret; } @@ -104,7 +132,24 @@ - (int)uninstallAppByPath:(NSString*)path { if(!path) return -200; - int ret = spawnRoot(rootHelperPath(), @[@"uninstall-path", path], nil, nil); + + NSMutableArray* args = [NSMutableArray new]; + [args addObject:@"uninstall-path"]; + + NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"uninstallationMethod"]; + int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0; + if(uninstallationMethodToUse == 1) + { + [args addObject:@"custom"]; + } + else + { + [args addObject:@"installd"]; + } + + [args addObject:path]; + + int ret = spawnRoot(rootHelperPath(), args, nil, nil); [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; return ret; } diff --git a/TrollStore/TSInstallationController.h b/TrollStore/TSInstallationController.h index 96ebdc2..0c64233 100644 --- a/TrollStore/TSInstallationController.h +++ b/TrollStore/TSInstallationController.h @@ -9,4 +9,6 @@ + (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completion; ++ (void)installLdid; + @end \ No newline at end of file diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index 9fedb61..dee437f 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -187,4 +187,47 @@ extern NSUserDefaults* trollStoreUserDefaults(void); }); } ++ (void)installLdid +{ + fetchLatestLdidVersion(^(NSString* latestVersion) + { + dispatch_async(dispatch_get_main_queue(), ^ + { + NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/latest/download/ldid"]; + NSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL]; + + [TSPresentationDelegate startActivity:@"Installing ldid"]; + + NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) + { + if(error) + { + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading ldid: %@", error] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; + [errorAlert addAction:closeAction]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [TSPresentationDelegate stopActivityWithCompletion:^ + { + [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; + }]; + }); + } + else + { + spawnRoot(rootHelperPath(), @[@"install-ldid", location.path, latestVersion], nil, nil); + dispatch_async(dispatch_get_main_queue(), ^ + { + [TSPresentationDelegate stopActivityWithCompletion:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"TrollStoreReloadSettingsNotification" object:nil userInfo:nil]; + }); + } + }]; + + [downloadTask resume]; + }); + }); +} + @end \ No newline at end of file diff --git a/TrollStore/TSSceneDelegate.m b/TrollStore/TSSceneDelegate.m index 8550dfe..4a6fa91 100644 --- a/TrollStore/TSSceneDelegate.m +++ b/TrollStore/TSSceneDelegate.m @@ -72,6 +72,20 @@ } } +// We want to auto install ldid if either it doesn't exist +// or if it's the one from an old TrollStore version that's no longer supported +- (void)handleLdidCheck +{ + NSString* tsAppPath = [NSBundle mainBundle].bundlePath; + + NSString* ldidPath = [tsAppPath stringByAppendingPathComponent:@"ldid"]; + NSString* ldidVersionPath = [tsAppPath stringByAppendingPathComponent:@"ldid.version"]; + + if(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath] || ![[NSFileManager defaultManager] fileExistsAtPath:ldidVersionPath]) + { + [TSInstallationController installLdid]; + } +} - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. @@ -88,6 +102,10 @@ { [self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene]; } + else + { + [self handleLdidCheck]; + } } diff --git a/TrollStore/TSSettingsAdvancedListController.h b/TrollStore/TSSettingsAdvancedListController.h new file mode 100644 index 0000000..0721751 --- /dev/null +++ b/TrollStore/TSSettingsAdvancedListController.h @@ -0,0 +1,5 @@ +#import + +@interface TSSettingsAdvancedListController : PSListController + +@end \ No newline at end of file diff --git a/TrollStore/TSSettingsAdvancedListController.m b/TrollStore/TSSettingsAdvancedListController.m new file mode 100644 index 0000000..97bc582 --- /dev/null +++ b/TrollStore/TSSettingsAdvancedListController.m @@ -0,0 +1,103 @@ +#import "TSSettingsAdvancedListController.h" +#import + +extern NSUserDefaults* trollStoreUserDefaults(); +@interface PSSpecifier () +@property (nonatomic,retain) NSArray* values; +@end + +@implementation TSSettingsAdvancedListController + +- (NSMutableArray*)specifiers +{ + if(!_specifiers) + { + _specifiers = [NSMutableArray new]; + + PSSpecifier* installationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; + //installationMethodGroupSpecifier.name = @"Installation"; + [installationMethodGroupSpecifier setProperty:@"installd:\nInstalls applications by doing a placeholder installation through installd, fixing the permissions and then adding it to icon cache.\nAdvantage: Might be slightly more persistent then the custom method in terms of icon cache reloads.\nDisadvantage: Causes some small issues with certain applications for seemingly no reason (E.g. Watusi cannot save preferences when being installed using this method).\n\nCustom (Recommended):\nInstalls applications by manually creating a bundle using MobileContainerManager, copying the app into it and adding it to icon cache.\nAdvantage: No known issues (As opposed to the Watusi issue outlined in the installd method).\nDisadvantage: Might be slightly less persistent then the installd method in terms of icon cache reloads.\n\nNOTE: In cases where installd is selected but the placeholder installation fails, TrollStore automatically falls back to using the Custom method." forKey:@"footerText"]; + [_specifiers addObject:installationMethodGroupSpecifier]; + + PSSpecifier* installationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method" + target:self + set:nil + get:nil + detail:nil + cell:PSStaticTextCell + edit:nil]; + [installationMethodSpecifier setProperty:@YES forKey:@"enabled"]; + installationMethodSpecifier.identifier = @"installationMethodLabel"; + [_specifiers addObject:installationMethodSpecifier]; + + PSSpecifier* installationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method Segment" + target:self + set:@selector(setPreferenceValue:specifier:) + get:@selector(readPreferenceValue:) + detail:nil + cell:PSSegmentCell + edit:nil]; + [installationMethodSegmentSpecifier setProperty:@YES forKey:@"enabled"]; + installationMethodSegmentSpecifier.identifier = @"installationMethodSegment"; + [installationMethodSegmentSpecifier setProperty:@"com.opa334.TrollStore" forKey:@"defaults"]; + [installationMethodSegmentSpecifier setProperty:@"installationMethod" forKey:@"key"]; + installationMethodSegmentSpecifier.values = @[@0, @1]; + installationMethodSegmentSpecifier.titleDictionary = @{@0 : @"installd", @1 : @"Custom"}; + [installationMethodSegmentSpecifier setProperty:@1 forKey:@"default"]; + [_specifiers addObject:installationMethodSegmentSpecifier]; + + PSSpecifier* uninstallationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; + //uninstallationMethodGroupSpecifier.name = @"Uninstallation"; + [uninstallationMethodGroupSpecifier setProperty:@"installd (Recommended):\nUninstalls applications using the same API that SpringBoard uses when uninstalling them from the home screen.\n\nCustom:\nUninstalls applications by removing them from icon cache and then deleting their application and data bundles directly.\n\nNOTE: In cases where installd is selected but the stock uninstallation fails, TrollStore automatically falls back to using the Custom method." forKey:@"footerText"]; + [_specifiers addObject:uninstallationMethodGroupSpecifier]; + + PSSpecifier* uninstallationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstallation Method" + target:self + set:nil + get:nil + detail:nil + cell:PSStaticTextCell + edit:nil]; + [uninstallationMethodSpecifier setProperty:@YES forKey:@"enabled"]; + uninstallationMethodSpecifier.identifier = @"uninstallationMethodLabel"; + [_specifiers addObject:uninstallationMethodSpecifier]; + + PSSpecifier* uninstallationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method Segment" + target:self + set:@selector(setPreferenceValue:specifier:) + get:@selector(readPreferenceValue:) + detail:nil + cell:PSSegmentCell + edit:nil]; + [uninstallationMethodSegmentSpecifier setProperty:@YES forKey:@"enabled"]; + uninstallationMethodSegmentSpecifier.identifier = @"uninstallationMethodSegment"; + [uninstallationMethodSegmentSpecifier setProperty:@"com.opa334.TrollStore" forKey:@"defaults"]; + [uninstallationMethodSegmentSpecifier setProperty:@"uninstallationMethod" forKey:@"key"]; + uninstallationMethodSegmentSpecifier.values = @[@0, @1]; + uninstallationMethodSegmentSpecifier.titleDictionary = @{@0 : @"installd", @1 : @"Custom"}; + [uninstallationMethodSegmentSpecifier setProperty:@0 forKey:@"default"]; + [_specifiers addObject:uninstallationMethodSegmentSpecifier]; + } + + [(UINavigationItem *)self.navigationItem setTitle:@"Advanced"]; + return _specifiers; +} + +- (void)setPreferenceValue:(NSObject*)value specifier:(PSSpecifier*)specifier +{ + NSUserDefaults* tsDefaults = trollStoreUserDefaults(); + [tsDefaults setObject:value forKey:[specifier propertyForKey:@"key"]]; +} + +- (NSObject*)readPreferenceValue:(PSSpecifier*)specifier +{ + NSUserDefaults* tsDefaults = trollStoreUserDefaults(); + NSObject* toReturn = [tsDefaults objectForKey:[specifier propertyForKey:@"key"]]; + if(!toReturn) + { + toReturn = [specifier propertyForKey:@"default"]; + } + return toReturn; +} + +@end \ No newline at end of file diff --git a/TrollStore/TSSettingsListController.h b/TrollStore/TSSettingsListController.h index 959d405..fc573ee 100644 --- a/TrollStore/TSSettingsListController.h +++ b/TrollStore/TSSettingsListController.h @@ -4,5 +4,6 @@ { PSSpecifier* _installPersistenceHelperSpecifier; NSString* _newerVersion; + NSString* _newerLdidVersion; } @end \ No newline at end of file diff --git a/TrollStore/TSSettingsListController.m b/TrollStore/TSSettingsListController.m index 6782f9f..8d2bec1 100644 --- a/TrollStore/TSSettingsListController.m +++ b/TrollStore/TSSettingsListController.m @@ -3,6 +3,8 @@ #import #import #import +#import "TSInstallationController.h" +#import "TSSettingsAdvancedListController.h" @interface NSUserDefaults (Private) - (instancetype)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container; @@ -15,6 +17,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:@"TrollStoreReloadSettingsNotification" object:nil]; fetchLatestTrollStoreVersion(^(NSString* latestVersion) { @@ -29,6 +32,26 @@ extern NSUserDefaults* trollStoreUserDefaults(void); }); } }); + + fetchLatestLdidVersion(^(NSString* latestVersion) + { + NSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid.version"]; + NSString* ldidVersion = nil; + NSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath]; + if(ldidVersionData) + { + ldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding]; + } + + if(![latestVersion isEqualToString:ldidVersion]) + { + _newerLdidVersion = latestVersion; + dispatch_async(dispatch_get_main_queue(), ^ + { + [self reloadSpecifiers]; + }); + } + }); } - (NSMutableArray*)specifiers @@ -88,8 +111,16 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [_specifiers addObject:rebuildIconCacheSpecifier]; NSString* ldidPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid"]; + NSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid.version"]; BOOL ldidInstalled = [[NSFileManager defaultManager] fileExistsAtPath:ldidPath]; + NSString* ldidVersion = nil; + NSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath]; + if(ldidVersionData) + { + ldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding]; + } + PSSpecifier* signingGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; signingGroupSpecifier.name = @"Signing"; @@ -106,7 +137,13 @@ extern NSUserDefaults* trollStoreUserDefaults(void); if(ldidInstalled) { - PSSpecifier* ldidInstalledSpecifier = [PSSpecifier preferenceSpecifierNamed:@"ldid: Installed" + NSString* installedTitle = @"ldid: Installed"; + if(ldidVersion) + { + installedTitle = [NSString stringWithFormat:@"%@ (%@)", installedTitle, ldidVersion]; + } + + PSSpecifier* ldidInstalledSpecifier = [PSSpecifier preferenceSpecifierNamed:installedTitle target:self set:nil get:nil @@ -116,6 +153,22 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [ldidInstalledSpecifier setProperty:@NO forKey:@"enabled"]; ldidInstalledSpecifier.identifier = @"ldidInstalled"; [_specifiers addObject:ldidInstalledSpecifier]; + + if(_newerLdidVersion && ![_newerLdidVersion isEqualToString:ldidVersion]) + { + NSString* updateTitle = [NSString stringWithFormat:@"Update to %@", _newerLdidVersion]; + PSSpecifier* ldidUpdateSpecifier = [PSSpecifier preferenceSpecifierNamed:updateTitle + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + ldidUpdateSpecifier.identifier = @"updateLdid"; + [ldidUpdateSpecifier setProperty:@YES forKey:@"enabled"]; + ldidUpdateSpecifier.buttonAction = @selector(installOrUpdateLdidPressed); + [_specifiers addObject:ldidUpdateSpecifier]; + } } else { @@ -126,9 +179,9 @@ extern NSUserDefaults* trollStoreUserDefaults(void); detail:nil cell:PSButtonCell edit:nil]; - installLdidSpecifier.identifier = @"ldidInstalled"; + installLdidSpecifier.identifier = @"installLdid"; [installLdidSpecifier setProperty:@YES forKey:@"enabled"]; - installLdidSpecifier.buttonAction = @selector(installLdidPressed); + installLdidSpecifier.buttonAction = @selector(installOrUpdateLdidPressed); [_specifiers addObject:installLdidSpecifier]; } @@ -234,11 +287,21 @@ extern NSUserDefaults* trollStoreUserDefaults(void); [_specifiers addObject:installAlertConfigurationSpecifier]; - PSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; - [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@lunotech11, @SerenaKit, @tylinux: Various contributions\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", [self getTrollStoreVersion]] forKey:@"footerText"]; + [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nTrollStore is NOT for piracy!\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@lunotech11, @SerenaKit, @tylinux: Various contributions\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", [self getTrollStoreVersion]] forKey:@"footerText"]; [_specifiers addObject:otherGroupSpecifier]; + PSSpecifier* advancedLinkSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Advanced" + target:self + set:nil + get:nil + detail:nil + cell:PSLinkListCell + edit:nil]; + advancedLinkSpecifier.detailControllerClass = [TSSettingsAdvancedListController class]; + [advancedLinkSpecifier setProperty:@YES forKey:@"enabled"]; + [_specifiers addObject:advancedLinkSpecifier]; + // Uninstall TrollStore PSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall TrollStore" target:self @@ -277,7 +340,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void); - (NSArray*)installationConfirmationNames { - return @[@"Always (Recommended)", @"Only on Remote Installs", @"Never (Not Recommeded)"]; + return @[@"Always (Recommended)", @"Only on Remote URL Installs", @"Never (Not Recommeded)"]; } - (void)respringButtonPressed @@ -285,41 +348,9 @@ extern NSUserDefaults* trollStoreUserDefaults(void); respring(); } -- (void)installLdidPressed +- (void)installOrUpdateLdidPressed { - NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/download/v2.1.5-procursus5/ldid"]; - NSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL]; - - [TSPresentationDelegate startActivity:@"Installing ldid"]; - - NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) - { - if(error) - { - UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading ldid: %@", error] preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; - [errorAlert addAction:closeAction]; - - dispatch_async(dispatch_get_main_queue(), ^ - { - [TSPresentationDelegate stopActivityWithCompletion:^ - { - [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; - }]; - }); - } - else - { - spawnRoot(rootHelperPath(), @[@"install-ldid", location.path], nil, nil); - dispatch_async(dispatch_get_main_queue(), ^ - { - [TSPresentationDelegate stopActivityWithCompletion:nil]; - [self reloadSpecifiers]; - }); - } - }]; - - [downloadTask resume]; + [TSInstallationController installLdid]; } - (void)installPersistenceHelperPressed @@ -410,4 +441,18 @@ extern NSUserDefaults* trollStoreUserDefaults(void); return toReturn; } +- (NSMutableArray*)argsForUninstallingTrollStore +{ + NSMutableArray* args = @[@"uninstall-trollstore"].mutableCopy; + + NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"uninstallationMethod"]; + int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0; + if(uninstallationMethodToUse == 1) + { + [args addObject:@"custom"]; + } + + return args; +} + @end \ No newline at end of file diff --git a/cert.p12 b/cert.p12 index 378b83b..a614168 100644 Binary files a/cert.p12 and b/cert.p12 differ diff --git a/cert_new.p12 b/cert_new.p12 deleted file mode 100644 index a614168..0000000 Binary files a/cert_new.p12 and /dev/null differ