This commit is contained in:
opa334 2022-09-09 20:22:34 +02:00
parent ab3261f0f2
commit 5d474901a2
16 changed files with 533 additions and 235 deletions

View File

@ -14,6 +14,7 @@
@property (getter=isInstalled,nonatomic,readonly) BOOL installed;
@property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder;
@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;

View File

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

View File

@ -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:&copyError];
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;

View File

@ -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;

View File

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

View File

@ -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];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,10 +29,17 @@
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];
//NSString* log;
int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force log:nil];
NSError* error;
if(ret != 0)
{
error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
}
NSLog(@"installed app! ret:%d, error: %@", ret, error);
dispatch_async(dispatch_get_main_queue(), ^
{
[infoAlert dismissViewControllerAnimated:YES completion:^
@ -47,6 +54,8 @@
completion();
}
}];
[errorAlert addAction:closeAction];
if(ret == 171)
{
UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
@ -55,7 +64,15 @@
}];
[errorAlert addAction:forceInstallAction];
}
[errorAlert addAction:closeAction];
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];
}
@ -100,7 +117,7 @@
{
// Update TrollStore itself
NSLog(@"Updating TrollStore...");
int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path]);
int ret = spawnRoot(helperPath(), @[@"install-trollstore", url.path], nil, nil);
doneBlock(ret == 0);
NSLog(@"Updated TrollStore!");
}

View File

@ -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];

View File

@ -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);

View File

@ -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++)
{
@ -64,6 +103,20 @@ int spawnRoot(NSString* path, NSArray* args)
}
} 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);
}

View File

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