mirror of https://github.com/opa334/TrollStore.git
1.0.8
This commit is contained in:
parent
ab3261f0f2
commit
5d474901a2
|
@ -13,7 +13,8 @@
|
||||||
@property (nonatomic,readonly) NSArray* plugInKitPlugins;
|
@property (nonatomic,readonly) NSArray* plugInKitPlugins;
|
||||||
@property (getter=isInstalled,nonatomic,readonly) BOOL installed;
|
@property (getter=isInstalled,nonatomic,readonly) BOOL installed;
|
||||||
@property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder;
|
@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
|
@end
|
||||||
|
|
||||||
@interface LSApplicationWorkspace : NSObject
|
@interface LSApplicationWorkspace : NSObject
|
||||||
|
@ -25,6 +26,11 @@
|
||||||
- (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block;
|
- (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface LSEnumerator : NSEnumerator
|
||||||
|
@property (nonatomic,copy) NSPredicate * predicate;
|
||||||
|
+ (instancetype)enumeratorForApplicationProxiesWithOptions:(NSUInteger)options;
|
||||||
|
@end
|
||||||
|
|
||||||
@interface LSPlugInKitProxy : LSBundleProxy
|
@interface LSPlugInKitProxy : LSBundleProxy
|
||||||
@property (nonatomic,readonly) NSString* pluginIdentifier;
|
@property (nonatomic,readonly) NSString* pluginIdentifier;
|
||||||
@property (nonatomic,readonly) NSDictionary * pluginKitDictionary;
|
@property (nonatomic,readonly) NSDictionary * pluginKitDictionary;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstoreroothelper
|
Package: com.opa334.trollstoreroothelper
|
||||||
Name: trollstoreroothelper
|
Name: trollstoreroothelper
|
||||||
Version: 1.0.6
|
Version: 1.0.8
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Description: An awesome tool of some sort!!
|
Description: An awesome tool of some sort!!
|
||||||
Maintainer: opa334
|
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);
|
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();
|
extern char*** _NSGetArgv();
|
||||||
NSString* safe_getExecutablePath()
|
NSString* safe_getExecutablePath()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +94,12 @@ NSString* appIdForAppPath(NSString* appPath)
|
||||||
return infoDictionaryForAppPath(appPath)[@"CFBundleIdentifier"];
|
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())
|
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
||||||
{
|
{
|
||||||
|
@ -411,15 +438,15 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef)
|
||||||
return evaluatesToCustomAnchor;
|
return evaluatesToCustomAnchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL signApp(NSString* appPath, NSError** error)
|
int signApp(NSString* appPath)
|
||||||
{
|
{
|
||||||
NSDictionary* appInfoDict = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]];
|
NSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);
|
||||||
if(!appInfoDict) return NO;
|
if(!appInfoDict) return 172;
|
||||||
|
|
||||||
NSString* executable = appInfoDict[@"CFBundleExecutable"];
|
NSString* executablePath = appMainExecutablePathForAppPath(appPath);
|
||||||
NSString* executablePath = [appPath stringByAppendingPathComponent:executable];
|
if(!executablePath) return 176;
|
||||||
|
|
||||||
if(![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) return NO;
|
if(![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) return 174;
|
||||||
|
|
||||||
NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"];
|
NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"];
|
||||||
if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]])
|
if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]])
|
||||||
|
@ -430,7 +457,7 @@ BOOL signApp(NSString* appPath, NSError** error)
|
||||||
if([tsBundleIsPreSignedNum boolValue] == YES)
|
if([tsBundleIsPreSignedNum boolValue] == YES)
|
||||||
{
|
{
|
||||||
NSLog(@"[signApp] taking fast path for app which declares it has already been signed (%@)", executablePath);
|
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);
|
NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath);
|
||||||
CFRelease(codeRef);
|
CFRelease(codeRef);
|
||||||
return YES;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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);
|
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* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"];
|
||||||
NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath];
|
NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath];
|
||||||
|
@ -481,7 +508,14 @@ BOOL signApp(NSString* appPath, NSError** error)
|
||||||
|
|
||||||
NSLog(@"- ldid error output end -");
|
NSLog(@"- ldid error output end -");
|
||||||
|
|
||||||
return ldidRet == 0;
|
if(ldidRet == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 175;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyPatchesToInfoDictionary(NSString* appPath)
|
void applyPatchesToInfoDictionary(NSString* appPath)
|
||||||
|
@ -491,9 +525,34 @@ void applyPatchesToInfoDictionary(NSString* appPath)
|
||||||
NSMutableDictionary* infoDictM = [[NSDictionary dictionaryWithContentsOfURL:infoPlistURL error:nil] mutableCopy];
|
NSMutableDictionary* infoDictM = [[NSDictionary dictionaryWithContentsOfURL:infoPlistURL error:nil] mutableCopy];
|
||||||
if(!infoDictM) return;
|
if(!infoDictM) return;
|
||||||
|
|
||||||
// enable notifications
|
// Enable Notifications
|
||||||
infoDictM[@"SBAppUsesLocalNotifications"] = @1;
|
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];
|
[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
|
// 171: a non trollstore app with the same identifier is already installled
|
||||||
// 172: no info.plist found in app
|
// 172: no info.plist found in app
|
||||||
// 173: app is not signed and cannot be signed because ldid not installed or didn't work
|
// 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);
|
NSLog(@"[installApp force = %d]", force);
|
||||||
|
|
||||||
|
if(!infoDictionaryForAppPath(appPath)) return 172;
|
||||||
|
|
||||||
NSString* appId = appIdForAppPath(appPath);
|
NSString* appId = appIdForAppPath(appPath);
|
||||||
if(!appId) return 172;
|
if(!appId) return 176;
|
||||||
|
|
||||||
applyPatchesToInfoDictionary(appPath);
|
applyPatchesToInfoDictionary(appPath);
|
||||||
|
|
||||||
if(sign)
|
if(sign)
|
||||||
{
|
{
|
||||||
if(!signApp(appPath, error)) return 173;
|
int signRet = signApp(appPath);
|
||||||
|
if(signRet != 0) return signRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL existed;
|
BOOL existed;
|
||||||
NSError* mcmError;
|
NSError* mcmError;
|
||||||
MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError];
|
MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError];
|
||||||
NSLog(@"[installApp] appContainer: %@, mcmError: %@", appContainer, mcmError);
|
|
||||||
if(!appContainer || mcmError)
|
if(!appContainer || mcmError)
|
||||||
{
|
{
|
||||||
if(error) *error = mcmError;
|
NSLog(@"[installApp] failed to create app container for %@: %@", appId, mcmError);
|
||||||
return 170;
|
return 170;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(existed)
|
||||||
|
{
|
||||||
|
NSLog(@"[installApp] got existing app container: %@", appContainer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog(@"[installApp] created app container: %@", appContainer);
|
||||||
|
}
|
||||||
|
|
||||||
// check if the bundle is empty
|
// check if the bundle is empty
|
||||||
BOOL isEmpty = YES;
|
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)
|
for(NSString* bundleItem in bundleItems)
|
||||||
{
|
{
|
||||||
if([bundleItem.pathExtension isEqualToString:@"app"])
|
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
|
// Make sure there isn't already an app store app installed with the same identifier
|
||||||
NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"];
|
NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"];
|
||||||
if(existed && !isEmpty && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force)
|
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
|
// 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)
|
// Apply correct permissions (First run, set everything to 644, owner 33)
|
||||||
NSURL* fileURL;
|
NSURL* fileURL;
|
||||||
|
@ -556,6 +634,7 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
|
||||||
NSString* filePath = fileURL.path;
|
NSString* filePath = fileURL.path;
|
||||||
chown(filePath.UTF8String, 33, 33);
|
chown(filePath.UTF8String, 33, 33);
|
||||||
chmod(filePath.UTF8String, 0644);
|
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)
|
// 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];
|
NSDictionary* infoDictionary = [NSDictionary dictionaryWithContentsOfFile:filePath];
|
||||||
NSString* executable = infoDictionary[@"CFBundleExecutable"];
|
NSString* executable = infoDictionary[@"CFBundleExecutable"];
|
||||||
if(executable)
|
if(executable && [executable isKindOfClass:[NSString class]])
|
||||||
{
|
{
|
||||||
NSString* executablePath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:executable];
|
NSString* executablePath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:executable];
|
||||||
chmod(executablePath.UTF8String, 0755);
|
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"])
|
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
|
// Set .app directory permissions too
|
||||||
NSDictionary* mainInfoDictionary = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]];
|
chmod(appPath.UTF8String, 0755);
|
||||||
if(!mainInfoDictionary) return 172;
|
chown(appPath.UTF8String, 33, 33);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wipe old version if needed
|
// Wipe old version if needed
|
||||||
if(existed)
|
if(existed)
|
||||||
|
@ -634,35 +712,45 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
|
||||||
NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent];
|
NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent];
|
||||||
NSLog(@"[installApp] new app path: %@", newAppPath);
|
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)
|
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);
|
registerPath((char*)newAppPath.UTF8String, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
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];
|
LSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:appId];
|
||||||
MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil];
|
MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil];
|
||||||
NSString *containerPath = [appContainer url].path;
|
NSString *containerPath = [appContainer url].path;
|
||||||
if(containerPath)
|
if(containerPath)
|
||||||
{
|
{
|
||||||
NSLog(@"deleting %@", containerPath);
|
NSLog(@"[uninstallApp] deleting %@", containerPath);
|
||||||
// delete app container path
|
// delete app container path
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:containerPath error:error];
|
[[NSFileManager defaultManager] removeItemAtPath:containerPath error:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete group container paths
|
// 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];
|
[[NSFileManager defaultManager] removeItemAtURL:groupURL error:nil];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -672,17 +760,17 @@ int uninstallApp(NSString* appPath, NSString* appId, NSError** error)
|
||||||
NSURL* pluginURL = pluginProxy.dataContainerURL;
|
NSURL* pluginURL = pluginProxy.dataContainerURL;
|
||||||
if(pluginURL)
|
if(pluginURL)
|
||||||
{
|
{
|
||||||
NSLog(@"deleting %@", pluginURL);
|
NSLog(@"[uninstallApp] deleting %@", pluginURL);
|
||||||
[[NSFileManager defaultManager] removeItemAtURL:pluginURL error:error];
|
[[NSFileManager defaultManager] removeItemAtURL:pluginURL error:nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unregister app
|
// unregister app
|
||||||
registerPath((char*)appPath.UTF8String, 1);
|
registerPath((char*)appPath.UTF8String, 1);
|
||||||
|
|
||||||
NSLog(@"deleting %@", [appPath stringByDeletingLastPathComponent]);
|
NSLog(@"[uninstallApp] deleting %@", [appPath stringByDeletingLastPathComponent]);
|
||||||
// delete app
|
// delete app
|
||||||
BOOL deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:error];
|
BOOL deleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil];
|
||||||
if(deleteSuc)
|
if(deleteSuc)
|
||||||
{
|
{
|
||||||
return 0;
|
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;
|
if(!appPath) return 1;
|
||||||
NSString* appId = appIdForAppPath(appPath);
|
NSString* appId = appIdForAppPath(appPath);
|
||||||
if(!appId) return 1;
|
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;
|
if(!appId) return 1;
|
||||||
NSString* appPath = appPathForAppId(appId, error);
|
NSString* appPath = appPathForAppId(appId);
|
||||||
if(!appPath) return 1;
|
if(!appPath) return 1;
|
||||||
return uninstallApp(appPath, appId, error);
|
return uninstallApp(appPath, appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 166: IPA does not exist or is not accessible
|
// 166: IPA does not exist or is not accessible
|
||||||
// 167: IPA does not appear to contain an app
|
// 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;
|
if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166;
|
||||||
|
|
||||||
BOOL suc = NO;
|
BOOL suc = NO;
|
||||||
NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
|
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;
|
if(!suc) return 1;
|
||||||
|
|
||||||
extract(ipaPath, tmpPath);
|
extract(ipaPath, tmpPath);
|
||||||
|
|
||||||
NSString* tmpPayloadPath = [tmpPath stringByAppendingPathComponent:@"Payload"];
|
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;
|
if(!items) return 167;
|
||||||
|
|
||||||
NSString* tmpAppPath;
|
NSString* tmpAppPath;
|
||||||
|
@ -740,7 +853,7 @@ int installIpa(NSString* ipaPath, BOOL force, NSError** error)
|
||||||
}
|
}
|
||||||
if(!tmpAppPath) return 167;
|
if(!tmpAppPath) return 167;
|
||||||
|
|
||||||
int ret = installApp(tmpAppPath, YES, force, error);
|
int ret = installApp(tmpAppPath, YES, force);
|
||||||
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil];
|
[[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil];
|
||||||
|
|
||||||
|
@ -751,7 +864,7 @@ void uninstallAllApps(void)
|
||||||
{
|
{
|
||||||
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
||||||
{
|
{
|
||||||
uninstallAppById(appIdForAppPath(appPath), nil);
|
uninstallAppById(appIdForAppPath(appPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,7 +928,7 @@ BOOL installTrollStore(NSString* pathToTar)
|
||||||
_installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper);
|
_installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return installApp(tmpTrollStore, NO, YES, nil);;
|
return installApp(tmpTrollStore, NO, YES);;
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshAppRegistrations()
|
void refreshAppRegistrations()
|
||||||
|
@ -943,7 +1056,6 @@ int main(int argc, char *argv[], char *envp[]) {
|
||||||
[mcmBundle load];
|
[mcmBundle load];
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
NSError* error;
|
|
||||||
|
|
||||||
NSString* cmd = [NSString stringWithUTF8String:argv[1]];
|
NSString* cmd = [NSString stringWithUTF8String:argv[1]];
|
||||||
if([cmd isEqualToString:@"install"])
|
if([cmd isEqualToString:@"install"])
|
||||||
|
@ -960,17 +1072,22 @@ int main(int argc, char *argv[], char *envp[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NSString* ipaPath = [NSString stringWithUTF8String:argv[2]];
|
NSString* ipaPath = [NSString stringWithUTF8String:argv[2]];
|
||||||
ret = installIpa(ipaPath, force, &error);
|
ret = installIpa(ipaPath, force);
|
||||||
} else if([cmd isEqualToString:@"uninstall"])
|
} else if([cmd isEqualToString:@"uninstall"])
|
||||||
{
|
{
|
||||||
if(argc <= 2) return -3;
|
if(argc <= 2) return -3;
|
||||||
NSString* appId = [NSString stringWithUTF8String:argv[2]];
|
NSString* appId = [NSString stringWithUTF8String:argv[2]];
|
||||||
ret = uninstallAppById(appId, &error);
|
ret = uninstallAppById(appId);
|
||||||
} else if([cmd isEqualToString:@"uninstall-path"])
|
} /*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;
|
if(argc <= 2) return -3;
|
||||||
NSString* appPath = [NSString stringWithUTF8String:argv[2]];
|
NSString* appPath = [NSString stringWithUTF8String:argv[2]];
|
||||||
ret = uninstallAppByPath(appPath, &error);
|
ret = uninstallAppByPath(appPath);
|
||||||
}else if([cmd isEqualToString:@"install-trollstore"])
|
}else if([cmd isEqualToString:@"install-trollstore"])
|
||||||
{
|
{
|
||||||
if(argc <= 2) return -3;
|
if(argc <= 2) return -3;
|
||||||
|
@ -1003,11 +1120,6 @@ int main(int argc, char *argv[], char *envp[]) {
|
||||||
uninstallPersistenceHelper();
|
uninstallPersistenceHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(error)
|
|
||||||
{
|
|
||||||
NSLog(@"error: %@", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSLog(@"returning %d", ret);
|
NSLog(@"returning %d", ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -142,6 +142,8 @@ void registerPath(char* cPath, int unregister)
|
||||||
dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
|
dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
|
||||||
dictToRegister[@"SignatureVersion"] = @132352;
|
dictToRegister[@"SignatureVersion"] = @132352;
|
||||||
dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
|
dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
|
||||||
|
dictToRegister[@"IsAdHocSigned"] = @YES;
|
||||||
|
dictToRegister[@"LSInstallType"] = @1;
|
||||||
|
|
||||||
NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);
|
NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);
|
||||||
if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier;
|
if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier;
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0.7</string>
|
<string>1.0.8</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
|
|
|
@ -149,7 +149,7 @@
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"refresh"]);
|
spawnRoot(helperPath(), @[@"refresh"], nil, nil);
|
||||||
respring();
|
respring();
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
|
@ -187,7 +187,7 @@
|
||||||
NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"];
|
NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"];
|
||||||
[[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil];
|
[[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(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil];
|
[[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil];
|
||||||
|
@ -223,7 +223,7 @@
|
||||||
|
|
||||||
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"uninstall-trollstore"]);
|
spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil);
|
||||||
[self reloadSpecifiers];
|
[self reloadSpecifiers];
|
||||||
}];
|
}];
|
||||||
[uninstallWarningAlert addAction:continueAction];
|
[uninstallWarningAlert addAction:continueAction];
|
||||||
|
@ -240,7 +240,7 @@
|
||||||
|
|
||||||
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"uninstall-persistence-helper"]);
|
spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil);
|
||||||
exit(0);
|
exit(0);
|
||||||
}];
|
}];
|
||||||
[uninstallWarningAlert addAction:continueAction];
|
[uninstallWarningAlert addAction:continueAction];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstorehelper
|
Package: com.opa334.trollstorehelper
|
||||||
Name: TrollStore Helper
|
Name: TrollStore Helper
|
||||||
Version: 1.0.7
|
Version: 1.0.8
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Description: Helper utility to install and manage TrollStore!
|
Description: Helper utility to install and manage TrollStore!
|
||||||
Maintainer: opa334
|
Maintainer: opa334
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0.7</string>
|
<string>1.0.8</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
|
|
|
@ -28,6 +28,48 @@
|
||||||
self.tableView.allowsMultipleSelectionDuringEditing = NO;
|
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
|
#pragma mark - Table view data source
|
||||||
|
|
||||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||||
|
@ -51,20 +93,50 @@
|
||||||
|
|
||||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
|
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
if (editingStyle == UITableViewCellEditingStyleDelete)
|
if(editingStyle == UITableViewCellEditingStyleDelete)
|
||||||
{
|
{
|
||||||
NSString* appPath = [[TSApplicationsManager sharedInstance] installedAppPaths][indexPath.row];
|
[self uninstallPressedForRowAtIndexPath:indexPath];
|
||||||
NSString* appId = [[TSApplicationsManager sharedInstance] appIdForAppPath:appPath];
|
|
||||||
|
|
||||||
if(appId)
|
|
||||||
{
|
|
||||||
[[TSApplicationsManager sharedInstance] uninstallApp:appId];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[[TSApplicationsManager sharedInstance] uninstallAppByPath:appPath];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (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
|
@end
|
|
@ -14,9 +14,10 @@
|
||||||
- (NSString*)displayNameForAppPath:(NSString*)appPath;
|
- (NSString*)displayNameForAppPath:(NSString*)appPath;
|
||||||
|
|
||||||
- (NSError*)errorForCode:(int)code;
|
- (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)installIpa:(NSString*)pathToIpa;
|
||||||
- (int)uninstallApp:(NSString*)appId;
|
- (int)uninstallApp:(NSString*)appId;
|
||||||
- (int)uninstallAppByPath:(NSString*)path;
|
- (int)uninstallAppByPath:(NSString*)path;
|
||||||
|
//- (int)detachFromApp:(NSString*)appId;
|
||||||
|
|
||||||
@end
|
@end
|
|
@ -24,7 +24,13 @@
|
||||||
- (NSDictionary*)infoDictionaryForAppPath:(NSString*)appPath
|
- (NSDictionary*)infoDictionaryForAppPath:(NSString*)appPath
|
||||||
{
|
{
|
||||||
NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"];
|
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
|
- (NSString*)appIdForAppPath:(NSString*)appPath
|
||||||
|
@ -35,7 +41,6 @@
|
||||||
- (NSString*)displayNameForAppPath:(NSString*)appPath
|
- (NSString*)displayNameForAppPath:(NSString*)appPath
|
||||||
{
|
{
|
||||||
NSDictionary* infoDict = [self infoDictionaryForAppPath:appPath];
|
NSDictionary* infoDict = [self infoDictionaryForAppPath:appPath];
|
||||||
|
|
||||||
NSString* displayName = infoDict[@"CFBundleDisplayName"];
|
NSString* displayName = infoDict[@"CFBundleDisplayName"];
|
||||||
if(![displayName isKindOfClass:[NSString class]]) displayName = nil;
|
if(![displayName isKindOfClass:[NSString class]]) displayName = nil;
|
||||||
if(!displayName || [displayName isEqualToString:@""])
|
if(!displayName || [displayName isEqualToString:@""])
|
||||||
|
@ -57,12 +62,14 @@
|
||||||
NSString* errorDescription = @"Unknown Error";
|
NSString* errorDescription = @"Unknown Error";
|
||||||
switch(code)
|
switch(code)
|
||||||
{
|
{
|
||||||
|
// IPA install errors
|
||||||
case 166:
|
case 166:
|
||||||
errorDescription = @"The IPA file does not exist or is not accessible.";
|
errorDescription = @"The IPA file does not exist or is not accessible.";
|
||||||
break;
|
break;
|
||||||
case 167:
|
case 167:
|
||||||
errorDescription = @"The IPA file does not appear to contain an app.";
|
errorDescription = @"The IPA file does not appear to contain an app.";
|
||||||
break;
|
break;
|
||||||
|
// App install errors
|
||||||
case 170:
|
case 170:
|
||||||
errorDescription = @"Failed to create container for app bundle.";
|
errorDescription = @"Failed to create container for app bundle.";
|
||||||
break;
|
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.";
|
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;
|
break;
|
||||||
case 172:
|
case 172:
|
||||||
errorDescription = @"The app does not seem to contain an Info.plist";
|
errorDescription = @"The app does not contain an Info.plist file.";
|
||||||
break;
|
break;
|
||||||
case 173:
|
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;
|
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}];
|
NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}];
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force
|
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
if(force)
|
if(force)
|
||||||
{
|
{
|
||||||
ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"]);
|
ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"], nil, logOut);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = spawnRoot(helperPath(), @[@"install", pathToIpa]);
|
ret = spawnRoot(helperPath(), @[@"install", pathToIpa], nil, logOut);
|
||||||
}
|
}
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -98,13 +124,13 @@
|
||||||
|
|
||||||
- (int)installIpa:(NSString*)pathToIpa
|
- (int)installIpa:(NSString*)pathToIpa
|
||||||
{
|
{
|
||||||
return [self installIpa:pathToIpa force:NO];
|
return [self installIpa:pathToIpa force:NO log:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)uninstallApp:(NSString*)appId
|
- (int)uninstallApp:(NSString*)appId
|
||||||
{
|
{
|
||||||
if(!appId) return -200;
|
if(!appId) return -200;
|
||||||
int ret = spawnRoot(helperPath(), @[@"uninstall", appId]);
|
int ret = spawnRoot(helperPath(), @[@"uninstall", appId], nil, nil);
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -112,9 +138,17 @@
|
||||||
- (int)uninstallAppByPath:(NSString*)path
|
- (int)uninstallAppByPath:(NSString*)path
|
||||||
{
|
{
|
||||||
if(!path) return -200;
|
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];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
||||||
return ret;
|
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
|
@end
|
|
@ -7,163 +7,180 @@
|
||||||
|
|
||||||
- (void)doIPAInstall:(NSString*)ipaPath scene:(UIWindowScene*)scene force:(BOOL)force completion:(void (^)(void))completion
|
- (void)doIPAInstall:(NSString*)ipaPath scene:(UIWindowScene*)scene force:(BOOL)force completion:(void (^)(void))completion
|
||||||
{
|
{
|
||||||
UIWindow* keyWindow = nil;
|
UIWindow* keyWindow = nil;
|
||||||
for(UIWindow* window in scene.windows)
|
for(UIWindow* window in scene.windows)
|
||||||
{
|
{
|
||||||
if(window.isKeyWindow)
|
if(window.isKeyWindow)
|
||||||
{
|
{
|
||||||
keyWindow = window;
|
keyWindow = window;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
||||||
UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];
|
UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];
|
||||||
activityIndicator.hidesWhenStopped = YES;
|
activityIndicator.hidesWhenStopped = YES;
|
||||||
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
|
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
|
||||||
[activityIndicator startAnimating];
|
[activityIndicator startAnimating];
|
||||||
[infoAlert.view addSubview:activityIndicator];
|
[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), ^
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||||
{
|
{
|
||||||
// Install IPA
|
// Install IPA
|
||||||
int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force];
|
//NSString* log;
|
||||||
NSError* error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
|
int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force log:nil];
|
||||||
|
|
||||||
NSLog(@"installed app! ret:%d, error: %@", ret, error);
|
NSError* error;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
[infoAlert dismissViewControllerAnimated:YES completion:^
|
error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
|
||||||
{
|
}
|
||||||
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];
|
|
||||||
|
|
||||||
[keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil];
|
NSLog(@"installed app! ret:%d, error: %@", ret, error);
|
||||||
}
|
|
||||||
|
|
||||||
if(ret != 171)
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
completion();
|
[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
|
- (void)handleURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts scene:(UIWindowScene*)scene
|
||||||
{
|
{
|
||||||
for(UIOpenURLContext* context in URLContexts)
|
for(UIOpenURLContext* context in URLContexts)
|
||||||
{
|
{
|
||||||
NSLog(@"openURLContexts %@", context.URL);
|
NSLog(@"openURLContexts %@", context.URL);
|
||||||
NSURL* url = context.URL;
|
NSURL* url = context.URL;
|
||||||
if (url != nil && [url isFileURL]) {
|
if (url != nil && [url isFileURL]) {
|
||||||
[url startAccessingSecurityScopedResource];
|
[url startAccessingSecurityScopedResource];
|
||||||
void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
|
void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
|
||||||
{
|
{
|
||||||
[url stopAccessingSecurityScopedResource];
|
[url stopAccessingSecurityScopedResource];
|
||||||
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
|
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
|
||||||
|
|
||||||
if(shouldExit)
|
if(shouldExit)
|
||||||
{
|
{
|
||||||
NSLog(@"Respring + Exit");
|
NSLog(@"Respring + Exit");
|
||||||
respring();
|
respring();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ([url.pathExtension isEqualToString:@"ipa"])
|
if ([url.pathExtension isEqualToString:@"ipa"])
|
||||||
{
|
{
|
||||||
[self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
|
[self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
|
||||||
doneBlock(NO);
|
doneBlock(NO);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
else if([url.pathExtension isEqualToString:@"tar"])
|
else if([url.pathExtension isEqualToString:@"tar"])
|
||||||
{
|
{
|
||||||
// Update TrollStore itself
|
// Update TrollStore itself
|
||||||
NSLog(@"Updating TrollStore...");
|
NSLog(@"Updating TrollStore...");
|
||||||
int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path]);
|
int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path], nil, nil);
|
||||||
doneBlock(ret == 0);
|
doneBlock(ret == 0);
|
||||||
NSLog(@"Updated TrollStore!");
|
NSLog(@"Updated TrollStore!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
|
- (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`.
|
// 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.
|
// 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).
|
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||||
|
|
||||||
NSLog(@"scene:%@ willConnectToSession:%@ options:%@", scene, session, connectionOptions);
|
NSLog(@"scene:%@ willConnectToSession:%@ options:%@", scene, session, connectionOptions);
|
||||||
UIWindowScene* windowScene = (UIWindowScene*)scene;
|
UIWindowScene* windowScene = (UIWindowScene*)scene;
|
||||||
_window = [[UIWindow alloc] initWithWindowScene:windowScene];
|
_window = [[UIWindow alloc] initWithWindowScene:windowScene];
|
||||||
_rootViewController = [[TSRootViewController alloc] init];
|
_rootViewController = [[TSRootViewController alloc] init];
|
||||||
_window.rootViewController = _rootViewController;
|
_window.rootViewController = _rootViewController;
|
||||||
[_window makeKeyAndVisible];
|
[_window makeKeyAndVisible];
|
||||||
|
|
||||||
if(connectionOptions.URLContexts.count)
|
if(connectionOptions.URLContexts.count)
|
||||||
{
|
{
|
||||||
[self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];
|
[self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidDisconnect:(UIScene *)scene {
|
- (void)sceneDidDisconnect:(UIScene *)scene {
|
||||||
// Called as the scene is being released by the system.
|
// 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.
|
// 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.
|
// 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).
|
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidBecomeActive:(UIScene *)scene {
|
- (void)sceneDidBecomeActive:(UIScene *)scene {
|
||||||
// Called when the scene has moved from an inactive state to an active state.
|
// 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.
|
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneWillResignActive:(UIScene *)scene {
|
- (void)sceneWillResignActive:(UIScene *)scene {
|
||||||
// Called when the scene will move from an active state to an inactive state.
|
// 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).
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneWillEnterForeground:(UIScene *)scene {
|
- (void)sceneWillEnterForeground:(UIScene *)scene {
|
||||||
// Called as the scene transitions from the background to the foreground.
|
// Called as the scene transitions from the background to the foreground.
|
||||||
// Use this method to undo the changes made on entering the background.
|
// Use this method to undo the changes made on entering the background.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidEnterBackground:(UIScene *)scene {
|
- (void)sceneDidEnterBackground:(UIScene *)scene {
|
||||||
// Called as the scene transitions from the foreground to the background.
|
// 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
|
// 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.
|
// to restore the scene back to its current state.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
|
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
|
||||||
{
|
{
|
||||||
NSLog(@"scene:%@ openURLContexts:%@", scene, URLContexts);
|
NSLog(@"scene:%@ openURLContexts:%@", scene, URLContexts);
|
||||||
[self handleURLContexts:URLContexts scene:(UIWindowScene*)scene];
|
[self handleURLContexts:URLContexts scene:(UIWindowScene*)scene];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
|
@ -225,7 +225,7 @@
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
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(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
|
@ -259,7 +259,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"install-ldid", location.path]);
|
spawnRoot(helperPath(), @[@"install-ldid", location.path], nil, nil);
|
||||||
dispatch_async(dispatch_get_main_queue(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
[self stopActivityWithCompletion:nil];
|
[self stopActivityWithCompletion:nil];
|
||||||
|
@ -294,7 +294,7 @@
|
||||||
{
|
{
|
||||||
UIAlertAction* installAction = [UIAlertAction actionWithTitle:[appProxy localizedName] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
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];
|
[self reloadSpecifiers];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@
|
||||||
|
|
||||||
- (void)uninstallPersistenceHelperPressed
|
- (void)uninstallPersistenceHelperPressed
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"uninstall-persistence-helper"]);
|
spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil);
|
||||||
[self reloadSpecifiers];
|
[self reloadSpecifiers];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@
|
||||||
|
|
||||||
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||||
{
|
{
|
||||||
spawnRoot(helperPath(), @[@"uninstall-trollstore"]);
|
spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil);
|
||||||
exit(0);
|
exit(0);
|
||||||
}];
|
}];
|
||||||
[uninstallWarningAlert addAction:continueAction];
|
[uninstallWarningAlert addAction:continueAction];
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
|
|
||||||
extern NSString* helperPath(void);
|
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 void respring(void);
|
||||||
extern NSString* getTrollStoreVersion(void);
|
extern NSString* getTrollStoreVersion(void);
|
|
@ -14,7 +14,29 @@ NSString* helperPath(void)
|
||||||
return [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"trollstorehelper"];
|
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];
|
NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];
|
||||||
[argsM insertObject:path.lastPathComponent atIndex:0];
|
[argsM insertObject:path.lastPathComponent atIndex:0];
|
||||||
|
@ -28,18 +50,35 @@ int spawnRoot(NSString* path, NSArray* args)
|
||||||
}
|
}
|
||||||
argsC[argCount] = NULL;
|
argsC[argCount] = NULL;
|
||||||
|
|
||||||
int rv;
|
|
||||||
posix_spawnattr_t attr;
|
posix_spawnattr_t attr;
|
||||||
rv = posix_spawnattr_init(&attr);
|
posix_spawnattr_init(&attr);
|
||||||
if(rv != 0) return rv;
|
|
||||||
|
|
||||||
posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
|
posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
|
||||||
posix_spawnattr_set_persona_uid_np(&attr, 0);
|
posix_spawnattr_set_persona_uid_np(&attr, 0);
|
||||||
posix_spawnattr_set_persona_gid_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;
|
pid_t task_pid;
|
||||||
int status = -200;
|
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);
|
posix_spawnattr_destroy(&attr);
|
||||||
for (NSUInteger i = 0; i < argCount; i++)
|
for (NSUInteger i = 0; i < argCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +102,20 @@ int spawnRoot(NSString* path, NSArray* args)
|
||||||
return -222;
|
return -222;
|
||||||
}
|
}
|
||||||
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
} 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);
|
return WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstore
|
Package: com.opa334.trollstore
|
||||||
Name: TrollStore
|
Name: TrollStore
|
||||||
Version: 1.0.7
|
Version: 1.0.8
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Description: An awesome application!
|
Description: An awesome application!
|
||||||
Maintainer: opa334
|
Maintainer: opa334
|
||||||
|
|
Loading…
Reference in New Issue