mirror of
				https://github.com/opa334/TrollStore.git
				synced 2025-11-04 07:32:36 +08:00 
			
		
		
		
	1.0.8
This commit is contained in:
		
							parent
							
								
									ab3261f0f2
								
							
						
					
					
						commit
						5d474901a2
					
				@ -13,7 +13,8 @@
 | 
			
		||||
@property (nonatomic,readonly) NSArray* plugInKitPlugins;
 | 
			
		||||
@property (getter=isInstalled,nonatomic,readonly) BOOL installed; 
 | 
			
		||||
@property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder; 
 | 
			
		||||
@property (getter=isRestricted,nonatomic,readonly) BOOL restricted; 
 | 
			
		||||
@property (getter=isRestricted,nonatomic,readonly) BOOL restricted;
 | 
			
		||||
@property (nonatomic,readonly) NSSet * claimedURLSchemes;
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@interface LSApplicationWorkspace : NSObject
 | 
			
		||||
@ -25,6 +26,11 @@
 | 
			
		||||
- (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block;
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@interface LSEnumerator : NSEnumerator
 | 
			
		||||
@property (nonatomic,copy) NSPredicate * predicate;
 | 
			
		||||
+ (instancetype)enumeratorForApplicationProxiesWithOptions:(NSUInteger)options;
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@interface LSPlugInKitProxy : LSBundleProxy
 | 
			
		||||
@property (nonatomic,readonly) NSString* pluginIdentifier;
 | 
			
		||||
@property (nonatomic,readonly) NSDictionary * pluginKitDictionary;
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
Package: com.opa334.trollstoreroothelper
 | 
			
		||||
Name: trollstoreroothelper
 | 
			
		||||
Version: 1.0.6
 | 
			
		||||
Version: 1.0.8
 | 
			
		||||
Architecture: iphoneos-arm
 | 
			
		||||
Description: An awesome tool of some sort!!
 | 
			
		||||
Maintainer: opa334
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										262
									
								
								Helper/main.m
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								Helper/main.m
									
									
									
									
									
								
							@ -54,6 +54,28 @@ typedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRe
 | 
			
		||||
 | 
			
		||||
BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper);
 | 
			
		||||
 | 
			
		||||
