This commit is contained in:
opa334 2022-09-03 18:49:53 +02:00
parent 068f735233
commit c2a5e5b988
12 changed files with 353 additions and 131 deletions

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstoreroothelper Package: com.opa334.trollstoreroothelper
Name: trollstoreroothelper Name: trollstoreroothelper
Version: 1.0.1 Version: 1.0.2
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: An awesome tool of some sort!! Description: An awesome tool of some sort!!
Maintainer: opa334 Maintainer: opa334

View File

@ -200,6 +200,16 @@ NSString* dumpEntitlements(NSString* binaryPath)
return output; return output;
} }
NSDictionary* dumpEntitlementsDict(NSString* binaryPath)
{
NSString* entitlementsString = dumpEntitlements(binaryPath);
NSData* plistData = [entitlementsString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSPropertyListFormat format;
NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:&format error:&error];
return plist;
}
BOOL signApp(NSString* appPath, NSError** error) BOOL signApp(NSString* appPath, NSError** error)
{ {
if(!isLdidInstalled()) return NO; if(!isLdidInstalled()) return NO;
@ -246,8 +256,10 @@ BOOL signApp(NSString* appPath, NSError** error)
// 170: failed to create container for app bundle // 170: failed to create container for app bundle
// 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
int installApp(NSString* appPath, BOOL sign, NSError** error) int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error)
{ {
NSLog(@"[installApp force = %d]", force);
NSString* appId = appIdForAppPath(appPath); NSString* appId = appIdForAppPath(appPath);
if(!appId) return 172; if(!appId) return 172;
@ -281,12 +293,15 @@ int installApp(NSString* appPath, BOOL sign, NSError** error)
// 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]) if(existed && !isEmpty && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force)
{ {
NSLog(@"[installApp] already installed and not a TrollStore app... bailing out"); NSLog(@"[installApp] already installed and not a TrollStore app... bailing out");
return 171; return 171;
} }
// Mark app as TrollStore app
[[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil];
// Apply correct permissions // Apply correct permissions
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil]; NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];
NSURL* fileURL; NSURL* fileURL;
@ -342,7 +357,7 @@ int installApp(NSString* appPath, BOOL sign, NSError** error)
while(fileURL = [enumerator nextObject]) while(fileURL = [enumerator nextObject])
{ {
// do not under any circumstance delete this file as it makes iOS loose the app registration // do not under any circumstance delete this file as it makes iOS loose the app registration
if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"]) if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"] || [fileURL.lastPathComponent isEqualToString:@"_TrollStore"])
{ {
NSLog(@"[installApp] skip removal of %@", fileURL); NSLog(@"[installApp] skip removal of %@", fileURL);
continue; continue;
@ -359,9 +374,6 @@ int installApp(NSString* appPath, BOOL sign, NSError** error)
BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error]; BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error];
if(suc) if(suc)
{ {
// Mark app as TrollStore app
[[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil];
NSLog(@"[installApp] app installed, adding to icon cache now..."); NSLog(@"[installApp] app installed, adding to icon cache now...");
registerPath((char*)newAppPath.UTF8String, 0); registerPath((char*)newAppPath.UTF8String, 0);
return 0; return 0;
@ -382,6 +394,7 @@ int uninstallApp(NSString* appId, NSError** error)
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];
NSLog(@"1");
NSString *containerPath = [appContainer url].path; NSString *containerPath = [appContainer url].path;
if(containerPath) if(containerPath)
{ {
@ -391,11 +404,11 @@ int uninstallApp(NSString* appId, NSError** error)
} }
// delete group container paths // delete group container paths
for(NSURL* groupURL in [appProxy groupContainerURLs]) [[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupID, NSURL* groupURL, BOOL* stop)
{ {
[[NSFileManager defaultManager] removeItemAtPath:groupURL.path error:error]; [[NSFileManager defaultManager] removeItemAtURL:groupURL error:nil];
NSLog(@"deleting %@", groupURL.path); NSLog(@"deleting %@", groupURL);
} }];
// delete app plugin paths // delete app plugin paths
for(LSPlugInKitProxy* pluginProxy in appProxy.plugInKitPlugins) for(LSPlugInKitProxy* pluginProxy in appProxy.plugInKitPlugins)
@ -427,7 +440,7 @@ int uninstallApp(NSString* appId, NSError** error)
// 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, NSError** error) int installIpa(NSString* ipaPath, BOOL force, NSError** error)
{ {
if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166; if(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166;
@ -455,7 +468,7 @@ int installIpa(NSString* ipaPath, NSError** error)
} }
if(!tmpAppPath) return 167; if(!tmpAppPath) return 167;
int ret = installApp(tmpAppPath, YES, error); int ret = installApp(tmpAppPath, YES, force, error);
[[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil]; [[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil];
@ -530,7 +543,7 @@ BOOL installTrollStore(NSString* pathToTar)
_installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper); _installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper);
} }
return installApp(tmpTrollStore, NO, nil);; return installApp(tmpTrollStore, NO, YES, nil);;
} }
void refreshAppRegistrations() void refreshAppRegistrations()
@ -663,9 +676,19 @@ int main(int argc, char *argv[], char *envp[]) {
NSString* cmd = [NSString stringWithUTF8String:argv[1]]; NSString* cmd = [NSString stringWithUTF8String:argv[1]];
if([cmd isEqualToString:@"install"]) if([cmd isEqualToString:@"install"])
{ {
NSLog(@"argc = %d", argc);
BOOL force = NO;
if(argc <= 2) return -3; if(argc <= 2) return -3;
if(argc > 3)
{
NSLog(@"argv3 = %s", argv[3]);
if(!strcmp(argv[3], "force"))
{
force = YES;
}
}
NSString* ipaPath = [NSString stringWithUTF8String:argv[2]]; NSString* ipaPath = [NSString stringWithUTF8String:argv[2]];
ret = installIpa(ipaPath, &error); ret = installIpa(ipaPath, force, &error);
} else if([cmd isEqualToString:@"uninstall"]) } else if([cmd isEqualToString:@"uninstall"])
{ {
if(argc <= 2) return -3; if(argc <= 2) return -3;

View File

@ -4,89 +4,247 @@
#import <objc/runtime.h> #import <objc/runtime.h>
#import "dlfcn.h" #import "dlfcn.h"
void registerPath(char *path, int unregister) // uicache on steroids
extern NSDictionary* dumpEntitlementsDict(NSString* binaryPath);
NSDictionary* constructGroupsContainersForEntitlements(NSDictionary* entitlements, BOOL systemGroups)
{ {
if(!path) return; if(!entitlements) return nil;
LSApplicationWorkspace *workspace = NSString* entitlementForGroups;
[LSApplicationWorkspace defaultWorkspace]; NSString* mcmClass;
if (unregister && ![[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:path]]) { if(systemGroups)
LSApplicationProxy *app = [LSApplicationProxy {
applicationProxyForIdentifier:[NSString stringWithUTF8String:path]]; entitlementForGroups = @"com.apple.security.system-groups";
if (app.bundleURL) mcmClass = @"MCMSystemDataContainer";
path = (char *)[[app bundleURL] fileSystemRepresentation]; }
else
{
entitlementForGroups = @"com.apple.security.application-groups";
mcmClass = @"MCMSharedDataContainer";
} }
NSString *rawPath = [NSString stringWithUTF8String:path]; NSArray* groupIDs = entitlements[entitlementForGroups];
rawPath = [rawPath stringByResolvingSymlinksInPath]; if(groupIDs && [groupIDs isKindOfClass:[NSArray class]])
{
NSMutableDictionary* groupContainers = [NSMutableDictionary new];
NSDictionary *infoPlist = [NSDictionary for(NSString* groupID in groupIDs)
dictionaryWithContentsOfFile: {
[rawPath stringByAppendingPathComponent:@"Info.plist"]]; MCMContainer* container = [NSClassFromString(mcmClass) containerWithIdentifier:groupID createIfNecessary:YES existed:nil error:nil];
NSString *bundleID = [infoPlist objectForKey:@"CFBundleIdentifier"]; if(container.url)
{
NSURL *url = [NSURL fileURLWithPath:rawPath]; groupContainers[groupID] = container.url.path;
if (bundleID && !unregister) {
MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer")
containerWithIdentifier:bundleID
createIfNecessary:YES
existed:nil
error:nil];
NSString *containerPath = [appContainer url].path;
NSMutableDictionary *plist = [NSMutableDictionary dictionary];
[plist setObject:@"System" forKey:@"ApplicationType"];
[plist setObject:@1 forKey:@"BundleNameIsLocalized"];
[plist setObject:bundleID forKey:@"CFBundleIdentifier"];
[plist setObject:@0 forKey:@"CompatibilityState"];
if (containerPath) [plist setObject:containerPath forKey:@"Container"];
[plist setObject:@0 forKey:@"IsDeletable"];
[plist setObject:rawPath forKey:@"Path"];
NSString *pluginsPath =
[rawPath stringByAppendingPathComponent:@"PlugIns"];
NSArray *plugins = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:pluginsPath
error:nil];
NSMutableDictionary *bundlePlugins = [NSMutableDictionary dictionary];
for (NSString *pluginName in plugins) {
NSString *fullPath =
[pluginsPath stringByAppendingPathComponent:pluginName];
NSDictionary *infoPlist = [NSDictionary
dictionaryWithContentsOfFile:
[fullPath stringByAppendingPathComponent:@"Info.plist"]];
NSString *pluginBundleID =
[infoPlist objectForKey:@"CFBundleIdentifier"];
if (!pluginBundleID) continue;
MCMContainer *pluginContainer =
[objc_getClass("MCMPluginKitPluginDataContainer")
containerWithIdentifier:pluginBundleID
createIfNecessary:YES
existed:nil
error:nil];
NSString *pluginContainerPath = [pluginContainer url].path;
NSMutableDictionary *pluginPlist = [NSMutableDictionary dictionary];
[pluginPlist setObject:@"PluginKitPlugin"
forKey:@"ApplicationType"];
[pluginPlist setObject:@1 forKey:@"BundleNameIsLocalized"];
[pluginPlist setObject:pluginBundleID forKey:@"CFBundleIdentifier"];
[pluginPlist setObject:@0 forKey:@"CompatibilityState"];
[pluginPlist setObject:pluginContainerPath forKey:@"Container"];
[pluginPlist setObject:fullPath forKey:@"Path"];
[pluginPlist setObject:bundleID forKey:@"PluginOwnerBundleID"];
[bundlePlugins setObject:pluginPlist forKey:pluginBundleID];
} }
[plist setObject:bundlePlugins forKey:@"_LSBundlePlugins"];
if (![workspace registerApplicationDictionary:plist]) {
fprintf(stderr, "Error: Unable to register %s\n", path);
} }
} else {
if (![workspace unregisterApplication:url]) { return groupContainers.copy;
fprintf(stderr, "Error: Unable to unregister %s\n", path); }
return nil;
}
BOOL constructContainerizationForEntitlements(NSDictionary* entitlements)
{
NSNumber* noContainer = entitlements[@"com.apple.private.security.no-container"];
if(noContainer && [noContainer isKindOfClass:[NSNumber class]])
{
if(noContainer.boolValue)
{
return NO;
}
}
NSNumber* containerRequired = entitlements[@"com.apple.private.security.container-required"];
if(containerRequired && [containerRequired isKindOfClass:[NSNumber class]])
{
if(!containerRequired.boolValue)
{
return NO;
}
}
return YES;
}
NSString* constructTeamIdentifierForEntitlements(NSDictionary* entitlements)
{
NSString* teamIdentifier = entitlements[@"com.apple.developer.team-identifier"];
if(teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]])
{
return teamIdentifier;
}
return nil;
}
NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerPath)
{
NSString* tmpDir = [containerPath stringByAppendingPathComponent:@"tmp"];
return @{
@"CFFIXED_USER_HOME" : containerPath,
@"HOME" : containerPath,
@"TMPDIR" : tmpDir
};
}
void registerPath(char* cPath, int unregister)
{
if(!cPath) return;
NSString* path = [NSString stringWithUTF8String:cPath];
LSApplicationWorkspace* workspace = [LSApplicationWorkspace defaultWorkspace];
if(unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path])
{
LSApplicationProxy* app = [LSApplicationProxy applicationProxyForIdentifier:path];
if(app.bundleURL)
{
path = [app bundleURL].path;
}
}
path = [path stringByResolvingSymlinksInPath];
NSDictionary* appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]];
NSString* appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"];
if(appBundleID && !unregister)
{
MCMContainer* appContainer = [NSClassFromString(@"MCMAppDataContainer") containerWithIdentifier:appBundleID createIfNecessary:YES existed:nil error:nil];
NSString* containerPath = [appContainer url].path;
NSMutableDictionary* dictToRegister = [NSMutableDictionary dictionary];
// Add entitlements
NSString* appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]];
NSDictionary* entitlements = dumpEntitlementsDict(appExecutablePath);
if(entitlements)
{
dictToRegister[@"Entitlements"] = entitlements;
}
// Misc
dictToRegister[@"ApplicationType"] = @"System";
dictToRegister[@"BundleNameIsLocalized"] = @1;
dictToRegister[@"CFBundleIdentifier"] = appBundleID;
dictToRegister[@"CodeInfoIdentifier"] = appBundleID;
dictToRegister[@"CompatibilityState"] = @0;
if(containerPath)
{
dictToRegister[@"Container"] = containerPath;
dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath);
}
dictToRegister[@"IsDeletable"] = @0;
dictToRegister[@"Path"] = path;
dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements));
NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);
if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier;
// Add group containers
NSDictionary* appGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO);
NSDictionary* systemGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO);
NSMutableDictionary* groupContainers = [NSMutableDictionary new];
[groupContainers addEntriesFromDictionary:appGroupContainers];
[groupContainers addEntriesFromDictionary:systemGroupContainers];
if(groupContainers.count)
{
if(appGroupContainers.count)
{
dictToRegister[@"HasAppGroupContainers"] = @YES;
}
if(systemGroupContainers.count)
{
dictToRegister[@"HasSystemGroupContainers"] = @YES;
}
dictToRegister[@"GroupContainers"] = groupContainers.copy;
}
// Add plugins
NSString* pluginsPath = [path stringByAppendingPathComponent:@"PlugIns"];
NSArray* plugins = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginsPath error:nil];
NSMutableDictionary* bundlePlugins = [NSMutableDictionary dictionary];
for (NSString* pluginName in plugins)
{
NSString* pluginPath = [pluginsPath stringByAppendingPathComponent:pluginName];
NSDictionary* pluginInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[pluginPath stringByAppendingPathComponent:@"Info.plist"]];
NSString* pluginBundleID = [pluginInfoPlist objectForKey:@"CFBundleIdentifier"];
if(!pluginBundleID) continue;
MCMContainer* pluginContainer = [NSClassFromString(@"MCMPluginKitPluginDataContainer") containerWithIdentifier:pluginBundleID createIfNecessary:YES existed:nil error:nil];
NSString* pluginContainerPath = [pluginContainer url].path;
NSMutableDictionary* pluginDict = [NSMutableDictionary dictionary];
// Add entitlements
NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]];
NSDictionary* pluginEntitlements = dumpEntitlementsDict(pluginExecutablePath);
if(pluginEntitlements)
{
pluginDict[@"Entitlements"] = pluginEntitlements;
}
// Misc
pluginDict[@"ApplicationType"] = @"PluginKitPlugin";
pluginDict[@"BundleNameIsLocalized"] = @1;
pluginDict[@"CFBundleIdentifier"] = pluginBundleID;
pluginDict[@"CodeInfoIdentifier"] = pluginBundleID;
pluginDict[@"CompatibilityState"] = @0;
if(pluginContainerPath)
{
pluginDict[@"Container"] = pluginContainerPath;
pluginDict[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(pluginContainerPath);
}
pluginDict[@"Path"] = pluginPath;
pluginDict[@"PluginOwnerBundleID"] = appBundleID;
pluginDict[@"IsContainerized"] = @(constructContainerizationForEntitlements(pluginEntitlements));
NSString* pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements);
if(pluginTeamIdentifier) pluginDict[@"TeamIdentifier"] = pluginTeamIdentifier;
// Add plugin group containers
NSDictionary* pluginAppGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO);
NSDictionary* pluginSystemGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO);
NSMutableDictionary* pluginGroupContainers = [NSMutableDictionary new];
[pluginGroupContainers addEntriesFromDictionary:pluginAppGroupContainers];
[pluginGroupContainers addEntriesFromDictionary:pluginSystemGroupContainers];
if(pluginGroupContainers.count)
{
if(pluginAppGroupContainers.count)
{
pluginDict[@"HasAppGroupContainers"] = @YES;
}
if(pluginSystemGroupContainers.count)
{
pluginDict[@"HasSystemGroupContainers"] = @YES;
}
pluginDict[@"GroupContainers"] = pluginGroupContainers.copy;
}
[bundlePlugins setObject:pluginDict forKey:pluginBundleID];
}
[dictToRegister setObject:bundlePlugins forKey:@"_LSBundlePlugins"];
if(![workspace registerApplicationDictionary:dictToRegister])
{
NSLog(@"Error: Unable to register %@", path);
}
}
else
{
NSURL* url = [NSURL fileURLWithPath:path];
if(![workspace unregisterApplication:url])
{
NSLog(@"Error: Unable to unregister %@", path);
} }
} }
} }

