mirror of https://github.com/opa334/TrollStore.git
Merge branch 'main' into exp-value-picker
This commit is contained in:
commit
aceff0e0dc
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstoreroothelper
|
Package: com.opa334.trollstoreroothelper
|
||||||
Name: trollstoreroothelper
|
Name: trollstoreroothelper
|
||||||
Version: 1.0.1
|
Version: 1.0.3
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Description: An awesome tool of some sort!!
|
Description: An awesome tool of some sort!!
|
||||||
Maintainer: opa334
|
Maintainer: opa334
|
||||||
|
|
211
Helper/main.m
211
Helper/main.m
|
@ -8,9 +8,54 @@
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import "CoreServices.h"
|
#import "CoreServices.h"
|
||||||
#import "Shared.h"
|
#import "Shared.h"
|
||||||
|
#import <mach-o/getsect.h>
|
||||||
|
#import <mach-o/dyld.h>
|
||||||
|
#import <mach/mach.h>
|
||||||
|
#import <mach-o/loader.h>
|
||||||
|
#import <mach-o/nlist.h>
|
||||||
|
#import <mach-o/reloc.h>
|
||||||
|
#import <mach-o/dyld_images.h>
|
||||||
|
#import <mach-o/fat.h>
|
||||||
|
#import <sys/utsname.h>
|
||||||
|
|
||||||
#import <SpringBoardServices/SpringBoardServices.h>
|
#import <SpringBoardServices/SpringBoardServices.h>
|
||||||
|
|
||||||
|
#ifdef __LP64__
|
||||||
|
#define segment_command_universal segment_command_64
|
||||||
|
#define mach_header_universal mach_header_64
|
||||||
|
#define MH_MAGIC_UNIVERSAL MH_MAGIC_64
|
||||||
|
#define MH_CIGAM_UNIVERSAL MH_CIGAM_64
|
||||||
|
#else
|
||||||
|
#define segment_command_universal segment_command
|
||||||
|
#define mach_header_universal mach_header
|
||||||
|
#define MH_MAGIC_UNIVERSAL MH_MAGIC
|
||||||
|
#define MH_CIGAM_UNIVERSAL MH_CIGAM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SWAP32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0xff0000) >> 8) | (((x) & 0xff00) << 8) | (((x) & 0xff) << 24))
|
||||||
|
uint32_t s32(uint32_t toSwap, BOOL shouldSwap)
|
||||||
|
{
|
||||||
|
return shouldSwap ? SWAP32(toSwap) : toSwap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CPU_SUBTYPE_ARM64E_NEW_ABI 0x80000002
|
||||||
|
|
||||||
|
struct CSSuperBlob {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CSBlob {
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CS_MAGIC_EMBEDDED_SIGNATURE 0xfade0cc0
|
||||||
|
#define CS_MAGIC_EMBEDDED_SIGNATURE_REVERSED 0xc00cdefa
|
||||||
|
#define CS_MAGIC_EMBEDDED_ENTITLEMENTS 0xfade7171
|
||||||
|
|
||||||
|
|
||||||
extern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier);
|
extern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier);
|
||||||
@interface SBSHomeScreenService : NSObject
|
@interface SBSHomeScreenService : NSObject
|
||||||
- (void)reloadIcons;
|
- (void)reloadIcons;
|
||||||
|
@ -176,28 +221,111 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput)
|
||||||
return WEXITSTATUS(status);
|
return WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* dumpEntitlements(NSString* binaryPath)
|
NSDictionary* dumpEntitlements(NSString* binaryPath)
|
||||||
{
|
{
|
||||||
NSString* output;
|
char* entitlementsData = NULL;
|
||||||
NSString* errorOutput;
|
uint32_t entitlementsLength = 0;
|
||||||
|
|
||||||
int ldidRet = runLdid(@[@"-e", binaryPath], &output, &errorOutput);
|
FILE* machoFile = fopen(binaryPath.UTF8String, "rb");
|
||||||
|
struct mach_header_universal header;
|
||||||
|
fread(&header,sizeof(header),1,machoFile);
|
||||||
|
|
||||||
NSLog(@"entitlements dump exited with status %d", ldidRet);
|
if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM)
|
||||||
|
{
|
||||||
|
fseek(machoFile,0,SEEK_SET);
|
||||||
|
|
||||||
|
struct fat_header fatHeader;
|
||||||
|
fread(&fatHeader,sizeof(fatHeader),1,machoFile);
|
||||||
|
|
||||||
|
BOOL swpFat = fatHeader.magic == FAT_CIGAM;
|
||||||
|
|
||||||
|
for(int i = 0; i < s32(fatHeader.nfat_arch, swpFat); i++)
|
||||||
|
{
|
||||||
|
struct fat_arch fatArch;
|
||||||
|
fseek(machoFile,sizeof(fatHeader) + sizeof(fatArch) * i,SEEK_SET);
|
||||||
|
fread(&fatArch,sizeof(fatArch),1,machoFile);
|
||||||
|
|
||||||
|
if(s32(fatArch.cputype, swpFat) != CPU_TYPE_ARM64)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(machoFile,s32(fatArch.offset, swpFat),SEEK_SET);
|
||||||
|
struct mach_header_universal header;
|
||||||
|
fread(&header,sizeof(header),1,machoFile);
|
||||||
|
|
||||||
|
BOOL swp = header.magic == MH_CIGAM_UNIVERSAL;
|
||||||
|
|
||||||
|
// This code is cursed, don't stare at it too long or it will stare back at you
|
||||||
|
uint32_t offset = s32(fatArch.offset, swpFat) + sizeof(header);
|
||||||
|
for(int c = 0; c < s32(header.ncmds, swp); c++)
|
||||||
|
{
|
||||||
|
fseek(machoFile,offset,SEEK_SET);
|
||||||
|
struct load_command cmd;
|
||||||
|
fread(&cmd,sizeof(cmd),1,machoFile);
|
||||||
|
uint32_t normalizedCmd = s32(cmd.cmd,swp);
|
||||||
|
if(normalizedCmd == LC_CODE_SIGNATURE)
|
||||||
|
{
|
||||||
|
struct linkedit_data_command codeSignCommand;
|
||||||
|
fseek(machoFile,offset,SEEK_SET);
|
||||||
|
fread(&codeSignCommand,sizeof(codeSignCommand),1,machoFile);
|
||||||
|
uint32_t codeSignCmdOffset = s32(fatArch.offset, swpFat) + s32(codeSignCommand.dataoff, swp);
|
||||||
|
fseek(machoFile, codeSignCmdOffset, SEEK_SET);
|
||||||
|
struct CSSuperBlob superBlob;
|
||||||
|
fread(&superBlob, sizeof(superBlob), 1, machoFile);
|
||||||
|
if(SWAP32(superBlob.magic) == CS_MAGIC_EMBEDDED_SIGNATURE)
|
||||||
|
{
|
||||||
|
uint32_t itemCount = SWAP32(superBlob.count);
|
||||||
|
for(int i = 0; i < itemCount; i++)
|
||||||
|
{
|
||||||
|
fseek(machoFile, codeSignCmdOffset + sizeof(superBlob) + i * sizeof(struct CSBlob),SEEK_SET);
|
||||||
|
struct CSBlob blob;
|
||||||
|
fread(&blob, sizeof(struct CSBlob), 1, machoFile);
|
||||||
|
fseek(machoFile, codeSignCmdOffset + SWAP32(blob.offset),SEEK_SET);
|
||||||
|
uint32_t blobMagic;
|
||||||
|
fread(&blobMagic, sizeof(uint32_t), 1, machoFile);
|
||||||
|
if(SWAP32(blobMagic) == CS_MAGIC_EMBEDDED_ENTITLEMENTS)
|
||||||
|
{
|
||||||
|
uint32_t entitlementsLengthTmp;
|
||||||
|
fread(&entitlementsLengthTmp, sizeof(uint32_t), 1, machoFile);
|
||||||
|
entitlementsLength = SWAP32(entitlementsLengthTmp);
|
||||||
|
entitlementsData = malloc(entitlementsLength - 8);
|
||||||
|
fread(&entitlementsData[0], entitlementsLength - 8, 1, machoFile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += cmd.cmdsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(machoFile);
|
||||||
|
|
||||||
|
NSData* entitlementsNSData = nil;
|
||||||
|
|
||||||
|
if(entitlementsData)
|
||||||
|
{
|
||||||
|
entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength];
|
||||||
|
free(entitlementsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entitlementsNSData)
|
||||||
|
{
|
||||||
|
NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil];
|
||||||
|
NSLog(@"%@ dumped entitlements %@", binaryPath, plist);
|
||||||
|
return plist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath);
|
||||||
|
}
|
||||||
|
|
||||||
NSLog(@"- dump error output start -");
|
return nil;
|
||||||
|
|
||||||
printMultilineNSString(errorOutput);
|
|
||||||
|
|
||||||
NSLog(@"- dump error output end -");
|
|
||||||
|
|
||||||
NSLog(@"- dumped entitlements output start -");
|
|
||||||
|
|
||||||
printMultilineNSString(output);
|
|
||||||
|
|
||||||
NSLog(@"- dumped entitlements output end -");
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL signApp(NSString* appPath, NSError** error)
|
BOOL signApp(NSString* appPath, NSError** error)
|
||||||
|
@ -217,8 +345,8 @@ BOOL signApp(NSString* appPath, NSError** error)
|
||||||
NSString* errorOutput;
|
NSString* errorOutput;
|
||||||
int ldidRet;
|
int ldidRet;
|
||||||
|
|
||||||
NSString* entitlements = dumpEntitlements(executablePath);
|
NSDictionary* entitlements = dumpEntitlements(executablePath);
|
||||||
if(entitlements.length == 0)
|
if(!entitlements)
|
||||||
{
|
{
|
||||||
NSLog(@"app main binary has no entitlements, signing app with fallback entitlements...");
|
NSLog(@"app main binary has no entitlements, signing app with fallback entitlements...");
|
||||||
// app has no entitlements, sign with fallback entitlements
|
// app has no entitlements, sign with fallback entitlements
|
||||||
|
@ -229,7 +357,7 @@ BOOL signApp(NSString* appPath, NSError** error)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// app has entitlements, keep them
|
// app has entitlements, keep them
|
||||||
ldidRet = runLdid(@[@"-S", @"-M", certArg, appPath], nil, &errorOutput);
|
ldidRet = runLdid(@[@"-s", certArg, appPath], nil, &errorOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"ldid exited with status %d", ldidRet);
|
NSLog(@"ldid exited with status %d", ldidRet);
|
||||||
|
@ -246,8 +374,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 +411,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 +475,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 +492,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 +512,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 +522,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 +558,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 +586,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 +661,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 +794,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;
|
||||||
|
|
306
Helper/uicache.m
306
Helper/uicache.m
|
@ -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* dumpEntitlements(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)
|
||||||
|
{
|
||||||
|
groupContainers[groupID] = container.url.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSURL *url = [NSURL fileURLWithPath:rawPath];
|
return groupContainers.copy;
|
||||||
|
}
|
||||||
|
|
||||||
if (bundleID && !unregister) {
|
return nil;
|
||||||
MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer")
|
}
|
||||||
containerWithIdentifier:bundleID
|
|
||||||
createIfNecessary:YES
|
|
||||||
existed:nil
|
|
||||||
error:nil];
|
|
||||||
NSString *containerPath = [appContainer url].path;
|
|
||||||
|
|
||||||
NSMutableDictionary *plist = [NSMutableDictionary dictionary];
|
BOOL constructContainerizationForEntitlements(NSDictionary* entitlements)
|
||||||
[plist setObject:@"System" forKey:@"ApplicationType"];
|
{
|
||||||
[plist setObject:@1 forKey:@"BundleNameIsLocalized"];
|
NSNumber* noContainer = entitlements[@"com.apple.private.security.no-container"];
|
||||||
[plist setObject:bundleID forKey:@"CFBundleIdentifier"];
|
if(noContainer && [noContainer isKindOfClass:[NSNumber class]])
|
||||||
[plist setObject:@0 forKey:@"CompatibilityState"];
|
{
|
||||||
if (containerPath) [plist setObject:containerPath forKey:@"Container"];
|
if(noContainer.boolValue)
|
||||||
[plist setObject:@0 forKey:@"IsDeletable"];
|
{
|
||||||
[plist setObject:rawPath forKey:@"Path"];
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSString *pluginsPath =
|
NSNumber* containerRequired = entitlements[@"com.apple.private.security.container-required"];
|
||||||
[rawPath stringByAppendingPathComponent:@"PlugIns"];
|
if(containerRequired && [containerRequired isKindOfClass:[NSNumber class]])
|
||||||
NSArray *plugins = [[NSFileManager defaultManager]
|
{
|
||||||
contentsOfDirectoryAtPath:pluginsPath
|
if(!containerRequired.boolValue)
|
||||||
error:nil];
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSMutableDictionary *bundlePlugins = [NSMutableDictionary dictionary];
|
return YES;
|
||||||
for (NSString *pluginName in plugins) {
|
}
|
||||||
NSString *fullPath =
|
|
||||||
[pluginsPath stringByAppendingPathComponent:pluginName];
|
|
||||||
|
|
||||||
NSDictionary *infoPlist = [NSDictionary
|
NSString* constructTeamIdentifierForEntitlements(NSDictionary* entitlements)
|
||||||
dictionaryWithContentsOfFile:
|
{
|
||||||
[fullPath stringByAppendingPathComponent:@"Info.plist"]];
|
NSString* teamIdentifier = entitlements[@"com.apple.developer.team-identifier"];
|
||||||
NSString *pluginBundleID =
|
if(teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]])
|
||||||
[infoPlist objectForKey:@"CFBundleIdentifier"];
|
{
|
||||||
if (!pluginBundleID) continue;
|
return teamIdentifier;
|
||||||
MCMContainer *pluginContainer =
|
}
|
||||||
[objc_getClass("MCMPluginKitPluginDataContainer")
|
return nil;
|
||||||
containerWithIdentifier:pluginBundleID
|
}
|
||||||
createIfNecessary:YES
|
|
||||||
existed:nil
|
|
||||||
error:nil];
|
|
||||||
NSString *pluginContainerPath = [pluginContainer url].path;
|
|
||||||
|
|
||||||
NSMutableDictionary *pluginPlist = [NSMutableDictionary dictionary];
|
NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerPath)
|
||||||
[pluginPlist setObject:@"PluginKitPlugin"
|
{
|
||||||
forKey:@"ApplicationType"];
|
NSString* tmpDir = [containerPath stringByAppendingPathComponent:@"tmp"];
|
||||||
[pluginPlist setObject:@1 forKey:@"BundleNameIsLocalized"];
|
return @{
|
||||||
[pluginPlist setObject:pluginBundleID forKey:@"CFBundleIdentifier"];
|
@"CFFIXED_USER_HOME" : containerPath,
|
||||||
[pluginPlist setObject:@0 forKey:@"CompatibilityState"];
|
@"HOME" : containerPath,
|
||||||
[pluginPlist setObject:pluginContainerPath forKey:@"Container"];
|
@"TMPDIR" : tmpDir
|
||||||
[pluginPlist setObject:fullPath forKey:@"Path"];
|
};
|
||||||
[pluginPlist setObject:bundleID forKey:@"PluginOwnerBundleID"];
|
}
|
||||||
[bundlePlugins setObject:pluginPlist forKey:pluginBundleID];
|
|
||||||
}
|
void registerPath(char* cPath, int unregister)
|
||||||
[plist setObject:bundlePlugins forKey:@"_LSBundlePlugins"];
|
{
|
||||||
if (![workspace registerApplicationDictionary:plist]) {
|
if(!cPath) return;
|
||||||
fprintf(stderr, "Error: Unable to register %s\n", path);
|
NSString* path = [NSString stringWithUTF8String:cPath];
|
||||||
}
|
|
||||||
} else {
|
LSApplicationWorkspace* workspace = [LSApplicationWorkspace defaultWorkspace];
|
||||||
if (![workspace unregisterApplication:url]) {
|
if(unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path])
|
||||||
fprintf(stderr, "Error: Unable to unregister %s\n", 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 = dumpEntitlements(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 = dumpEntitlements(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -285,6 +285,8 @@ int writeRemountPrivatePreboot(void)
|
||||||
|
|
||||||
[self updateStatus:@"Done!"];
|
[self updateStatus:@"Done!"];
|
||||||
|
|
||||||
|
NSLog(@"%@", helperOutput);
|
||||||
|
|
||||||
// Print installed message
|
// Print installed message
|
||||||
if(ret == 0)
|
if(ret == 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,8 +65,9 @@ int IOGPU_get_command_queue_extra_refills_needed(void)
|
||||||
|| strstr(u.machine, "iPhone12,")
|
|| strstr(u.machine, "iPhone12,")
|
||||||
|| strstr(u.machine, "iPhone13,")
|
|| strstr(u.machine, "iPhone13,")
|
||||||
|| strstr(u.machine, "iPhone14,")
|
|| strstr(u.machine, "iPhone14,")
|
||||||
|| strstr(u.machine, "iPad13,")
|
|
||||||
|| strstr(u.machine, "iPad7,")
|
|| strstr(u.machine, "iPad7,")
|
||||||
|
|| strstr(u.machine, "iPad12,")
|
||||||
|
|| strstr(u.machine, "iPad13,")
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -74,10 +75,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;
|
||||||
|
|
1
LICENSE
1
LICENSE
|
@ -10,6 +10,7 @@ License: MIT
|
||||||
Files: Helper/uicache.m
|
Files: Helper/uicache.m
|
||||||
Copyright: Copyright (c) 2019 CoolStar,
|
Copyright: Copyright (c) 2019 CoolStar,
|
||||||
Modified work Copyright (c) 2020-2022 Procursus Team <team@procurs.us>
|
Modified work Copyright (c) 2020-2022 Procursus Team <team@procurs.us>
|
||||||
|
Modified work Copyright (c) 2022 Lars Fröder <opa334@protonmail.com>
|
||||||
License: BSD-4-Clause
|
License: BSD-4-Clause
|
||||||
|
|
||||||
Files: Installer/TrollInstaller/exploit
|
Files: Installer/TrollInstaller/exploit
|
||||||
|
|
|
@ -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.3</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
|
|
Binary file not shown.
|
@ -76,6 +76,19 @@
|
||||||
[refreshAppRegistrationsSpecifier setProperty:@YES forKey:@"enabled"];
|
[refreshAppRegistrationsSpecifier setProperty:@YES forKey:@"enabled"];
|
||||||
refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrations);
|
refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrations);
|
||||||
[_specifiers addObject:refreshAppRegistrationsSpecifier];
|
[_specifiers addObject:refreshAppRegistrationsSpecifier];
|
||||||
|
|
||||||
|
PSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall TrollStore"
|
||||||
|
target:self
|
||||||
|
set:nil
|
||||||
|
get:nil
|
||||||
|
detail:nil
|
||||||
|
cell:PSButtonCell
|
||||||
|
edit:nil];
|
||||||
|
uninstallTrollStoreSpecifier.identifier = @"uninstallTrollStore";
|
||||||
|
[uninstallTrollStoreSpecifier setProperty:@YES forKey:@"enabled"];
|
||||||
|
[uninstallTrollStoreSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"];
|
||||||
|
uninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed);
|
||||||
|
[_specifiers addObject:uninstallTrollStoreSpecifier];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -201,6 +214,22 @@
|
||||||
[downloadTask resume];
|
[downloadTask resume];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)uninstallTrollStorePressed
|
||||||
|
{
|
||||||
|
UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert];
|
||||||
|
|
||||||
|
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||||
|
[uninstallWarningAlert addAction:cancelAction];
|
||||||
|
|
||||||
|
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||||
|
{
|
||||||
|
spawnRoot(helperPath(), @[@"uninstall-trollstore"]);
|
||||||
|
[self reloadSpecifiers];
|
||||||
|
}];
|
||||||
|
[uninstallWarningAlert addAction:continueAction];
|
||||||
|
|
||||||
|
[self presentViewController:uninstallWarningAlert animated:YES completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)uninstallPersistenceHelperPressed
|
- (void)uninstallPersistenceHelperPressed
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstorehelper
|
Package: com.opa334.trollstorehelper
|
||||||
Name: TrollStore Helper
|
Name: TrollStore Helper
|
||||||
Version: 1.0
|
Version: 1.0.3
|
||||||
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
|
||||||
|
|
|
@ -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.3</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -39,43 +93,13 @@
|
||||||
respring();
|
respring();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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)];
|
doneBlock(NO);
|
||||||
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);
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else if([url.pathExtension isEqualToString:@"tar"])
|
else if([url.pathExtension isEqualToString:@"tar"])
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Package: com.opa334.trollstore
|
Package: com.opa334.trollstore
|
||||||
Name: TrollStore
|
Name: TrollStore
|
||||||
Version: 1.0.1
|
Version: 1.0.3
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Description: An awesome application!
|
Description: An awesome application!
|
||||||
Maintainer: opa334
|
Maintainer: opa334
|
||||||
|
|
|
@ -36,6 +36,8 @@ make clean
|
||||||
make package FINALPACKAGE=1
|
make package FINALPACKAGE=1
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
|
rm ../PersistenceHelper/Resources/trollstorehelper
|
||||||
|
|
||||||
cp ../PersistenceHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./out/TrollStore.app/PersistenceHelper
|
cp ../PersistenceHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./out/TrollStore.app/PersistenceHelper
|
||||||
ldid -S -M -Kcert.p12 ./out/TrollStore.app/PersistenceHelper
|
ldid -S -M -Kcert.p12 ./out/TrollStore.app/PersistenceHelper
|
||||||
|
|
||||||
|
@ -45,3 +47,19 @@ cd out
|
||||||
COPYFILE_DISABLE=1 tar -czvf TrollStore.tar ./TrollStore.app
|
COPYFILE_DISABLE=1 tar -czvf TrollStore.tar ./TrollStore.app
|
||||||
rm -rf ./TrollStore.app
|
rm -rf ./TrollStore.app
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
|
# Step five: compile installer
|
||||||
|
xcodebuild -project ../Installer/TrollInstaller/TrollInstaller.xcodeproj -scheme TrollInstaller -destination generic/platform=iOS -archivePath ./out/Installer.xcarchive archive
|
||||||
|
|
||||||
|
if [[ -f "./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision" ]]; then
|
||||||
|
rm ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision
|
||||||
|
fi
|
||||||
|
|
||||||
|
ldid -s ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app
|
||||||
|
mkdir ./out/Payload
|
||||||
|
mv ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app ./out/Payload/TrollInstaller.app
|
||||||
|
cd out
|
||||||
|
zip -vr TrollInstaller.ipa Payload
|
||||||
|
cd -
|
||||||
|
rm -rf ./out/Payload
|
||||||
|
rm -rf ./out/Installer.xcarchive
|
Loading…
Reference in New Issue