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 (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