NSArray<LSApplicationProxy*>* applicationsWithGroupId(NSString* groupId)
 | 
			
		||||
{
 | 
			
		||||
	LSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0];
 | 
			
		||||
	enumerator.predicate = [NSPredicate predicateWithFormat:@"groupContainerURLs[%@] != nil", groupId];
 | 
			
		||||
	return enumerator.allObjects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NSSet<NSString*>* appleURLSchemes(void)
 | 
			
		||||
{
 | 
			
		||||
	LSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0];
 | 
			
		||||
	enumerator.predicate = [NSPredicate predicateWithFormat:@"bundleIdentifier BEGINSWITH 'com.apple'"];
 | 
			
		||||
 | 
			
		||||
	NSMutableSet* systemURLSchemes = [NSMutableSet new];
 | 
			
		||||
	LSApplicationProxy* proxy;
 | 
			
		||||
	while(proxy = [enumerator nextObject])
 | 
			
		||||
	{
 | 
			
		||||
		[systemURLSchemes unionSet:proxy.claimedURLSchemes];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return systemURLSchemes.copy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern char*** _NSGetArgv();
 | 
			
		||||
NSString* safe_getExecutablePath()
 | 
			
		||||
{
 | 
			
		||||
@ -72,7 +94,12 @@ NSString* appIdForAppPath(NSString* appPath)
 | 
			
		||||
	return infoDictionaryForAppPath(appPath)[@"CFBundleIdentifier"];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NSString* appPathForAppId(NSString* appId, NSError** error)
 | 
			
		||||
NSString* appMainExecutablePathForAppPath(NSString* appPath)
 | 
			
		||||
{
 | 
			
		||||
	return [appPath stringByAppendingPathComponent:infoDictionaryForAppPath(appPath)[@"CFBundleExecutable"]];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NSString* appPathForAppId(NSString* appId)
 | 
			
		||||
{
 | 
			
		||||
	for(NSString* appPath in trollStoreInstalledAppBundlePaths())
 | 
			
		||||
	{
 | 
			
		||||
@ -411,15 +438,15 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef)
 | 
			
		||||
	return evaluatesToCustomAnchor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BOOL signApp(NSString* appPath, NSError** error)
 | 
			
		||||
int signApp(NSString* appPath)
 | 
			
		||||
{
 | 
			
		||||
	NSDictionary* appInfoDict = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]];
 | 
			
		||||
	if(!appInfoDict) return NO;
 | 
			
		||||
	NSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);
 | 
			
		||||
	if(!appInfoDict) return 172;
 | 
			
		||||
 | 
			
		||||
	NSString* executable = appInfoDict[@"CFBundleExecutable"];
 | 
			
		||||
	NSString* executablePath = [appPath stringByAppendingPathComponent:executable];
 | 
			
		||||
	NSString* executablePath = appMainExecutablePathForAppPath(appPath);
 | 
			
		||||
	if(!executablePath) return 176;
 | 
			
		||||
 | 
			
		||||
	if(![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) return NO;
 | 
			
		||||
	if(![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) return 174;
 | 
			
		||||
	
 | 
			
		||||
	NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"];
 | 
			
		||||
	if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]])
 | 
			
		||||
@ -430,7 +457,7 @@ BOOL signApp(NSString* appPath, NSError** error)
 | 
			
		||||
		if([tsBundleIsPreSignedNum boolValue] == YES)
 | 
			
		||||
		{
 | 
			
		||||
			NSLog(@"[signApp] taking fast path for app which declares it has already been signed (%@)", executablePath);
 | 
			
		||||
			return YES;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
@ -441,7 +468,7 @@ BOOL signApp(NSString* appPath, NSError** error)
 | 
			
		||||
		{
 | 
			
		||||
			NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath);
 | 
			
		||||
			CFRelease(codeRef);
 | 
			
		||||
			return YES;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
@ -449,7 +476,7 @@ BOOL signApp(NSString* appPath, NSError** error)
 | 
			
		||||
		NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", executablePath);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(!isLdidInstalled()) return NO;
 | 
			
		||||
	if(!isLdidInstalled()) return 173;
 | 
			
		||||
 | 
			
		||||
	NSString* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"];
 | 
			
		||||
	NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath];
 | 
			
		||||
@ -481,7 +508,14 @@ BOOL signApp(NSString* appPath, NSError** error)
 | 
			
		||||
 | 
			
		||||
	NSLog(@"- ldid error output end -");
 | 
			
		||||
 | 
			
		||||
	return ldidRet == 0;
 | 
			
		||||
	if(ldidRet == 0)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		return 175;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void applyPatchesToInfoDictionary(NSString* appPath)
 | 
			
		||||
@ -491,9 +525,34 @@ void applyPatchesToInfoDictionary(NSString* appPath)
 | 
			
		||||
	NSMutableDictionary* infoDictM = [[NSDictionary dictionaryWithContentsOfURL:infoPlistURL error:nil] mutableCopy];
 | 
			
		||||
	if(!infoDictM) return;
 | 
			
		||||
 | 
			
		||||
	// enable notifications
 | 
			
		||||
	// Enable Notifications
 | 
			
		||||
	infoDictM[@"SBAppUsesLocalNotifications"] = @1;
 | 
			
		||||
 | 
			
		||||
	// Remove system claimed URL schemes if existant
 | 
			
		||||
	NSSet* appleSchemes = appleURLSchemes();
 | 
			
		||||
	NSArray* CFBundleURLTypes = infoDictM[@"CFBundleURLTypes"];
 | 
			
		||||
	if([CFBundleURLTypes isKindOfClass:[NSArray class]])
 | 
			
		||||
	{
 | 
			
		||||
		NSMutableArray* CFBundleURLTypesM = [NSMutableArray new];
 | 
			
		||||
 | 
			
		||||
		for(NSDictionary* URLType in CFBundleURLTypes)
 | 
			
		||||
		{
 | 
			
		||||
			if(![URLType isKindOfClass:[NSDictionary class]]) continue;
 | 
			
		||||
 | 
			
		||||
			NSMutableDictionary* modifiedURLType = URLType.mutableCopy;
 | 
			
		||||
			NSArray* URLSchemes = URLType[@"CFBundleURLSchemes"];
 | 
			
		||||
			if(URLSchemes)
 | 
			
		||||
			{
 | 
			
		||||
				NSMutableSet* URLSchemesSet = [NSMutableSet setWithArray:URLSchemes];
 | 
			
		||||
				[URLSchemesSet minusSet:appleSchemes];
 | 
			
		||||
				modifiedURLType[@"CFBundleURLSchemes"] = [URLSchemesSet allObjects];
 | 
			
		||||
			}
 | 
			
		||||
			[CFBundleURLTypesM addObject:modifiedURLType.copy];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		infoDictM[@"CFBundleURLTypes"] = CFBundleURLTypesM.copy;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[infoDictM writeToURL:infoPlistURL error:nil];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -501,33 +560,45 @@ void applyPatchesToInfoDictionary(NSString* appPath)
 | 
			
		||||
// 171: a non trollstore app with the same identifier is already installled
 | 
			
		||||
// 172: no info.plist found in app
 | 
			
		||||
// 173: app is not signed and cannot be signed because ldid not installed or didn't work
 | 
			
		||||
int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
// 174: 
 | 
			
		||||
int installApp(NSString* appPath, BOOL sign, BOOL force)
 | 
			
		||||
{
 | 
			
		||||
	NSLog(@"[installApp force = %d]", force);
 | 
			
		||||
 | 
			
		||||
	if(!infoDictionaryForAppPath(appPath)) return 172;
 | 
			
		||||
 | 
			
		||||
	NSString* appId = appIdForAppPath(appPath);
 | 
			
		||||
	if(!appId) return 172;
 | 
			
		||||
	if(!appId) return 176;
 | 
			
		||||
 | 
			
		||||
	applyPatchesToInfoDictionary(appPath);
 | 
			
		||||
 | 
			
		||||
	if(sign)
 | 
			
		||||
	{
 | 
			
		||||
		if(!signApp(appPath, error)) return 173;
 | 
			
		||||
		int signRet = signApp(appPath);
 | 
			
		||||
		if(signRet != 0) return signRet;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BOOL existed;
 | 
			
		||||
	NSError* mcmError;
 | 
			
		||||
	MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError];
 | 
			
		||||
	NSLog(@"[installApp] appContainer: %@, mcmError: %@", appContainer, mcmError);
 | 
			
		||||
	if(!appContainer || mcmError)
 | 
			
		||||
	{
 | 
			
		||||
		if(error) *error = mcmError;
 | 
			
		||||
		NSLog(@"[installApp] failed to create app container for %@: %@", appId, mcmError);
 | 
			
		||||
		return 170;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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:error];
 | 
			
		||||
	NSArray* bundleItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appContainer.url.path error:nil];
 | 
			
		||||
	for(NSString* bundleItem in bundleItems)
 | 
			
		||||
	{
 | 
			
		||||
		if([bundleItem.pathExtension isEqualToString:@"app"])
 | 
			
		||||
@ -537,6 +608,8 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
@ -546,7 +619,12 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mark app as TrollStore app
 | 
			
		||||
	[[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil];
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Apply correct permissions (First run, set everything to 644, owner 33)
 | 
			
		||||
	NSURL* fileURL;
 | 
			
		||||
@ -556,6 +634,7 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
		NSString* filePath = fileURL.path;
 | 
			
		||||
		chown(filePath.UTF8String, 33, 33);
 | 
			
		||||
		chmod(filePath.UTF8String, 0644);
 | 
			
		||||
		NSLog(@"[installApp] setting %@ to chown(33,33) chmod(0644)", filePath);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Apply correct permissions (Second run, set executables and directories to 0755)
 | 
			
		||||
@ -571,10 +650,28 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
		{
 | 
			
		||||
			NSDictionary* infoDictionary = [NSDictionary dictionaryWithContentsOfFile:filePath];
 | 
			
		||||
			NSString* executable = infoDictionary[@"CFBundleExecutable"];
 | 
			
		||||
			if(executable)
 | 
			
		||||
			if(executable && [executable isKindOfClass:[NSString class]])
 | 
			
		||||
			{
 | 
			
		||||
				NSString* executablePath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:executable];
 | 
			
		||||
				chmod(executablePath.UTF8String, 0755);
 | 
			
		||||
				NSLog(@"[installApp] applied permissions for bundle executable %@", executablePath);
 | 
			
		||||
			}
 | 
			
		||||
			NSArray* tsRootBinaries = infoDictionary[@"TSRootBinaries"];
 | 
			
		||||
			if(tsRootBinaries && [tsRootBinaries isKindOfClass:[NSArray class]])
 | 
			
		||||
			{
 | 
			
		||||
				for(NSString* rootBinary in tsRootBinaries)
 | 
			
		||||
				{
 | 
			
		||||
					if([rootBinary isKindOfClass:[NSString class]])
 | 
			
		||||
					{
 | 
			
		||||
						NSString* rootBinaryPath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:rootBinary];
 | 
			
		||||
						if([[NSFileManager defaultManager] fileExistsAtPath:rootBinaryPath])
 | 
			
		||||
						{
 | 
			
		||||
							chmod(rootBinaryPath.UTF8String, 0755);
 | 
			
		||||
							chown(rootBinaryPath.UTF8String, 0, 0);
 | 
			
		||||
							NSLog(@"[installApp] applied permissions for root binary %@", rootBinaryPath);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if(!isDir && [filePath.pathExtension isEqualToString:@"dylib"])
 | 
			
		||||
@ -588,28 +685,9 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// chown 0 all root binaries
 | 
			
		||||
	NSDictionary* mainInfoDictionary = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]];
 | 
			
		||||
	if(!mainInfoDictionary) return 172;
 | 
			
		||||
	NSObject* tsRootBinaries = mainInfoDictionary[@"TSRootBinaries"];
 | 
			
		||||
	if([tsRootBinaries isKindOfClass:[NSArray class]])
 | 
			
		||||
	{
 | 
			
		||||
		NSArray* tsRootBinariesArr = (NSArray*)tsRootBinaries;
 | 
			
		||||
		for(NSObject* rootBinary in tsRootBinariesArr)
 | 
			
		||||
		{
 | 
			
		||||
			if([rootBinary isKindOfClass:[NSString class]])
 | 
			
		||||
			{
 | 
			
		||||
				NSString* rootBinaryStr = (NSString*)rootBinary;
 | 
			
		||||
				NSString* rootBinaryPath = [appPath stringByAppendingPathComponent:rootBinaryStr];
 | 
			
		||||
				if([[NSFileManager defaultManager] fileExistsAtPath:rootBinaryPath])
 | 
			
		||||
				{
 | 
			
		||||
					chmod(rootBinaryPath.UTF8String, 0755);
 | 
			
		||||
					chown(rootBinaryPath.UTF8String, 0, 0);
 | 
			
		||||
					NSLog(@"[installApp] applying permissions for root binary %@", rootBinaryPath);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Set .app directory permissions too
 | 
			
		||||
	chmod(appPath.UTF8String, 0755);
 | 
			
		||||
	chown(appPath.UTF8String, 33, 33);
 | 
			
		||||
 | 
			
		||||
	// Wipe old version if needed
 | 
			
		||||
	if(existed)
 | 
			
		||||
@ -634,35 +712,45 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
 | 
			
		||||
	NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent];
 | 
			
		||||
	NSLog(@"[installApp] new app path: %@", newAppPath);
 | 
			
		||||
	
 | 
			
		||||
	BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error];
 | 
			
		||||
	NSError* copyError;
 | 
			
		||||
	BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:©Error];
 | 
			
		||||
	if(suc)
 | 
			
		||||
	{
 | 
			
		||||
		NSLog(@"[installApp] app installed, adding to icon cache now...");
 | 
			
		||||
		NSLog(@"[installApp] App %@ installed, adding to icon cache now...", appId);
 | 
			
		||||
		registerPath((char*)newAppPath.UTF8String, 0);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		return 1;
 | 
			
		||||
		NSLog(@"[installApp] Failed to copy app bundle for app %@, error: %@", appId, copyError);
 | 
			
		||||
		return 178;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uninstallApp(NSString* appPath, NSString* appId, NSError** error)
 | 
			
		||||
int uninstallApp(NSString* appPath, NSString* appId)
 | 
			
		||||
{
 | 
			
		||||
	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(@"deleting %@", containerPath);
 | 
			
		||||
		NSLog(@"[uninstallApp] deleting %@", containerPath);
 | 
			
		||||
		// delete app container path
 | 
			
		||||
		[[NSFileManager defaultManager] removeItemAtPath:containerPath error:error];
 | 
			
		||||
		[[NSFileManager defaultManager] removeItemAtPath:containerPath error:nil];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// delete group container paths
 | 
			
		||||
	[[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupID, NSURL* groupURL, BOOL* stop)
 | 
			
		||||
	[[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupId, NSURL* groupURL, BOOL* stop)
 | 
			
		||||
	{
 | 
			
		||||
		NSLog(@"deleting %@", groupURL);
 | 
			
		||||
		// If another app still has this group, don't delete it
 | 
			
		||||
		NSArray<LSApplicationProxy*>* 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];
 | 
			
		||||
	}];
 | 
			
		||||
 | 
			
		||||
@ -672,17 +760,17 @@ int uninstallApp(NSString* appPath, NSString* appId, NSError** error)
 | 
			
		||||
		NSURL* pluginURL = pluginProxy.dataContainerURL;
 | 
			
		||||
		if(pluginURL)
 | 
			
		||||
		{
 | 
			
		||||
			NSLog(@"deleting %@", pluginURL);
 | 
			
		||||
			[[NSFileManager defaultManager] removeItemAtURL:pluginURL error:error];
 | 
			
		||||
			NSLog(@"[uninstallApp] deleting %@", pluginURL);
 | 
			
		||||
			[[NSFileManager defaultManager] removeItemAtURL:pluginURL error:nil];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// unregister app
 | 
			
		||||
	registerPath((char*)appPath.UTF8String, 1);
 | 
			
		||||
 | 
			
		||||
	NSLog(@"deleting %@", [appPath stringByDeletingLastPathComponent]);
 | 
			
		||||
	NSLog(@"[uninstallApp] deleting %@", [appPath stringByDeletingLastPathComponent]);
 | 
			
		||||
	// delete app
 | 
			
		||||
	BOOL deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:error];
 | 
			
		||||
	BOOL deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil];
 | 
			
		||||
	if(deleteSuc)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
@ -693,40 +781,65 @@ int uninstallApp(NSString* appPath, NSString* appId, NSError** error)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uninstallAppByPath(NSString* appPath, NSError** error)
 | 
			
		||||
/*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);
 | 
			
		||||
	if(!appId) return 1;
 | 
			
		||||
	return uninstallApp(appPath, appId, error);
 | 
			
		||||
	return uninstallApp(appPath, appId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uninstallAppById(NSString* appId, NSError** error)
 | 
			
		||||
int uninstallAppById(NSString* appId)
 | 
			
		||||
{
 | 
			
		||||
	if(!appId) return 1;
 | 
			
		||||
	NSString* appPath = appPathForAppId(appId, error);
 | 
			
		||||
	NSString* appPath = appPathForAppId(appId);
 | 
			
		||||
	if(!appPath) return 1;
 | 
			
		||||
	return uninstallApp(appPath, appId, error);
 | 
			
		||||
	return uninstallApp(appPath, appId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 166: IPA does not exist or is not accessible
 | 
			
		||||
// 167: IPA does not appear to contain an app
 | 
			
		||||
 | 
			
		||||
int installIpa(NSString* ipaPath, BOOL force, NSError** error)
 | 
			
		||||
int installIpa(NSString* ipaPath, BOOL force)
 | 
			
		||||
{
 | 
			
		||||
	if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166;
 | 
			
		||||
 | 
			
		||||
	BOOL suc = NO;
 | 
			
		||||
	NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
 | 
			
		||||
	
 | 
			
		||||
	suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:error];
 | 
			
		||||
	suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath 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:error];
 | 
			
		||||
	NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpPayloadPath error:nil];
 | 
			
		||||
	if(!items) return 167;
 | 
			
		||||
	
 | 
			
		||||
	NSString* tmpAppPath;
 | 
			
		||||
@ -740,7 +853,7 @@ int installIpa(NSString* ipaPath, BOOL force, NSError** error)
 | 
			
		||||
	}
 | 
			
		||||
	if(!tmpAppPath) return 167;
 | 
			
		||||
	
 | 
			
		||||
	int ret = installApp(tmpAppPath, YES, force, error);
 | 
			
		||||
	int ret = installApp(tmpAppPath, YES, force);
 | 
			
		||||
	
 | 
			
		||||
	[[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil];
 | 
			
		||||
 | 
			
		||||
@ -751,7 +864,7 @@ void uninstallAllApps(void)
 | 
			
		||||
{
 | 
			
		||||
	for(NSString* appPath in trollStoreInstalledAppBundlePaths())
 | 
			
		||||
	{
 | 
			
		||||
		uninstallAppById(appIdForAppPath(appPath), nil);
 | 
			
		||||
		uninstallAppById(appIdForAppPath(appPath));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -815,7 +928,7 @@ BOOL installTrollStore(NSString* pathToTar)
 | 
			
		||||
		_installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return installApp(tmpTrollStore, NO, YES, nil);;
 | 
			
		||||
	return installApp(tmpTrollStore, NO, YES);;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void refreshAppRegistrations()
 | 
			
		||||
@ -943,7 +1056,6 @@ int main(int argc, char *argv[], char *envp[]) {
 | 
			
		||||
		[mcmBundle load];
 | 
			
		||||
 | 
			
		||||
		int ret = 0;
 | 
			
		||||
		NSError* error;
 | 
			
		||||
 | 
			
		||||
		NSString* cmd = [NSString stringWithUTF8String:argv[1]];
 | 
			
		||||
		if([cmd isEqualToString:@"install"])
 | 
			
		||||
@ -960,17 +1072,22 @@ int main(int argc, char *argv[], char *envp[]) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			NSString* ipaPath = [NSString stringWithUTF8String:argv[2]];
 | 
			
		||||
			ret = installIpa(ipaPath, force, &error);
 | 
			
		||||
			ret = installIpa(ipaPath, force);
 | 
			
		||||
		} else if([cmd isEqualToString:@"uninstall"])
 | 
			
		||||
		{
 | 
			
		||||
			if(argc <= 2) return -3;
 | 
			
		||||
			NSString* appId = [NSString stringWithUTF8String:argv[2]];
 | 
			
		||||
			ret = uninstallAppById(appId, &error);
 | 
			
		||||
		} else if([cmd isEqualToString:@"uninstall-path"])
 | 
			
		||||
			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"])
 | 
			
		||||
		{
 | 
			
		||||
			if(argc <= 2) return -3;
 | 
			
		||||
			NSString* appPath = [NSString stringWithUTF8String:argv[2]];
 | 
			
		||||
			ret = uninstallAppByPath(appPath, &error);
 | 
			
		||||
			ret = uninstallAppByPath(appPath);
 | 
			
		||||
		}else if([cmd isEqualToString:@"install-trollstore"])
 | 
			
		||||
		{
 | 
			
		||||
			if(argc <= 2) return -3;
 | 
			
		||||
@ -1003,11 +1120,6 @@ int main(int argc, char *argv[], char *envp[]) {
 | 
			
		||||
			uninstallPersistenceHelper();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(error)
 | 
			
		||||
		{
 | 
			
		||||
			NSLog(@"error: %@", error);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		NSLog(@"returning %d", ret);
 | 
			
		||||
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
@ -142,6 +142,8 @@ void registerPath(char* cPath, int unregister)
 | 
			
		||||
		dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
 | 
			
		||||
		dictToRegister[@"SignatureVersion"] = @132352;
 | 
			
		||||
		dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
 | 
			
		||||
		dictToRegister[@"IsAdHocSigned"] = @YES;
 | 
			
		||||
		dictToRegister[@"LSInstallType"] = @1;
 | 
			
		||||
 | 
			
		||||
		NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);
 | 
			
		||||
		if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier;
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@
 | 
			
		||||
		<string>iPhoneOS</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>CFBundleVersion</key>
 | 
			
		||||
	<string>1.0.7</string>
 | 
			
		||||
	<string>1.0.8</string>
 | 
			
		||||
	<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UIDeviceFamily</key>
 | 
			
		||||
 | 
			
		||||
@ -149,7 +149,7 @@
 | 
			
		||||
 | 
			
		||||
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
 | 
			
		||||
	{
 | 
			
		||||
		spawnRoot(helperPath(), @[@"refresh"]);
 | 
			
		||||
		spawnRoot(helperPath(), @[@"refresh"], nil, nil);
 | 
			
		||||
		respring();
 | 
			
		||||
 | 
			
		||||
		dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
@ -187,7 +187,7 @@
 | 
			
		||||
			NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"];
 | 
			
		||||
			[[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil];
 | 
			
		||||
 | 
			
		||||
			int ret = spawnRoot(helperPath(), @[@"install-trollstore", tarTmpPath]);
 | 
			
		||||
			int ret = spawnRoot(helperPath(), @[@"install-trollstore", tarTmpPath], nil, nil);
 | 
			
		||||
			dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
			{
 | 
			
		||||
				[[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil];
 | 
			
		||||
@ -223,7 +223,7 @@
 | 
			
		||||
 | 
			
		||||
	UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
 | 
			
		||||
	{
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-trollstore"]);
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil);
 | 
			
		||||
		[self reloadSpecifiers];
 | 
			
		||||
	}];
 | 
			
		||||
	[uninstallWarningAlert addAction:continueAction];
 | 
			
		||||
@ -240,7 +240,7 @@
 | 
			
		||||
 | 
			
		||||
	UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
 | 
			
		||||
	{
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-persistence-helper"]);
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}];
 | 
			
		||||
	[uninstallWarningAlert addAction:continueAction];
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
Package: com.opa334.trollstorehelper
 | 
			
		||||
Name: TrollStore Helper
 | 
			
		||||
Version: 1.0.7
 | 
			
		||||
Version: 1.0.8
 | 
			
		||||
Architecture: iphoneos-arm
 | 
			
		||||
Description: Helper utility to install and manage TrollStore!
 | 
			
		||||
Maintainer: opa334
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@
 | 
			
		||||
		<string>iPhoneOS</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>CFBundleVersion</key>
 | 
			
		||||
	<string>1.0.7</string>
 | 
			
		||||
	<string>1.0.8</string>
 | 
			
		||||
	<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UIDeviceFamily</key>
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,48 @@
 | 
			
		||||
    self.tableView.allowsMultipleSelectionDuringEditing = NO;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)showError:(NSError*)error
 | 
			
		||||
{
 | 
			
		||||
    UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
    UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
 | 
			
		||||
    [errorAlert addAction:closeAction];
 | 
			
		||||
    [self presentViewController:errorAlert animated:YES completion:nil];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)uninstallPressedForRowAtIndexPath:(NSIndexPath*)indexPath
 | 
			
		||||
{
 | 
			
		||||
    TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];
 | 
			
		||||
 | 
			
		||||
    NSString* appPath = [appsManager installedAppPaths][indexPath.row];
 | 
			
		||||
    NSString* appId = [appsManager appIdForAppPath:appPath];
 | 
			
		||||
    NSString* appName = [appsManager displayNameForAppPath:appPath];
 | 
			
		||||
 | 
			
		||||
    UIAlertController* confirmAlert = [UIAlertController alertControllerWithTitle:@"Confirm Uninstallation" message:[NSString stringWithFormat:@"Uninstalling the app '%@' will delete the app and all data associated to it.", appName] preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
 | 
			
		||||
    UIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@"Uninstall" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
 | 
			
		||||
    {
 | 
			
		||||
        if(appId)
 | 
			
		||||
        {
 | 
			
		||||
            [appsManager uninstallApp:appId];
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            [appsManager uninstallAppByPath:appPath];
 | 
			
		||||
        }
 | 
			
		||||
    }];
 | 
			
		||||
    [confirmAlert addAction:uninstallAction];
 | 
			
		||||
 | 
			
		||||
    UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
 | 
			
		||||
    [confirmAlert addAction:cancelAction];
 | 
			
		||||
 | 
			
		||||
    [self presentViewController:confirmAlert animated:YES completion:nil];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)deselectRow
 | 
			
		||||
{
 | 
			
		||||
    [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#pragma mark - Table view data source
 | 
			
		||||
 | 
			
		||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
 | 
			
		||||
@ -51,20 +93,50 @@
 | 
			
		||||
 | 
			
		||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
 | 
			
		||||
{
 | 
			
		||||
    if (editingStyle == UITableViewCellEditingStyleDelete)
 | 
			
		||||
    if(editingStyle == UITableViewCellEditingStyleDelete)
 | 
			
		||||
    {
 | 
			
		||||
        NSString* appPath = [[TSApplicationsManager sharedInstance] installedAppPaths][indexPath.row];
 | 
			
		||||
        NSString* appId = [[TSApplicationsManager sharedInstance] appIdForAppPath:appPath];
 | 
			
		||||
        
 | 
			
		||||
        if(appId)
 | 
			
		||||
        {
 | 
			
		||||
            [[TSApplicationsManager sharedInstance] uninstallApp:appId];
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            [[TSApplicationsManager sharedInstance] uninstallAppByPath:appPath];
 | 
			
		||||
        }
 | 
			
		||||
        [self uninstallPressedForRowAtIndexPath:indexPath];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 | 
			
		||||
{
 | 
			
		||||
    TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];
 | 
			
		||||
 | 
			
		||||
    NSString* appPath = [appsManager installedAppPaths][indexPath.row];
 | 
			
		||||
    NSString* appId = [appsManager appIdForAppPath:appPath];
 | 
			
		||||
    NSString* appName = [appsManager displayNameForAppPath:appPath];
 | 
			
		||||
 | 
			
		||||
    UIAlertController* appSelectAlert = [UIAlertController alertControllerWithTitle:appName message:appId preferredStyle:UIAlertControllerStyleActionSheet];
 | 
			
		||||
 | 
			
		||||
    /*UIAlertAction* detachAction = [UIAlertAction actionWithTitle:@"Detach from TrollStore" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
    {
 | 
			
		||||
        int detachRet = [appsManager detachFromApp:appId];
 | 
			
		||||
        if(detachRet != 0)
 | 
			
		||||
        {
 | 
			
		||||
            [self showError:[appsManager errorForCode:detachRet]];
 | 
			
		||||
        }
 | 
			
		||||
        [self deselectRow];
 | 
			
		||||
    }];
 | 
			
		||||
    [appSelectAlert addAction:detachAction];*/
 | 
			
		||||
 | 
			
		||||
    UIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@"Uninstall App" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
 | 
			
		||||
    {
 | 
			
		||||
        [self uninstallPressedForRowAtIndexPath:indexPath];
 | 
			
		||||
        [self deselectRow];
 | 
			
		||||
    }];
 | 
			
		||||
    [appSelectAlert addAction:uninstallAction];
 | 
			
		||||
 | 
			
		||||
    UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)
 | 
			
		||||
    {
 | 
			
		||||
        [self deselectRow];
 | 
			
		||||
    }];
 | 
			
		||||
    [appSelectAlert addAction:cancelAction];
 | 
			
		||||
 | 
			
		||||
	appSelectAlert.popoverPresentationController.sourceView = tableView;
 | 
			
		||||
	appSelectAlert.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath];
 | 
			
		||||
 | 
			
		||||
    [self presentViewController:appSelectAlert animated:YES completion:nil];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
@ -14,9 +14,10 @@
 | 
			
		||||
- (NSString*)displayNameForAppPath:(NSString*)appPath;
 | 
			
		||||
 | 
			
		||||
- (NSError*)errorForCode:(int)code;
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force;
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut;
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa;
 | 
			
		||||
- (int)uninstallApp:(NSString*)appId;
 | 
			
		||||
- (int)uninstallAppByPath:(NSString*)path;
 | 
			
		||||
//- (int)detachFromApp:(NSString*)appId;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
@ -24,7 +24,13 @@
 | 
			
		||||
- (NSDictionary*)infoDictionaryForAppPath:(NSString*)appPath
 | 
			
		||||
{
 | 
			
		||||
    NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"];
 | 
			
		||||
    return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath];
 | 
			
		||||
    NSError* error;
 | 
			
		||||
    NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:infoPlistPath] error:&error];
 | 
			
		||||
    if(error)
 | 
			
		||||
    {
 | 
			
		||||
        NSLog(@"error getting info dict: %@", error);
 | 
			
		||||
    }
 | 
			
		||||
    return infoDict;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSString*)appIdForAppPath:(NSString*)appPath
 | 
			
		||||
@ -35,7 +41,6 @@
 | 
			
		||||
- (NSString*)displayNameForAppPath:(NSString*)appPath
 | 
			
		||||
{
 | 
			
		||||
    NSDictionary* infoDict = [self infoDictionaryForAppPath:appPath];
 | 
			
		||||
    
 | 
			
		||||
    NSString* displayName = infoDict[@"CFBundleDisplayName"];
 | 
			
		||||
    if(![displayName isKindOfClass:[NSString class]]) displayName = nil;
 | 
			
		||||
    if(!displayName || [displayName isEqualToString:@""])
 | 
			
		||||
@ -57,12 +62,14 @@
 | 
			
		||||
    NSString* errorDescription = @"Unknown Error";
 | 
			
		||||
    switch(code)
 | 
			
		||||
    {
 | 
			
		||||
        // IPA install errors
 | 
			
		||||
        case 166:
 | 
			
		||||
        errorDescription = @"The IPA file does not exist or is not accessible.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 167:
 | 
			
		||||
        errorDescription = @"The IPA file does not appear to contain an app.";
 | 
			
		||||
        break;
 | 
			
		||||
        // App install errors
 | 
			
		||||
        case 170:
 | 
			
		||||
        errorDescription = @"Failed to create container for app bundle.";
 | 
			
		||||
        break;
 | 
			
		||||
@ -70,27 +77,46 @@
 | 
			
		||||
        errorDescription = @"A non-TrollStore app with the same identifier is already installed. If you are absolutely sure it is not, you can force install it.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 172:
 | 
			
		||||
        errorDescription = @"The app does not seem to contain an Info.plist";
 | 
			
		||||
        errorDescription = @"The app does not contain an Info.plist file.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 173:
 | 
			
		||||
        errorDescription = @"The app is not signed with a fake CoreTrust certificate and ldid does not seem to be installed. Make sure ldid is installed in the settings tab and try again.";
 | 
			
		||||
        errorDescription = @"The app is not signed with a fake CoreTrust certificate and ldid is not installed. Install ldid in the settings tab and try again.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 174:
 | 
			
		||||
        errorDescription = @"The apps main executable does not exists.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 175:
 | 
			
		||||
        errorDescription = @"Failed to sign the app. ldid returned a non zero status code.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 176:
 | 
			
		||||
        errorDescription = @"The apps Info.plist is missing required values.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 177:
 | 
			
		||||
        errorDescription = @"Failed to mark app as TrollStore app.";
 | 
			
		||||
        break;
 | 
			
		||||
        case 178:
 | 
			
		||||
        errorDescription = @"Failed to copy app bundle.";
 | 
			
		||||
        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;*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}];
 | 
			
		||||
    return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    if(force)
 | 
			
		||||
    {
 | 
			
		||||
        ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"]);
 | 
			
		||||
        ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"], nil, logOut);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        ret = spawnRoot(helperPath(), @[@"install", pathToIpa]);
 | 
			
		||||
        ret = spawnRoot(helperPath(), @[@"install", pathToIpa], nil, logOut);
 | 
			
		||||
    }
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
 | 
			
		||||
    return ret;
 | 
			
		||||
@ -98,13 +124,13 @@
 | 
			
		||||
 | 
			
		||||
- (int)installIpa:(NSString*)pathToIpa
 | 
			
		||||
{
 | 
			
		||||
    return [self installIpa:pathToIpa force:NO];
 | 
			
		||||
    return [self installIpa:pathToIpa force:NO log:nil];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)uninstallApp:(NSString*)appId
 | 
			
		||||
{
 | 
			
		||||
    if(!appId) return -200;
 | 
			
		||||
    int ret = spawnRoot(helperPath(), @[@"uninstall", appId]);
 | 
			
		||||
    int ret = spawnRoot(helperPath(), @[@"uninstall", appId], nil, nil);
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@ -112,9 +138,17 @@
 | 
			
		||||
- (int)uninstallAppByPath:(NSString*)path
 | 
			
		||||
{
 | 
			
		||||
    if(!path) return -200;
 | 
			
		||||
    int ret = spawnRoot(helperPath(), @[@"uninstall-path", path]);
 | 
			
		||||
    int ret = spawnRoot(helperPath(), @[@"uninstall-path", path], nil, nil);
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*- (int)detachFromApp:(NSString*)appId
 | 
			
		||||
{
 | 
			
		||||
    if(!appId) return -200;
 | 
			
		||||
    int ret = spawnRoot(helperPath(), @[@"detach", appId], nil, nil);
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
 | 
			
		||||
    return ret;
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
@ -7,163 +7,180 @@
 | 
			
		||||
 | 
			
		||||
- (void)doIPAInstall:(NSString*)ipaPath scene:(UIWindowScene*)scene force:(BOOL)force completion:(void (^)(void))completion
 | 
			
		||||
{
 | 
			
		||||
    UIWindow* keyWindow = nil;
 | 
			
		||||
    for(UIWindow* window in scene.windows)
 | 
			
		||||
    {
 | 
			
		||||
        if(window.isKeyWindow)
 | 
			
		||||
        {
 | 
			
		||||
            keyWindow = window;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	UIWindow* keyWindow = nil;
 | 
			
		||||
	for(UIWindow* window in scene.windows)
 | 
			
		||||
	{
 | 
			
		||||
		if(window.isKeyWindow)
 | 
			
		||||
		{
 | 
			
		||||
			keyWindow = window;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
    UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];
 | 
			
		||||
    activityIndicator.hidesWhenStopped = YES;
 | 
			
		||||
    activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
 | 
			
		||||
    [activityIndicator startAnimating];
 | 
			
		||||
    [infoAlert.view addSubview:activityIndicator];
 | 
			
		||||
	UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
	UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];
 | 
			
		||||
	activityIndicator.hidesWhenStopped = YES;
 | 
			
		||||
	activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
 | 
			
		||||
	[activityIndicator startAnimating];
 | 
			
		||||
	[infoAlert.view addSubview:activityIndicator];
 | 
			
		||||
 | 
			
		||||
    [keyWindow.rootViewController presentViewController:infoAlert animated:YES completion:nil];
 | 
			
		||||
	[keyWindow.rootViewController presentViewController:infoAlert animated:YES completion:nil];
 | 
			
		||||
 | 
			
		||||
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
 | 
			
		||||
    {
 | 
			
		||||
        // Install IPA
 | 
			
		||||
        int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force];
 | 
			
		||||
        NSError* error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
 | 
			
		||||
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
 | 
			
		||||
	{
 | 
			
		||||
		// Install IPA
 | 
			
		||||
		//NSString* log;
 | 
			
		||||
		int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force log:nil];
 | 
			
		||||
 | 
			
		||||
        NSLog(@"installed app! ret:%d, error: %@", ret, error);
 | 
			
		||||
        dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
        {
 | 
			
		||||
            [infoAlert dismissViewControllerAnimated:YES completion:^
 | 
			
		||||
            {
 | 
			
		||||
                if(ret != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
                    UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
                    {
 | 
			
		||||
                        if(ret == 171)
 | 
			
		||||
                        {
 | 
			
		||||
                            completion();
 | 
			
		||||
                        }
 | 
			
		||||
                    }];
 | 
			
		||||
                    if(ret == 171)
 | 
			
		||||
                    {
 | 
			
		||||
                        UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
                        {
 | 
			
		||||
                            [self doIPAInstall:ipaPath scene:scene force:YES completion:completion];
 | 
			
		||||
                        }];
 | 
			
		||||
                        [errorAlert addAction:forceInstallAction];
 | 
			
		||||
                    }
 | 
			
		||||
                    [errorAlert addAction:closeAction];
 | 
			
		||||
		NSError* error;
 | 
			
		||||
		if(ret != 0)
 | 
			
		||||
		{
 | 
			
		||||
			error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
                    [keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil];
 | 
			
		||||
                }
 | 
			
		||||
		NSLog(@"installed app! ret:%d, error: %@", ret, error);
 | 
			
		||||
 | 
			
		||||
                if(ret != 171)
 | 
			
		||||
                {
 | 
			
		||||
                    completion();
 | 
			
		||||
                }
 | 
			
		||||
            }];
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
		dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
		{
 | 
			
		||||
			[infoAlert dismissViewControllerAnimated:YES completion:^
 | 
			
		||||
			{
 | 
			
		||||
				if(ret != 0)
 | 
			
		||||
				{
 | 
			
		||||
					UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
 | 
			
		||||
					UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
					{
 | 
			
		||||
						if(ret == 171)
 | 
			
		||||
						{
 | 
			
		||||
							completion();
 | 
			
		||||
						}
 | 
			
		||||
					}];
 | 
			
		||||
					[errorAlert addAction:closeAction];
 | 
			
		||||
 | 
			
		||||
					if(ret == 171)
 | 
			
		||||
					{
 | 
			
		||||
						UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
						{
 | 
			
		||||
							[self doIPAInstall:ipaPath scene:scene force:YES completion:completion];
 | 
			
		||||
						}];
 | 
			
		||||
						[errorAlert addAction:forceInstallAction];
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						/*UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
						{
 | 
			
		||||
							UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
 | 
			
		||||
							pasteboard.string = log;
 | 
			
		||||
						}];
 | 
			
		||||
						[errorAlert addAction:copyLogAction];*/
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					[keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil];
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if(ret != 171)
 | 
			
		||||
				{
 | 
			
		||||
					completion();
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)handleURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts scene:(UIWindowScene*)scene
 | 
			
		||||
{
 | 
			
		||||
    for(UIOpenURLContext* context in URLContexts)
 | 
			
		||||
    {
 | 
			
		||||
        NSLog(@"openURLContexts %@", context.URL);
 | 
			
		||||
        NSURL* url = context.URL;
 | 
			
		||||
        if (url != nil && [url isFileURL]) {
 | 
			
		||||
            [url startAccessingSecurityScopedResource];
 | 
			
		||||
            void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
 | 
			
		||||
            {
 | 
			
		||||
                [url stopAccessingSecurityScopedResource];
 | 
			
		||||
                [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
 | 
			
		||||
	for(UIOpenURLContext* context in URLContexts)
 | 
			
		||||
	{
 | 
			
		||||
		NSLog(@"openURLContexts %@", context.URL);
 | 
			
		||||
		NSURL* url = context.URL;
 | 
			
		||||
		if (url != nil && [url isFileURL]) {
 | 
			
		||||
			[url startAccessingSecurityScopedResource];
 | 
			
		||||
			void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
 | 
			
		||||
			{
 | 
			
		||||
				[url stopAccessingSecurityScopedResource];
 | 
			
		||||
				[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
 | 
			
		||||
 | 
			
		||||
                if(shouldExit)
 | 
			
		||||
                {
 | 
			
		||||
                    NSLog(@"Respring + Exit");
 | 
			
		||||
                    respring();
 | 
			
		||||
                    exit(0);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            
 | 
			
		||||
            if ([url.pathExtension isEqualToString:@"ipa"])
 | 
			
		||||
            {
 | 
			
		||||
                [self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
 | 
			
		||||
                    doneBlock(NO);
 | 
			
		||||
                }];
 | 
			
		||||
            }
 | 
			
		||||
            else if([url.pathExtension isEqualToString:@"tar"])
 | 
			
		||||
            {
 | 
			
		||||
                // Update TrollStore itself
 | 
			
		||||
                NSLog(@"Updating TrollStore...");
 | 
			
		||||
                int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path]);
 | 
			
		||||
                doneBlock(ret == 0);
 | 
			
		||||
                NSLog(@"Updated TrollStore!");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
				if(shouldExit)
 | 
			
		||||
				{
 | 
			
		||||
					NSLog(@"Respring + Exit");
 | 
			
		||||
					respring();
 | 
			
		||||
					exit(0);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			
 | 
			
		||||
			if ([url.pathExtension isEqualToString:@"ipa"])
 | 
			
		||||
			{
 | 
			
		||||
				[self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
 | 
			
		||||
					doneBlock(NO);
 | 
			
		||||
				}];
 | 
			
		||||
			}
 | 
			
		||||
			else if([url.pathExtension isEqualToString:@"tar"])
 | 
			
		||||
			{
 | 
			
		||||
				// Update TrollStore itself
 | 
			
		||||
				NSLog(@"Updating TrollStore...");
 | 
			
		||||
				int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path], nil, nil);
 | 
			
		||||
				doneBlock(ret == 0);
 | 
			
		||||
				NSLog(@"Updated TrollStore!");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (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`.
 | 
			
		||||
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
 | 
			
		||||
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
 | 
			
		||||
    
 | 
			
		||||
    NSLog(@"scene:%@ willConnectToSession:%@ options:%@", scene, session, connectionOptions);
 | 
			
		||||
    UIWindowScene* windowScene = (UIWindowScene*)scene;
 | 
			
		||||
    _window = [[UIWindow alloc] initWithWindowScene:windowScene];
 | 
			
		||||
    _rootViewController = [[TSRootViewController alloc] init];
 | 
			
		||||
    _window.rootViewController = _rootViewController;
 | 
			
		||||
	// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
 | 
			
		||||
	// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
 | 
			
		||||
	// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
 | 
			
		||||
	
 | 
			
		||||
	NSLog(@"scene:%@ willConnectToSession:%@ options:%@", scene, session, connectionOptions);
 | 
			
		||||
	UIWindowScene* windowScene = (UIWindowScene*)scene;
 | 
			
		||||
	_window = [[UIWindow alloc] initWithWindowScene:windowScene];
 | 
			
		||||
	_rootViewController = [[TSRootViewController alloc] init];
 | 
			
		||||
	_window.rootViewController = _rootViewController;
 | 
			
		||||
	[_window makeKeyAndVisible];
 | 
			
		||||
 | 
			
		||||
    if(connectionOptions.URLContexts.count)
 | 
			
		||||
    {
 | 
			
		||||
        [self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];
 | 
			
		||||
    }
 | 
			
		||||
	if(connectionOptions.URLContexts.count)
 | 
			
		||||
	{
 | 
			
		||||
		[self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)sceneDidDisconnect:(UIScene *)scene {
 | 
			
		||||
    // Called as the scene is being released by the system.
 | 
			
		||||
    // This occurs shortly after the scene enters the background, or when its session is discarded.
 | 
			
		||||
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
 | 
			
		||||
    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
 | 
			
		||||
	// Called as the scene is being released by the system.
 | 
			
		||||
	// This occurs shortly after the scene enters the background, or when its session is discarded.
 | 
			
		||||
	// Release any resources associated with this scene that can be re-created the next time the scene connects.
 | 
			
		||||
	// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)sceneDidBecomeActive:(UIScene *)scene {
 | 
			
		||||
    // Called when the scene has moved from an inactive state to an active state.
 | 
			
		||||
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
 | 
			
		||||
	// Called when the scene has moved from an inactive state to an active state.
 | 
			
		||||
	// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)sceneWillResignActive:(UIScene *)scene {
 | 
			
		||||
    // Called when the scene will move from an active state to an inactive state.
 | 
			
		||||
    // This may occur due to temporary interruptions (ex. an incoming phone call).
 | 
			
		||||
	// Called when the scene will move from an active state to an inactive state.
 | 
			
		||||
	// This may occur due to temporary interruptions (ex. an incoming phone call).
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)sceneWillEnterForeground:(UIScene *)scene {
 | 
			
		||||
    // Called as the scene transitions from the background to the foreground.
 | 
			
		||||
    // Use this method to undo the changes made on entering the background.
 | 
			
		||||
	// Called as the scene transitions from the background to the foreground.
 | 
			
		||||
	// Use this method to undo the changes made on entering the background.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)sceneDidEnterBackground:(UIScene *)scene {
 | 
			
		||||
    // Called as the scene transitions from the foreground to the background.
 | 
			
		||||
    // Use this method to save data, release shared resources, and store enough scene-specific state information
 | 
			
		||||
    // to restore the scene back to its current state.
 | 
			
		||||
	// Called as the scene transitions from the foreground to the background.
 | 
			
		||||
	// Use this method to save data, release shared resources, and store enough scene-specific state information
 | 
			
		||||
	// to restore the scene back to its current state.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
 | 
			
		||||
{
 | 
			
		||||
    NSLog(@"scene:%@ openURLContexts:%@", scene, URLContexts);
 | 
			
		||||
    [self handleURLContexts:URLContexts scene:(UIWindowScene*)scene];
 | 
			
		||||
	NSLog(@"scene:%@ openURLContexts:%@", scene, URLContexts);
 | 
			
		||||
	[self handleURLContexts:URLContexts scene:(UIWindowScene*)scene];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
@ -225,7 +225,7 @@
 | 
			
		||||
 | 
			
		||||
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
 | 
			
		||||
	{
 | 
			
		||||
		spawnRoot(helperPath(), @[@"refresh-all"]);
 | 
			
		||||
		spawnRoot(helperPath(), @[@"refresh-all"], nil, nil);
 | 
			
		||||
 | 
			
		||||
		dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
		{
 | 
			
		||||
@ -259,7 +259,7 @@
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			spawnRoot(helperPath(), @[@"install-ldid", location.path]);
 | 
			
		||||
			spawnRoot(helperPath(), @[@"install-ldid", location.path], nil, nil);
 | 
			
		||||
			dispatch_async(dispatch_get_main_queue(), ^
 | 
			
		||||
			{
 | 
			
		||||
				[self stopActivityWithCompletion:nil];
 | 
			
		||||
@ -294,7 +294,7 @@
 | 
			
		||||
	{
 | 
			
		||||
		UIAlertAction* installAction = [UIAlertAction actionWithTitle:[appProxy localizedName] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
 | 
			
		||||
		{
 | 
			
		||||
			spawnRoot(helperPath(), @[@"install-persistence-helper", appProxy.bundleIdentifier]);
 | 
			
		||||
			spawnRoot(helperPath(), @[@"install-persistence-helper", appProxy.bundleIdentifier], nil, nil);
 | 
			
		||||
			[self reloadSpecifiers];
 | 
			
		||||
		}];
 | 
			
		||||
 | 
			
		||||
@ -314,7 +314,7 @@
 | 
			
		||||
 | 
			
		||||
- (void)uninstallPersistenceHelperPressed
 | 
			
		||||
{
 | 
			
		||||
	spawnRoot(helperPath(), @[@"uninstall-persistence-helper"]);
 | 
			
		||||
	spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil);
 | 
			
		||||
	[self reloadSpecifiers];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -327,7 +327,7 @@
 | 
			
		||||
 | 
			
		||||
	UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
 | 
			
		||||
	{
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-trollstore"]);
 | 
			
		||||
		spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}];
 | 
			
		||||
	[uninstallWarningAlert addAction:continueAction];
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
@import Foundation;
 | 
			
		||||
 | 
			
		||||
extern NSString* helperPath(void);
 | 
			
		||||
extern int spawnRoot(NSString* path, NSArray* args);
 | 
			
		||||
extern void printMultilineNSString(NSString* stringToPrint);
 | 
			
		||||
extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr);
 | 
			
		||||
extern void respring(void);
 | 
			
		||||
extern NSString* getTrollStoreVersion(void);
 | 
			
		||||
@ -14,7 +14,29 @@ NSString* helperPath(void)
 | 
			
		||||
	return [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"trollstorehelper"];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int spawnRoot(NSString* path, NSArray* args)
 | 
			
		||||
NSString* getNSStringFromFile(int fd)
 | 
			
		||||
{
 | 
			
		||||
	NSMutableString* ms = [NSMutableString new];
 | 
			
		||||
	ssize_t num_read;
 | 
			
		||||
	char c;
 | 
			
		||||
	while((num_read = read(fd, &c, sizeof(c))))
 | 
			
		||||
	{
 | 
			
		||||
		[ms appendString:[NSString stringWithFormat:@"%c", c]];
 | 
			
		||||
	}
 | 
			
		||||
	return ms.copy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printMultilineNSString(NSString* stringToPrint)
 | 
			
		||||
{
 | 
			
		||||
	NSCharacterSet *separator = [NSCharacterSet newlineCharacterSet];
 | 
			
		||||
	NSArray* lines = [stringToPrint componentsSeparatedByCharactersInSet:separator];
 | 
			
		||||
	for(NSString* line in lines)
 | 
			
		||||
	{
 | 
			
		||||
		NSLog(@"%@", line);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr)
 | 
			
		||||
{
 | 
			
		||||
	NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];
 | 
			
		||||
	[argsM insertObject:path.lastPathComponent atIndex:0];
 | 
			
		||||
@ -28,18 +50,35 @@ int spawnRoot(NSString* path, NSArray* args)
 | 
			
		||||
	}
 | 
			
		||||
	argsC[argCount] = NULL;
 | 
			
		||||
 | 
			
		||||
	int rv;
 | 
			
		||||
	posix_spawnattr_t attr;
 | 
			
		||||
	rv = posix_spawnattr_init(&attr);
 | 
			
		||||
	if(rv != 0) return rv;
 | 
			
		||||
	posix_spawnattr_init(&attr);
 | 
			
		||||
 | 
			
		||||
	posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
 | 
			
		||||
	posix_spawnattr_set_persona_uid_np(&attr, 0);
 | 
			
		||||
	posix_spawnattr_set_persona_gid_np(&attr, 0);
 | 
			
		||||
 | 
			
		||||
	posix_spawn_file_actions_t action;
 | 
			
		||||
	posix_spawn_file_actions_init(&action);
 | 
			
		||||
 | 
			
		||||
	int outErr[2];
 | 
			
		||||
	if(stdErr)
 | 
			
		||||
	{
 | 
			
		||||
		pipe(outErr);
 | 
			
		||||
		posix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);
 | 
			
		||||
		posix_spawn_file_actions_addclose(&action, outErr[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int out[2];
 | 
			
		||||
	if(stdOut)
 | 
			
		||||
	{
 | 
			
		||||
		pipe(out);
 | 
			
		||||
		posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
 | 
			
		||||
		posix_spawn_file_actions_addclose(&action, out[0]);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	pid_t task_pid;
 | 
			
		||||
	int status = -200;
 | 
			
		||||
	int spawnError = posix_spawn(&task_pid, [path UTF8String], NULL, &attr, (char* const*)argsC, NULL);
 | 
			
		||||
	int spawnError = posix_spawn(&task_pid, [path UTF8String], &action, &attr, (char* const*)argsC, NULL);
 | 
			
		||||
	posix_spawnattr_destroy(&attr);
 | 
			
		||||
	for (NSUInteger i = 0; i < argCount; i++)
 | 
			
		||||
	{
 | 
			
		||||
@ -63,6 +102,20 @@ int spawnRoot(NSString* path, NSArray* args)
 | 
			
		||||
			return -222;
 | 
			
		||||
		}
 | 
			
		||||
	} while (!WIFEXITED(status) && !WIFSIGNALED(status));
 | 
			
		||||
 | 
			
		||||
	if(stdOut)
 | 
			
		||||
	{
 | 
			
		||||
		close(out[1]);
 | 
			
		||||
		NSString* output = getNSStringFromFile(out[0]);
 | 
			
		||||
		*stdOut = output;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(stdErr)
 | 
			
		||||
	{
 | 
			
		||||
		close(outErr[1]);
 | 
			
		||||
		NSString* errorOutput = getNSStringFromFile(outErr[0]);
 | 
			
		||||
		*stdErr = errorOutput;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return WEXITSTATUS(status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
Package: com.opa334.trollstore
 | 
			
		||||
Name: TrollStore
 | 
			
		||||
Version: 1.0.7
 | 
			
		||||
Version: 1.0.8
 | 
			
		||||
Architecture: iphoneos-arm
 | 
			
		||||
Description: An awesome application!
 | 
			
		||||
Maintainer: opa334
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user