diff --git a/RootHelper/control b/RootHelper/control index 1a94d3d..dc13dd4 100644 --- a/RootHelper/control +++ b/RootHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstoreroothelper Name: trollstoreroothelper -Version: 1.3.5 +Version: 1.4 Architecture: iphoneos-arm Description: An awesome tool of some sort!! Maintainer: opa334 diff --git a/RootHelper/entitlements.plist b/RootHelper/entitlements.plist index 1071ba4..70c45bc 100644 --- a/RootHelper/entitlements.plist +++ b/RootHelper/entitlements.plist @@ -1,3 +1,4 @@ + @@ -5,12 +6,12 @@ com.apple.private.security.container-required - com.apple.security.exception.files.absolute-path.read-write - - / - + com.apple.private.security.no-sandbox + com.apple.private.security.container-manager + com.apple.private.MobileContainerManager.allowed + com.apple.private.coreservices.canmaplsdatabase com.apple.lsapplicationworkspace.rebuildappdatabases @@ -19,7 +20,7 @@ com.apple.private.security.storage.MobileDocuments - com.apple.private.MobileContainerManager.allowed + com.apple.private.security.storage-exempt.heritable com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled @@ -27,9 +28,21 @@ com.apple.private.uninstall.deletion + com.apple.springboard.launchapplications + com.apple.backboardd.launchapplications + com.apple.frontboard.launchapplications + com.apple.multitasking.termination + com.apple.private.mobileinstall.allowedSPI + + InstallForLaunchServices + Install + UninstallForLaunchServices + Uninstall + UpdatePlaceholderMetadata + diff --git a/RootHelper/main.m b/RootHelper/main.m index 6d5ebef..403c866 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -55,7 +55,13 @@ NSSet* appleURLSchemes(void) LSApplicationProxy* proxy; while(proxy = [enumerator nextObject]) { - [systemURLSchemes unionSet:proxy.claimedURLSchemes]; + for(NSString* claimedURLScheme in proxy.claimedURLSchemes) + { + if([claimedURLScheme isKindOfClass:NSString.class]) + { + [systemURLSchemes addObject:claimedURLScheme.lowercaseString]; + } + } } return systemURLSchemes.copy; @@ -83,22 +89,26 @@ NSSet* immutableAppBundleIdentifiers(void) NSDictionary* infoDictionaryForAppPath(NSString* appPath) { + if(!appPath) return nil; NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"]; return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; } NSString* appIdForAppPath(NSString* appPath) { + if(!appPath) return nil; return infoDictionaryForAppPath(appPath)[@"CFBundleIdentifier"]; } NSString* appMainExecutablePathForAppPath(NSString* appPath) { + if(!appPath) return nil; return [appPath stringByAppendingPathComponent:infoDictionaryForAppPath(appPath)[@"CFBundleExecutable"]]; } NSString* appPathForAppId(NSString* appId) { + if(!appId) return nil; for(NSString* appPath in trollStoreInstalledAppBundlePaths()) { if([appIdForAppPath(appPath) isEqualToString:appId]) @@ -111,7 +121,7 @@ NSString* appPathForAppId(NSString* appId) BOOL isMachoFile(NSString* filePath) { - FILE* file = fopen(filePath.UTF8String, "r"); + FILE* file = fopen(filePath.fileSystemRepresentation, "r"); if(!file) return NO; fseek(file, 0, SEEK_SET); @@ -130,8 +140,8 @@ void fixPermissionsOfAppBundle(NSString* appBundlePath) while(fileURL = [enumerator nextObject]) { NSString* filePath = fileURL.path; - chown(filePath.UTF8String, 33, 33); - chmod(filePath.UTF8String, 0644); + chown(filePath.fileSystemRepresentation, 33, 33); + chmod(filePath.fileSystemRepresentation, 0644); } // Apply correct permissions (Second run, set executables and directories to 0755) @@ -145,15 +155,53 @@ void fixPermissionsOfAppBundle(NSString* appBundlePath) if(isDir || isMachoFile(filePath)) { - chmod(filePath.UTF8String, 0755); + chmod(filePath.fileSystemRepresentation, 0755); } } - - // Set .app directory permissions too - chmod(appBundlePath.UTF8String, 0755); - chown(appBundlePath.UTF8String, 33, 33); } +NSArray* TSURLScheme(void) +{ + return @[ + @{ + @"CFBundleURLName" : @"com.apple.Magnifier", + @"CFBundleURLSchemes" : @[ + @"apple-magnifier" + ] + } + ]; +} + +BOOL getTSURLSchemeState(NSString* customAppPath) +{ + NSString* pathToUse = customAppPath ?: trollStoreAppPath(); + + NSDictionary* trollStoreInfoDict = infoDictionaryForAppPath(pathToUse); + return (BOOL)trollStoreInfoDict[@"CFBundleURLTypes"]; +} + +void setTSURLSchemeState(BOOL newState, NSString* customAppPath) +{ + NSString* tsAppPath = trollStoreAppPath(); + NSString* pathToUse = customAppPath ?: tsAppPath; + if(newState != getTSURLSchemeState(pathToUse)) + { + NSDictionary* trollStoreInfoDict = infoDictionaryForAppPath(pathToUse); + NSMutableDictionary* trollStoreInfoDictM = trollStoreInfoDict.mutableCopy; + if(newState) + { + trollStoreInfoDictM[@"CFBundleURLTypes"] = TSURLScheme(); + } + else + { + [trollStoreInfoDictM removeObjectForKey:@"CFBundleURLTypes"]; + } + NSString* outPath = [pathToUse stringByAppendingPathComponent:@"Info.plist"]; + [trollStoreInfoDictM.copy writeToURL:[NSURL fileURLWithPath:outPath] error:nil]; + } +} + + void installLdid(NSString* ldidToCopyPath) { if(![[NSFileManager defaultManager] fileExistsAtPath:ldidToCopyPath]) return; @@ -166,8 +214,8 @@ void installLdid(NSString* ldidToCopyPath) [[NSFileManager defaultManager] copyItemAtPath:ldidToCopyPath toPath:ldidPath error:nil]; - chmod(ldidPath.UTF8String, 0755); - chown(ldidPath.UTF8String, 0, 0); + chmod(ldidPath.fileSystemRepresentation, 0755); + chown(ldidPath.fileSystemRepresentation, 0, 0); } BOOL isLdidInstalled(void) @@ -206,7 +254,7 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) pid_t task_pid; int status = -200; - int spawnError = posix_spawn(&task_pid, [ldidPath UTF8String], &action, NULL, (char* const*)argsC, NULL); + int spawnError = posix_spawn(&task_pid, [ldidPath fileSystemRepresentation], &action, NULL, (char* const*)argsC, NULL); for (NSUInteger i = 0; i < argCount; i++) { free(argsC[i]); @@ -500,7 +548,19 @@ void applyPatchesToInfoDictionary(NSString* appPath) if(URLSchemes) { NSMutableSet* URLSchemesSet = [NSMutableSet setWithArray:URLSchemes]; - [URLSchemesSet minusSet:appleSchemes]; + for(NSString* existingURLScheme in [URLSchemesSet copy]) + { + if(![existingURLScheme isKindOfClass:[NSString class]]) + { + [URLSchemesSet removeObject:existingURLScheme]; + continue; + } + + if([appleSchemes containsObject:existingURLScheme.lowercaseString]) + { + [URLSchemesSet removeObject:existingURLScheme]; + } + } modifiedURLType[@"CFBundleURLSchemes"] = [URLSchemesSet allObjects]; } [CFBundleURLTypesM addObject:modifiedURLType.copy]; @@ -517,172 +577,153 @@ 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* appPath, BOOL sign, BOOL force) +int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate) { NSLog(@"[installApp force = %d]", force); - if(!infoDictionaryForAppPath(appPath)) return 172; + NSString* appPayloadPath = [appPackagePath stringByAppendingPathComponent:@"Payload"]; + + NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPayloadPath error:nil]; + if(!items) return 167; + + NSString* appBundlePath; + for(NSString* item in items) + { + if([item.pathExtension isEqualToString:@"app"]) + { + appBundlePath = [appPayloadPath stringByAppendingPathComponent:item]; + break; + } + } + if(!appBundlePath) return 167; - NSString* appId = appIdForAppPath(appPath); + NSString* appId = appIdForAppPath(appBundlePath); if(!appId) return 176; - if([immutableAppBundleIdentifiers() containsObject:appId.lowercaseString]) + + if(([appId.lowercaseString isEqualToString:@"com.opa334.trollstore"] && !isTSUpdate) || [immutableAppBundleIdentifiers() containsObject:appId.lowercaseString]) { return 179; } - if(![appId isEqualToString:@"com.opa334.TrollStore"]) + if(!infoDictionaryForAppPath(appBundlePath)) return 172; + + if(!isTSUpdate) { - applyPatchesToInfoDictionary(appPath); + applyPatchesToInfoDictionary(appBundlePath); } if(sign) { - int signRet = signApp(appPath); + int signRet = signApp(appBundlePath); if(signRet != 0) return signRet; } - BOOL existed; - NSError* mcmError; - MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError]; - if(!appContainer || mcmError) + LSApplicationProxy* existingAppProxy = [LSApplicationProxy applicationProxyForIdentifier:appId]; + if(existingAppProxy.installed) { - NSLog(@"[installApp] failed to create app container for %@: %@", appId, mcmError); - return 170; - } + // App update + // Replace existing bundle with new version - if(existed) - { - NSLog(@"[installApp] got existing app container: %@", appContainer); - } - else - { - NSLog(@"[installApp] created app container: %@", appContainer); - } + // Check if the existing app bundle is empty + BOOL appBundleExists = existingAppProxy.bundleURL && [existingAppProxy.bundleURL checkResourceIsReachableAndReturnError:nil]; - // 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"]) + // LSBundleProxy also has a bundleContainerURL property, but unforunately it is unreliable and just nil most of the time + NSURL* bundleContainerURL = existingAppProxy.bundleURL.URLByDeletingLastPathComponent; + + // 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(appBundleExists && ![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(appPath); - - // 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(appBundleExists) { - // 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:existingAppProxy.bundleURL error:nil]; } - } - // Install app - NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent]; - NSLog(@"[installApp] new app path: %@", newAppPath); - - NSError* copyError; - BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:©Error]; - if(suc) - { - NSLog(@"[installApp] App %@ installed, adding to icon cache now...", appId); - registerPath((char*)newAppPath.UTF8String, 0, YES); - return 0; + // Install new version into existing app bundle + NSError* copyError; + BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundlePath toPath:[bundleContainerURL.path stringByAppendingPathComponent:appBundlePath.lastPathComponent] 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 + // Do initial placeholder installation using LSApplicationWorkspace + + NSError* installError; + BOOL suc = NO; + @try + { + suc = [[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); + suc = NO; + } + + if(!suc) + { + NSLog(@"[installApp] encountered error %@ while trying to do placeholder install", installError); + return 180; + } + + // Get newly installed proxy + existingAppProxy = [LSApplicationProxy applicationProxyForIdentifier:appId]; + + // Mark app as TrollStore app + NSURL* bundleContainerURL = existingAppProxy.bundleURL.URLByDeletingLastPathComponent; + NSURL* trollStoreMarkURL = [bundleContainerURL URLByAppendingPathComponent:@"_TrollStore"]; + 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; + } } + + // At this point the (new version of the) app is installed but still needs to be registered + // Also permissions need to be fixed + fixPermissionsOfAppBundle(existingAppProxy.bundleURL.path); + registerPath((char*)existingAppProxy.bundleURL.path.fileSystemRepresentation, 0, YES); } int uninstallApp(NSString* appPath, NSString* appId) { - if(appId) + BOOL deleteSuc = NO; + if(!appId && appPath) { - BKSTerminateApplicationForReasonAndReportWithDescription(appId, 5, false, @"TrollStore - App uninstalled"); - - LSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:appId]; - MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; - NSString *containerPath = [appContainer url].path; - if(containerPath) - { - NSLog(@"[uninstallApp] deleting %@", containerPath); - // delete app container path - [[NSFileManager defaultManager] removeItemAtPath:containerPath error:nil]; - } - - // delete group container paths - [[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupId, NSURL* groupURL, BOOL* stop) - { - // If another app still has this group, don't delete it - NSArray* appsWithGroup = applicationsWithGroupId(groupId); - if(appsWithGroup.count > 1) - { - NSLog(@"[uninstallApp] not deleting %@, appsWithGroup.count:%lu", groupURL, appsWithGroup.count); - return; - } - - NSLog(@"[uninstallApp] deleting %@", groupURL); - [[NSFileManager defaultManager] removeItemAtURL:groupURL error:nil]; - }]; - - // delete app plugin paths - for(LSPlugInKitProxy* pluginProxy in appProxy.plugInKitPlugins) - { - NSURL* pluginURL = pluginProxy.dataContainerURL; - if(pluginURL) - { - NSLog(@"[uninstallApp] deleting %@", pluginURL); - [[NSFileManager defaultManager] removeItemAtURL:pluginURL error:nil]; - } - } + // Special case, something is wrong about this app + // Most likely the Info.plist is missing + // (Hopefully this never happens) + deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil]; + registerPath((char*)appPath.fileSystemRepresentation, 1, YES); } - // unregister app - registerPath((char*)appPath.UTF8String, 1, YES); + if(appId) + { + deleteSuc = [[LSApplicationWorkspace defaultWorkspace] uninstallApplication:appId withOptions:nil]; + } - NSLog(@"[uninstallApp] deleting %@", [appPath stringByDeletingLastPathComponent]); - // delete app - BOOL deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil]; if(deleteSuc) { return 0; @@ -693,35 +734,18 @@ int uninstallApp(NSString* appPath, NSString* appId) } } -/*int detachApp(NSString* appId) -{ - NSString* appPath = appPathForAppId(appId); - NSString* executablePath = appMainExecutablePathForAppPath(appPath); - NSString* trollStoreMarkPath = [[appPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"_TrollStore"]; - - // Not attached to TrollStore - if(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreMarkPath]) return 0; - - // Refuse to detach app if it's still signed with fake root cert - SecStaticCodeRef codeRef = getStaticCodeRef(executablePath); - if(codeRef != NULL) - { - if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) - { - CFRelease(codeRef); - return 184; - } - } - - // Deleting TrollStore mark to detach app - BOOL suc = [[NSFileManager defaultManager] removeItemAtPath:trollStoreMarkPath error:nil]; - return !suc; -}*/ - int uninstallAppByPath(NSString* appPath) { if(!appPath) return 1; - NSString* appId = appIdForAppPath(appPath); + + NSString* standardizedAppPath = appPath.stringByStandardizingPath; + + if(![standardizedAppPath hasPrefix:@"/var/containers/Bundle/Application/"] && standardizedAppPath.pathComponents.count == 5) + { + return 1; + } + + NSString* appId = appIdForAppPath(standardizedAppPath); return uninstallApp(appPath, appId); } @@ -741,38 +765,21 @@ int installIpa(NSString* ipaPath, BOOL force) if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166; BOOL suc = NO; - NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; + NSString* tmpPackagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; - suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:nil]; + suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPackagePath withIntermediateDirectories:NO attributes:nil error:nil]; if(!suc) return 1; - extract(ipaPath, tmpPath); - - NSString* tmpPayloadPath = [tmpPath stringByAppendingPathComponent:@"Payload"]; - - NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpPayloadPath error:nil]; - if(!items) return 167; - - NSString* tmpAppPath; - for(NSString* item in items) + int extractRet = extract(ipaPath, tmpPackagePath); + if(extractRet != 0) { - if([item.pathExtension isEqualToString:@"app"]) - { - tmpAppPath = [tmpPayloadPath stringByAppendingPathComponent:item]; - break; - } + [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; + return 168; } - if(!tmpAppPath) return 167; - NSString* appId = appIdForAppPath(tmpAppPath); - if([appId.lowercaseString isEqualToString:@"com.opa334.trollstore"]) - { - return 179; - } + int ret = installApp(tmpPackagePath, YES, force, NO); - int ret = installApp(tmpAppPath, YES, force); - - [[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; return ret; } @@ -785,78 +792,85 @@ void uninstallAllApps(void) } } -BOOL uninstallTrollStore(BOOL unregister) +int uninstallTrollStore(BOOL unregister) { NSString* trollStore = trollStorePath(); if(![[NSFileManager defaultManager] fileExistsAtPath:trollStore]) return NO; if(unregister) { - registerPath((char*)trollStoreAppPath().UTF8String, 1, YES); + registerPath((char*)trollStoreAppPath().fileSystemRepresentation, 1, YES); } return [[NSFileManager defaultManager] removeItemAtPath:trollStore error:nil]; } -BOOL installTrollStore(NSString* pathToTar) +int installTrollStore(NSString* pathToTar) { - //_CFPreferencesCopyValueWithContainerType _CFPreferencesCopyValueWithContainer = (_CFPreferencesCopyValueWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesCopyValueWithContainer"); _CFPreferencesSetValueWithContainerType _CFPreferencesSetValueWithContainer = (_CFPreferencesSetValueWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSetValueWithContainer"); _CFPreferencesSynchronizeWithContainerType _CFPreferencesSynchronizeWithContainer = (_CFPreferencesSynchronizeWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSynchronizeWithContainer"); - - /*CFPropertyListRef SBShowNonDefaultSystemAppsValue = _CFPreferencesCopyValueWithContainer(CFSTR("SBShowNonDefaultSystemApps"), CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); - if(SBShowNonDefaultSystemAppsValue != kCFBooleanTrue) - {*/ - _CFPreferencesSetValueWithContainer(CFSTR("SBShowNonDefaultSystemApps"), kCFBooleanTrue, CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); - _CFPreferencesSynchronizeWithContainer(CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); - //NSLog(@"unrestricted springboard apps"); - /*}*/ - + _CFPreferencesSetValueWithContainer(CFSTR("SBShowNonDefaultSystemApps"), kCFBooleanTrue, CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); + _CFPreferencesSynchronizeWithContainer(CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); if(![[NSFileManager defaultManager] fileExistsAtPath:pathToTar]) return 1; if(![pathToTar.pathExtension isEqualToString:@"tar"]) return 1; - NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; - BOOL suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:nil]; + NSString* tmpPackagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; + NSString* tmpPayloadPath = [tmpPackagePath stringByAppendingPathComponent:@"Payload"]; + BOOL suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPayloadPath withIntermediateDirectories:YES attributes:nil error:nil]; if(!suc) return 1; - extract(pathToTar, tmpPath); + int extractRet = extract(pathToTar, tmpPayloadPath); + if(extractRet != 0) + { + [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; + return 169; + } - NSString* tmpTrollStore = [tmpPath stringByAppendingPathComponent:@"TrollStore.app"]; - if(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStore]) return 1; + 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]) { - NSString* tmpLdidPath = [tmpTrollStore stringByAppendingPathComponent:@"ldid"]; + NSString* tmpLdidPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid"]; if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidPath]) { [[NSFileManager defaultManager] copyItemAtPath:existingLdidPath toPath:tmpLdidPath error:nil]; } } + // Merge existing URL scheme settings value + if(!getTSURLSchemeState(nil)) + { + setTSURLSchemeState(NO, tmpTrollStorePath); + } + // Update system app persistence helper if used LSApplicationProxy* persistenceHelperApp = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_SYSTEM); if(persistenceHelperApp) { - NSString* trollStorePersistenceHelper = [tmpTrollStore stringByAppendingPathComponent:@"PersistenceHelper"]; - NSString* trollStoreRootHelper = [tmpTrollStore stringByAppendingPathComponent:@"trollstorehelper"]; + NSString* trollStorePersistenceHelper = [tmpTrollStorePath stringByAppendingPathComponent:@"PersistenceHelper"]; + NSString* trollStoreRootHelper = [tmpTrollStorePath stringByAppendingPathComponent:@"trollstorehelper"]; _installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper); } - return installApp(tmpTrollStore, NO, YES);; + int ret = installApp(tmpPackagePath, NO, YES, YES); + NSLog(@"[installTrollStore] installApp => %d", ret); + [[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil]; + return ret; } void refreshAppRegistrations() { - //registerPath((char*)trollStoreAppPath().UTF8String, 1, YES); - registerPath((char*)trollStoreAppPath().UTF8String, 0, YES); + //registerPath((char*)trollStoreAppPath().fileSystemRepresentation, 1, YES); + registerPath((char*)trollStoreAppPath().fileSystemRepresentation, 0, YES); for(NSString* appPath in trollStoreInstalledAppBundlePaths()) { - //registerPath((char*)appPath.UTF8String, 1, YES); - registerPath((char*)appPath.UTF8String, 0, YES); + //registerPath((char*)appPath.fileSystemRepresentation, 1, YES); + registerPath((char*)appPath.fileSystemRepresentation, 0, YES); } } @@ -893,8 +907,8 @@ BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePer return NO; } - chmod(executablePath.UTF8String, 0755); - chown(executablePath.UTF8String, 33, 33); + chmod(executablePath.fileSystemRepresentation, 0755); + chown(executablePath.fileSystemRepresentation, 33, 33); NSError* error; if(![[NSFileManager defaultManager] copyItemAtPath:sourceRootHelper toPath:rootHelperPath error:&error]) @@ -902,8 +916,8 @@ BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePer NSLog(@"error copying root helper: %@", error); } - chmod(rootHelperPath.UTF8String, 0755); - chown(rootHelperPath.UTF8String, 0, 0); + chmod(rootHelperPath.fileSystemRepresentation, 0755); + chown(rootHelperPath.fileSystemRepresentation, 0, 0); // mark system app as persistence helper if(![[NSFileManager defaultManager] fileExistsAtPath:markPath]) @@ -1050,8 +1064,6 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) NSLog(@"trollstore helper go, uid: %d, gid: %d", getuid(), getgid()); - loadMCMFramework(); - int ret = 0; NSString* cmd = [NSString stringWithUTF8String:argv[1]]; @@ -1073,12 +1085,7 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) if(argc <= 2) return -3; NSString* appId = [NSString stringWithUTF8String:argv[2]]; ret = uninstallAppById(appId); - } /*else if([cmd isEqualToString:@"detach"]) - { - if(argc <= 2) return -3; - NSString* appId = [NSString stringWithUTF8String:argv[2]]; - ret = detachApp(appId); - } */else if([cmd isEqualToString:@"uninstall-path"]) + } else if([cmd isEqualToString:@"uninstall-path"]) { if(argc <= 2) return -3; NSString* appPath = [NSString stringWithUTF8String:argv[2]]; @@ -1093,7 +1100,10 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) { uninstallAllApps(); uninstallTrollStore(YES); - } else if([cmd isEqualToString:@"install-ldid"]) + } else if([cmd isEqualToString:@"uninstall-trollstore-preserve-apps"]) + { + uninstallTrollStore(YES); + }else if([cmd isEqualToString:@"install-ldid"]) { if(argc <= 2) return -3; NSString* ldidPath = [NSString stringWithUTF8String:argv[2]]; @@ -1129,7 +1139,16 @@ int MAIN_NAME(int argc, char *argv[], char *envp[]) NSString* trollStoreMark = [[appPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"_TrollStore"]; if([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark]) { - registerPath((char*)appPath.UTF8String, 0, [newRegistration isEqualToString:@"System"]); + registerPath((char*)appPath.fileSystemRepresentation, 0, [newRegistration isEqualToString:@"System"]); + } + } else if([cmd isEqualToString:@"url-scheme"]) + { + if(argc <= 2) return -3; + NSString* modifyArg = [NSString stringWithUTF8String:argv[2]]; + BOOL newState = [modifyArg isEqualToString:@"enable"]; + if(newState == YES || [modifyArg isEqualToString:@"disable"]) + { + setTSURLSchemeState(newState, nil); } } diff --git a/RootHelper/unarchive.m b/RootHelper/unarchive.m index 83bbd5b..be72fb2 100644 --- a/RootHelper/unarchive.m +++ b/RootHelper/unarchive.m @@ -45,7 +45,7 @@ int extract(NSString* fileToExtract, NSString* extractionPath) ext = archive_write_disk_new(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); - if ((r = archive_read_open_filename(a, fileToExtract.UTF8String, 10240))) + if ((r = archive_read_open_filename(a, fileToExtract.fileSystemRepresentation, 10240))) return 1; for (;;) { @@ -59,8 +59,8 @@ int extract(NSString* fileToExtract, NSString* extractionPath) NSString* currentFile = [NSString stringWithUTF8String:archive_entry_pathname(entry)]; NSString* fullOutputPath = [extractionPath stringByAppendingPathComponent:currentFile]; - //printf("extracting %s to %s\n", currentFile.UTF8String, fullOutputPath.UTF8String); - archive_entry_set_pathname(entry, fullOutputPath.UTF8String); + //printf("extracting %@ to %@\n", currentFile, fullOutputPath); + archive_entry_set_pathname(entry, fullOutputPath.fileSystemRepresentation); r = archive_write_header(ext, entry); if (r < ARCHIVE_OK) diff --git a/Shared/CoreServices.h b/Shared/CoreServices.h index de33eef..4a21547 100644 --- a/Shared/CoreServices.h +++ b/Shared/CoreServices.h @@ -1,6 +1,9 @@ +extern NSString *LSInstallTypeKey; + @interface LSBundleProxy @property (nonatomic,readonly) NSString * bundleIdentifier; @property (nonatomic) NSURL* dataContainerURL; +@property (nonatomic,readonly) NSURL* bundleContainerURL; -(NSString*)localizedName; @end @@ -26,7 +29,7 @@ - (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3; - (BOOL)openApplicationWithBundleID:(NSString *)arg1 ; - (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block; -- (BOOL)installApplication:(NSString*)pathToExtractedApp withOptions:(NSDictionary*)options; +- (BOOL)installApplication:(NSURL*)appPackageURL withOptions:(NSDictionary*)options error:(NSError**)error; - (BOOL)uninstallApplication:(NSString*)appId withOptions:(NSDictionary*)options; @end diff --git a/Shared/TSListControllerShared.h b/Shared/TSListControllerShared.h index b78269b..cb9612d 100644 --- a/Shared/TSListControllerShared.h +++ b/Shared/TSListControllerShared.h @@ -3,10 +3,6 @@ #import @interface TSListControllerShared : PSListController -{ - UIAlertController* _activityController; -} - - (BOOL)isTrollStore; - (NSString*)getTrollStoreVersion; - (void)downloadTrollStoreAndDo:(void (^)(NSString* localTrollStoreTarPath))doHandler; diff --git a/Shared/TSListControllerShared.m b/Shared/TSListControllerShared.m index a25c456..f57a1be 100644 --- a/Shared/TSListControllerShared.m +++ b/Shared/TSListControllerShared.m @@ -190,19 +190,26 @@ - (void)uninstallTrollStorePressed { - UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert]; + 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* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [uninstallWarningAlert addAction:cancelAction]; - - UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) + UIAlertAction* uninstallAllAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Uninstall Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { spawnRoot(rootHelperPath(), @[@"uninstall-trollstore"], nil, nil); [self handleUninstallation]; }]; - [uninstallWarningAlert addAction:continueAction]; + [uninstallAlert addAction:uninstallAllAction]; - [TSPresentationDelegate presentViewController:uninstallWarningAlert animated:YES completion:nil]; + UIAlertAction* preserveAppsAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Preserve Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) + { + spawnRoot(rootHelperPath(), @[@"uninstall-trollstore-preserve-apps"], nil, nil); + [self handleUninstallation]; + }]; + [uninstallAlert addAction:preserveAppsAction]; + + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + [uninstallAlert addAction:cancelAction]; + + [TSPresentationDelegate presentViewController:uninstallAlert animated:YES completion:nil]; } @end \ No newline at end of file diff --git a/TrollHelper/Resources/Info.plist b/TrollHelper/Resources/Info.plist index 6891349..13e385b 100644 --- a/TrollHelper/Resources/Info.plist +++ b/TrollHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.3.5 + 1.4 LSRequiresIPhoneOS UIDeviceFamily diff --git a/TrollHelper/control b/TrollHelper/control index 3a87a38..54b263f 100644 --- a/TrollHelper/control +++ b/TrollHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstorehelper Name: TrollStore Helper -Version: 1.3.5 +Version: 1.4 Architecture: iphoneos-arm Description: Helper utility to install and manage TrollStore! Maintainer: opa334 diff --git a/TrollHelper/entitlements.plist b/TrollHelper/entitlements.plist index 687e38b..8d8ff62 100644 --- a/TrollHelper/entitlements.plist +++ b/TrollHelper/entitlements.plist @@ -3,31 +3,7 @@ application-identifier - com.opa334.TrollStore - platform-application - - com.apple.security.exception.files.absolute-path.read-write - - / - - com.apple.private.security.no-sandbox - - com.apple.private.persona-mgmt - - com.apple.private.security.container-manager - - com.apple.private.coreservices.canmaplsdatabase - - com.apple.lsapplicationworkspace.rebuildappdatabases - - com.apple.private.MobileContainerManager.allowed - - com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled - - com.apple.private.MobileInstallationHelperService.allowed - - com.apple.private.uninstall.deletion - + com.opa334.trollstorepersistencehelper com.apple.CommCenter.fine-grained cellular-plan @@ -35,11 +11,49 @@ data-allowed-write preferences-write + com.apple.private.persona-mgmt + + + + platform-application + + com.apple.private.security.no-sandbox + + com.apple.private.security.container-manager + + com.apple.private.MobileContainerManager.allowed + + com.apple.private.coreservices.canmaplsdatabase + + com.apple.lsapplicationworkspace.rebuildappdatabases + com.apple.private.security.storage.AppBundles + com.apple.private.security.storage.MobileDocuments + + com.apple.private.security.storage-exempt.heritable + + com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled + + com.apple.private.MobileInstallationHelperService.allowed + + com.apple.private.uninstall.deletion + + com.apple.springboard.launchapplications + com.apple.backboardd.launchapplications + com.apple.frontboard.launchapplications + com.apple.multitasking.termination + com.apple.private.mobileinstall.allowedSPI + + InstallForLaunchServices + Install + UninstallForLaunchServices + Uninstall + UpdatePlaceholderMetadata + \ No newline at end of file diff --git a/TrollStore/Resources/Info.plist b/TrollStore/Resources/Info.plist index 3556ea2..403e4f5 100644 --- a/TrollStore/Resources/Info.plist +++ b/TrollStore/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.3.5 + 1.4 LSRequiresIPhoneOS UIDeviceFamily diff --git a/TrollStore/TSAppInfo.m b/TrollStore/TSAppInfo.m index d9c7e02..b8867b5 100644 --- a/TrollStore/TSAppInfo.m +++ b/TrollStore/TSAppInfo.m @@ -366,7 +366,7 @@ extern UIImage* imageWithSize(UIImage* image, CGSize size); _archive = archive_read_new(); archive_read_support_format_all(_archive); archive_read_support_filter_all(_archive); - int r = archive_read_open_filename(_archive, _path.UTF8String, 10240); + int r = archive_read_open_filename(_archive, _path.fileSystemRepresentation, 10240); return r ? r : 0; } diff --git a/TrollStore/TSAppTableViewController.m b/TrollStore/TSAppTableViewController.m index 89e25fb..368be5f 100644 --- a/TrollStore/TSAppTableViewController.m +++ b/TrollStore/TSAppTableViewController.m @@ -178,7 +178,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size) - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { NSString* pathToIPA = urls.firstObject.path; - [TSInstallationController presentInstallationAlertForFile:pathToIPA completion:nil]; + [TSInstallationController presentInstallationAlertIfEnabledForFile:pathToIPA isRemoteInstall:NO completion:nil]; } - (void)openAppPressedForRowAtIndexPath:(NSIndexPath*)indexPath diff --git a/TrollStore/TSApplicationsManager.m b/TrollStore/TSApplicationsManager.m index 880d5eb..b585132 100644 --- a/TrollStore/TSApplicationsManager.m +++ b/TrollStore/TSApplicationsManager.m @@ -30,6 +30,12 @@ case 167: errorDescription = @"The IPA file does not appear to contain an app."; break; + case 168: + errorDescription = @"Failed to extract IPA file."; + break; + case 169: + errorDescription = @"Failed to extract update tar file."; + break; // App install errors case 170: errorDescription = @"Failed to create container for app bundle."; @@ -61,10 +67,9 @@ case 179: errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues."; break; - // App detach errors - /*case 184: - errorDescription = @"Refusing to detach, the app is still signed with a fake root certificate. The detach option is only for when you have installed an App Store app on top of a TrollStore app."; - break;*/ + case 180: + errorDescription = @"The LSApplicationWorkspace app installation failed."; + break; } NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}]; diff --git a/TrollStore/TSInstallationController.h b/TrollStore/TSInstallationController.h index 122b6aa..96ebdc2 100644 --- a/TrollStore/TSInstallationController.h +++ b/TrollStore/TSInstallationController.h @@ -2,7 +2,7 @@ @interface TSInstallationController : NSObject -+ (void)presentInstallationAlertForFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion; ++ (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock; + (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completion; + (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion; diff --git a/TrollStore/TSInstallationController.m b/TrollStore/TSInstallationController.m index 9d1a236..fd02b6d 100644 --- a/TrollStore/TSInstallationController.m +++ b/TrollStore/TSInstallationController.m @@ -5,6 +5,8 @@ #import #import +extern NSUserDefaults* trollStoreUserDefaults(void); + @implementation TSInstallationController + (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completionBlock @@ -73,8 +75,30 @@ }); } -+ (void)presentInstallationAlertForFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completionBlock ++ (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock { + NSNumber* installAlertConfigurationNum = [trollStoreUserDefaults() objectForKey:@"installAlertConfiguration"]; + NSUInteger installAlertConfiguration = 0; + if(installAlertConfigurationNum) + { + installAlertConfiguration = installAlertConfigurationNum.unsignedIntegerValue; + if(installAlertConfiguration > 2) + { + // broken pref? revert to 0 + installAlertConfiguration = 0; + } + } + + // Check if user disabled alert for this kind of install + if(installAlertConfiguration > 0) + { + if(installAlertConfiguration == 2 || (installAlertConfiguration == 1 && !remoteInstall)) + { + [self handleAppInstallFromFile:pathToIPA completion:completionBlock]; + return; + } + } + TSAppInfo* appInfo = [[TSAppInfo alloc] initWithIPAPath:pathToIPA]; [appInfo loadInfoWithCompletion:^(NSError* error) { @@ -144,7 +168,7 @@ NSString* tmpIpaPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.ipa"]; [[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil]; [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tmpIpaPath error:nil]; - [self presentInstallationAlertForFile:tmpIpaPath completion:^(BOOL success, NSError* error) + [self presentInstallationAlertIfEnabledForFile:tmpIpaPath isRemoteInstall:YES completion:^(BOOL success, NSError* error) { [[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil]; if(completionBlock) completionBlock(success, error); diff --git a/TrollStore/TSSceneDelegate.m b/TrollStore/TSSceneDelegate.m index 8f3b4c1..8550dfe 100644 --- a/TrollStore/TSSceneDelegate.m +++ b/TrollStore/TSSceneDelegate.m @@ -32,7 +32,7 @@ if ([url.pathExtension.lowercaseString isEqualToString:@"ipa"] || [url.pathExtension.lowercaseString isEqualToString:@"tipa"]) { - [TSInstallationController presentInstallationAlertForFile:url.path completion:^(BOOL success, NSError* error){ + [TSInstallationController presentInstallationAlertIfEnabledForFile:url.path isRemoteInstall:NO completion:^(BOOL success, NSError* error){ doneBlock(NO); }]; } diff --git a/TrollStore/TSSettingsListController.m b/TrollStore/TSSettingsListController.m index 9f3e6d2..2b6f7e6 100644 --- a/TrollStore/TSSettingsListController.m +++ b/TrollStore/TSSettingsListController.m @@ -1,8 +1,14 @@ #import "TSSettingsListController.h" #import #import +#import #import +@interface NSUserDefaults (Private) +- (instancetype)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container; +@end +extern NSUserDefaults* trollStoreUserDefaults(void); + @implementation TSSettingsListController - (void)viewDidLoad @@ -195,6 +201,40 @@ } } + PSSpecifier* installationSettingsGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; + installationSettingsGroupSpecifier.name = @"Security"; + [installationSettingsGroupSpecifier setProperty:@"The URL Scheme, when enabled, will allow apps and websites to trigger TrollStore installations through the apple-magnifier://install?url= URL scheme." forKey:@"footerText"]; + + [_specifiers addObject:installationSettingsGroupSpecifier]; + + PSSpecifier* URLSchemeToggle = [PSSpecifier preferenceSpecifierNamed:@"URL Scheme Enabled" + target:self + set:@selector(setURLSchemeEnabled:forSpecifier:) + get:@selector(getURLSchemeEnabledForSpecifier:) + detail:nil + cell:PSSwitchCell + edit:nil]; + + [_specifiers addObject:URLSchemeToggle]; + + PSSpecifier* installAlertConfigurationSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Show Install Confirmation Alert" + target:self + set:@selector(setPreferenceValue:specifier:) + get:@selector(readPreferenceValue:) + detail:nil + cell:PSLinkListCell + edit:nil]; + + installAlertConfigurationSpecifier.detailControllerClass = [PSListItemsController class]; + [installAlertConfigurationSpecifier setProperty:@"installationConfirmationValues" forKey:@"valuesDataSource"]; + [installAlertConfigurationSpecifier setProperty:@"installationConfirmationNames" forKey:@"titlesDataSource"]; + [installAlertConfigurationSpecifier setProperty:@"com.opa334.TrollStore" forKey:@"defaults"]; + [installAlertConfigurationSpecifier setProperty:@"installAlertConfiguration" forKey:@"key"]; + [installAlertConfigurationSpecifier setProperty:@0 forKey:@"default"]; + + [_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"]; [_specifiers addObject:otherGroupSpecifier]; @@ -230,6 +270,16 @@ return _specifiers; } +- (NSArray*)installationConfirmationValues +{ + return @[@0, @1, @2]; +} + +- (NSArray*)installationConfirmationNames +{ + return @[@"Always (Recommended)", @"Only on Remote Installs", @"Never (Not Recommeded)"]; +} + - (void)respringButtonPressed { respring(); @@ -313,9 +363,51 @@ [TSPresentationDelegate presentViewController:selectAppAlert animated:YES completion:nil]; } +- (id)getURLSchemeEnabledForSpecifier:(PSSpecifier*)specifier +{ + BOOL URLSchemeActive = (BOOL)[NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]; + return @(URLSchemeActive); +} + +- (void)setURLSchemeEnabled:(id)value forSpecifier:(PSSpecifier*)specifier +{ + NSNumber* newValue = value; + NSString* newStateString = [newValue boolValue] ? @"enable" : @"disable"; + spawnRoot(rootHelperPath(), @[@"url-scheme", newStateString], nil, nil); + + UIAlertController* rebuildNoticeAlert = [UIAlertController alertControllerWithTitle:@"URL Scheme Changed" message:@"In order to properly apply the change of the URL scheme setting, rebuilding the icon cache is needed." preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* rebuildNowAction = [UIAlertAction actionWithTitle:@"Rebuild Now" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + { + [self rebuildIconCachePressed]; + }]; + [rebuildNoticeAlert addAction:rebuildNowAction]; + + UIAlertAction* rebuildLaterAction = [UIAlertAction actionWithTitle:@"Rebuild Later" style:UIAlertActionStyleCancel handler:nil]; + [rebuildNoticeAlert addAction:rebuildLaterAction]; + + [TSPresentationDelegate presentViewController:rebuildNoticeAlert animated:YES completion:nil]; +} + - (void)doTheDashPressed { spawnRoot(rootHelperPath(), @[@"dash"], nil, nil); } +- (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/control b/TrollStore/control index 502dbb9..264d020 100644 --- a/TrollStore/control +++ b/TrollStore/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.3.5 +Version: 1.4 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334 diff --git a/TrollStore/main.m b/TrollStore/main.m index dee9a84..99cf0a0 100644 --- a/TrollStore/main.m +++ b/TrollStore/main.m @@ -2,6 +2,11 @@ #import "TSAppDelegate.h" #import "TSUtil.h" +NSUserDefaults* trollStoreUserDefaults(void) +{ + return [[NSUserDefaults alloc] initWithSuiteName:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.opa334.TrollStore.plist"]]; +} + int main(int argc, char *argv[]) { @autoreleasepool { chineseWifiFixup();