View File

@ -65,6 +65,7 @@ int IOGPU_get_command_queue_extra_refills_needed(void)
|| strstr(u.machine, "iPhone14,") || strstr(u.machine, "iPhone14,")
|| strstr(u.machine, "iPad13,") || strstr(u.machine, "iPad13,")
|| strstr(u.machine, "iPad7,") || strstr(u.machine, "iPad7,")
|| strstr(u.machine, "iPad12,")
) )
{ {
return 1; return 1;
@ -72,10 +73,12 @@ int IOGPU_get_command_queue_extra_refills_needed(void)
// iPhone 8, X // iPhone 8, X
// iPhone XS, XR // iPhone XS, XR
// iPad Pro A12Z // iPad Pro A12Z
// iPad 8 A12
else if ( else if (
strstr(u.machine, "iPhone10,") strstr(u.machine, "iPhone10,")
|| strstr(u.machine, "iPhone11,") || strstr(u.machine, "iPhone11,")
|| strstr(u.machine, "iPad8,") || strstr(u.machine, "iPad8,")
|| strstr(u.machine, "iPad11,")
) )
{ {
return 3; return 3;

View File

@ -52,7 +52,7 @@
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0.2</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstorehelper Package: com.opa334.trollstorehelper
Name: TrollStore Helper Name: TrollStore Helper
Version: 1.0 Version: 1.0.2
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: Helper app to install and manage TrollStore! Description: Helper app to install and manage TrollStore!
Maintainer: opa334 Maintainer: opa334

View File

@ -50,7 +50,7 @@
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0.1</string> <string>1.0.2</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>

View File

@ -14,6 +14,7 @@
- (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; - (int)installIpa:(NSString*)pathToIpa;
- (int)uninstallApp:(NSString*)appId; - (int)uninstallApp:(NSString*)appId;

View File

@ -67,7 +67,7 @@
errorDescription = @"Failed to create container for app bundle."; errorDescription = @"Failed to create container for app bundle.";
break; break;
case 171: case 171:
errorDescription = @"A non-TrollStore app with the same identifier is already installed. If you are absolutely sure it is not, try refreshing icon cache in TrollStore settings or try rebooting your device."; 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 seem to contain an Info.plist";
@ -78,13 +78,26 @@
return error; return error;
} }
- (int)installIpa:(NSString*)pathToIpa - (int)installIpa:(NSString*)pathToIpa force:(BOOL)force
{ {
int ret = spawnRoot(helperPath(), @[@"install", pathToIpa]); int ret;
if(force)
{
ret = spawnRoot(helperPath(), @[@"install", pathToIpa, @"force"]);
}
else
{
ret = spawnRoot(helperPath(), @[@"install", pathToIpa]);
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
return ret; return ret;
} }
- (int)installIpa:(NSString*)pathToIpa
{
return [self installIpa:pathToIpa force:NO];
}
- (int)uninstallApp:(NSString*)appId - (int)uninstallApp:(NSString*)appId
{ {
int ret = spawnRoot(helperPath(), @[@"uninstall", appId]); int ret = spawnRoot(helperPath(), @[@"uninstall", appId]);

View File

@ -5,7 +5,7 @@
@implementation TSSceneDelegate @implementation TSSceneDelegate
- (void)handleURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts scene:(UIWindowScene*)scene - (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)
@ -17,6 +17,60 @@
} }
} }
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];
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];
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];
[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) for(UIOpenURLContext* context in URLContexts)
{ {
NSLog(@"openURLContexts %@", context.URL); NSLog(@"openURLContexts %@", context.URL);
@ -43,39 +97,9 @@
if ([url.pathExtension isEqualToString:@"ipa"]) if ([url.pathExtension isEqualToString:@"ipa"])
{ {
UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert]; [self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
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];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
// Install IPA
int ret = [[TSApplicationsManager sharedInstance] installIpa:tmpCopyURL.path];
NSError* error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
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:nil];
[errorAlert addAction:closeAction];
[keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil];
}
doneBlock(NO); doneBlock(NO);
}]; }];
});
});
} }
else if([url.pathExtension isEqualToString:@"tar"]) else if([url.pathExtension isEqualToString:@"tar"])
{ {

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstore Package: com.opa334.trollstore
Name: TrollStore Name: TrollStore
Version: 1.0.1 Version: 1.0.2
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: An awesome application! Description: An awesome application!
Maintainer: opa334 Maintainer: opa334