Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
imod-commission authored Jan 14, 2024
2 parents 699567d + 4e9d1a2 commit 527e7a9
Show file tree
Hide file tree
Showing 24 changed files with 601 additions and 135 deletions.
4 changes: 2 additions & 2 deletions Bootstrap.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 0.4.3;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.roothide.Bootstrap;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -470,7 +470,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 0.4.3;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.roothide.Bootstrap;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions Bootstrap/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@interface AppDelegate : UIResponder <UIApplicationDelegate>

+(void)showHudMsg:(NSString*)msg;
+(void)showHudMsg:(NSString*)msg detail:(NSString*)info;
+(void)dismissHud;

+ (void)showAlert:(UIAlertController*)alert;
Expand Down
10 changes: 10 additions & 0 deletions Bootstrap/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ +(void)showHudMsg:(NSString*)msg
});
}

+(void)showHudMsg:(NSString*)msg detail:(NSString*)info
{
dispatch_async(dispatch_get_main_queue(), ^{
switchHud = [MBProgressHUD showHUDAddedTo:UIApplication.sharedApplication.keyWindow animated:YES];
[switchHud showAnimated:YES];
switchHud.label.text = msg;
switchHud.detailsLabel.text = info;
});
}

+(void)dismissHud {
dispatch_async(dispatch_get_main_queue(), ^{
[switchHud hideAnimated:YES];
Expand Down
50 changes: 43 additions & 7 deletions Bootstrap/AppEnabler.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import <Foundation/Foundation.h>
#include <sys/stat.h>
#include "AppList.h"
#include "common.h"

Expand Down Expand Up @@ -135,6 +136,7 @@ int backupApp(NSString* bundlePath)
return 0;
}

//if the app package is changed/upgraded, the directory structure may change and some paths may become invalid.
int restoreApp(NSString* bundlePath)
{
SYSLOG("restoreApp=%@", bundlePath);
Expand Down Expand Up @@ -200,24 +202,39 @@ int enableForApp(NSString* bundlePath)

ASSERT([fm createSymbolicLinkAtPath:[jbroot(bundlePath) stringByAppendingString:@"/.jbroot"] withDestinationPath:jbroot(@"/") error:nil]);

ASSERT(spawnBootstrap((char*[]){"/usr/bin/uicache","-p", bundlePath.UTF8String, NULL}, nil, nil) == 0);
NSString* log=nil;
NSString* err=nil;
if(spawnBootstrap((char*[]){"/usr/bin/uicache","-p", bundlePath.UTF8String, NULL}, &log, &err) != 0) {
STRAPLOG("%@\nERR:%@", log, err);
ABORT();
}
}
else if([appInfo[@"CFBundleIdentifier"] hasPrefix:@"com.apple."]
|| [NSFileManager.defaultManager fileExistsAtPath:[bundlePath stringByAppendingString:@"/../_TrollStore"]])
{
ASSERT(backupApp(bundlePath) == 0);

ASSERT([fm createSymbolicLinkAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] withDestinationPath:jbroot(@"/") error:nil]);

ASSERT(spawnBootstrap((char*[]){"/usr/bin/uicache","-s","-p", rootfsPrefix(bundlePath).UTF8String, NULL}, nil, nil) == 0);

NSString* log=nil;
NSString* err=nil;
if(spawnBootstrap((char*[]){"/usr/bin/uicache","-s","-p", rootfsPrefix(bundlePath).UTF8String, NULL}, &log, &err) != 0) {
STRAPLOG("%@\nERR:%@", log, err);
ABORT();
}
}
else
{
ASSERT(backupApp(bundlePath) == 0);

ASSERT([fm createSymbolicLinkAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] withDestinationPath:jbroot(@"/") error:nil]);

ASSERT(spawnBootstrap((char*[]){"/usr/bin/uicache","-s","-p", rootfsPrefix(bundlePath).UTF8String, NULL}, nil, nil) == 0);
NSString* log=nil;
NSString* err=nil;
if(spawnBootstrap((char*[]){"/usr/bin/uicache","-s","-p", rootfsPrefix(bundlePath).UTF8String, NULL}, &log, &err) != 0) {
STRAPLOG("%@\nERR:%@", log, err);
ABORT();
}
}

return 0;
Expand All @@ -242,17 +259,36 @@ int disableForApp(NSString* bundlePath)
else if([appInfo[@"CFBundleIdentifier"] hasPrefix:@"com.apple."]
|| [NSFileManager.defaultManager fileExistsAtPath:[bundlePath stringByAppendingString:@"/../_TrollStore"]])
{

struct stat st;
if(lstat([bundlePath stringByAppendingString:@"/.jbroot"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.prelib"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.prelib"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.preload"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.preload"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.rebuild"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.rebuild"] error:nil]);

ASSERT(restoreApp(bundlePath) == 0);
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] error:nil]);

ASSERT(spawnBootstrap((char*[]){"/usr/bin/uicache","-s","-p", rootfsPrefix(bundlePath).UTF8String, NULL}, nil, nil) == 0);
}
else
{
//should be an appstored app

ASSERT(restoreApp(bundlePath) == 0);
struct stat st;
if(lstat([bundlePath stringByAppendingString:@"/.jbroot"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.prelib"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.prelib"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.preload"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.preload"] error:nil]);
if(lstat([bundlePath stringByAppendingString:@"/.rebuild"].fileSystemRepresentation, &st)==0)
ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.rebuild"] error:nil]);

ASSERT([fm removeItemAtPath:[bundlePath stringByAppendingString:@"/.jbroot"] error:nil]);
ASSERT(restoreApp(bundlePath) == 0);

//unregister or respring to keep app's icon on home screen
ASSERT(spawnBootstrap((char*[]){"/usr/bin/uicache","-u", rootfsPrefix(bundlePath).UTF8String, NULL}, nil, nil) == 0);
Expand Down
154 changes: 131 additions & 23 deletions Bootstrap/AppViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

@interface PrivateApi_LSApplicationWorkspace
- (NSArray*)allInstalledApplications;
- (bool)openApplicationWithBundleID:(id)arg1;
- (BOOL)openApplicationWithBundleID:(id)arg1;
- (NSArray*)privateURLSchemes;
- (NSArray*)publicURLSchemes;
- (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1
Expand Down Expand Up @@ -93,21 +93,29 @@ - (void)viewDidLoad {
[refreshControl addTarget:self action:@selector(startRefresh) forControlEvents:UIControlEventValueChanged];
self.tableView.refreshControl = refreshControl;

[self updateData];
[self updateData:YES];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(startRefresh)
selector:@selector(startRefresh2)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}

- (void)startRefresh {
[self.tableView.refreshControl beginRefreshing];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self updateData];
[self updateData:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView.refreshControl endRefreshing];
});
});
}

- (void)startRefresh2 {
[self.tableView.refreshControl beginRefreshing];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self updateData:NO];
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadSearch];
[self.tableView reloadData];
[self.tableView.refreshControl endRefreshing];
});
});
Expand All @@ -119,7 +127,29 @@ - (void)viewWillAppear:(BOOL)animated {
[self.tableView.refreshControl endRefreshing];
}

- (void)updateData {
-(BOOL)tweakEnabled:(AppList*)app {
struct stat st;
if(lstat([app.bundleURL.path stringByAppendingPathComponent:@".jbroot"].fileSystemRepresentation, &st)==0) {
return YES;
}

if(!isDefaultInstallationPath(app.bundleURL.path)) {
return NO;
}

NSString* uuidPath = [app.bundleURL.path stringByDeletingLastPathComponent];
NSString* backupFlag = [uuidPath stringByAppendingPathComponent:@".appbackup"];
NSString* backupPath = [[uuidPath stringByAppendingPathComponent:app.bundleURL.path.lastPathComponent] stringByAppendingPathExtension:@"appbackup"];
SYSLOG("uuidPath=%@, backupFlag=%@, backupPath=%@", uuidPath, backupFlag, backupPath);
if([NSFileManager.defaultManager fileExistsAtPath:backupFlag] && [NSFileManager.defaultManager fileExistsAtPath:backupPath]) {
//if the app has been successfully backed up but failed to enable tweak injection for some reason, or the app bundle is overwritten, we will still show that tweak injection has been enabled so that the user will have the chance to restore the app in appenabler
return YES;
}

return NO;
}

- (void)updateData:(BOOL)sort {
NSMutableArray* applications = [NSMutableArray new];
PrivateApi_LSApplicationWorkspace* _workspace = [NSClassFromString(@"LSApplicationWorkspace") new];
NSArray* allInstalledApplications = [_workspace allInstalledApplications];
Expand Down Expand Up @@ -147,20 +177,77 @@ - (void)updateData {
[app.bundleURL.path stringByAppendingString:@"/.TrollStorePresistenceHelper"]])
continue;

if([NSFileManager.defaultManager fileExistsAtPath:
[app.bundleURL.path stringByAppendingString:@"/.Bootstrap"]])
continue;

if([app.bundleURL.path.lastPathComponent isEqualToString:@"TrollStore.app"])
continue;

if([app.bundleURL.path.lastPathComponent isEqualToString:@"Bootstrap.app"])
continue;

if([app.bundleIdentifier isEqualToString:NSBundle.mainBundle.bundleIdentifier]
|| [app.bundleIdentifier isEqualToString:@"com.roothide.Bootstrap"])
continue;

[applications addObject:app];
}

NSArray *appsSortedByName = [applications sortedArrayUsingComparator:^NSComparisonResult(AppList *app1, AppList *app2) {
return [app1.name localizedStandardCompare:app2.name];
}];
if(sort)
{
NSArray *appsSortedByName = [applications sortedArrayUsingComparator:^NSComparisonResult(AppList *app1, AppList *app2) {

BOOL enabled1 = [self tweakEnabled:app1];
BOOL enabled2 = [self tweakEnabled:app2];

if((enabled1&&!enabled2) || (!enabled1&&enabled2)) {
return [@(enabled2) compare:@(enabled1)];
}

if(app1.isHiddenApp || app2.isHiddenApp) {
return (enabled1&&enabled2) ? [@(app2.isHiddenApp) compare:@(app1.isHiddenApp)] : [@(app1.isHiddenApp) compare:@(app2.isHiddenApp)];
}

return [app1.name localizedStandardCompare:app2.name];
}];

self->appsArray = appsSortedByName;
}
else
{
NSMutableArray *newapps = [NSMutableArray array];
[applications enumerateObjectsUsingBlock:^(AppList *newobj, NSUInteger idx, BOOL * _Nonnull stop) {
__block BOOL hasBeenContained = NO;
[self->appsArray enumerateObjectsUsingBlock:^(AppList *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.bundleIdentifier isEqualToString:newobj.bundleIdentifier]) {
hasBeenContained = YES;
*stop = YES;
}
}];
if (!hasBeenContained) {
[newapps addObject:newobj];
}
}];

NSMutableArray *tmpArray = [NSMutableArray array];
[self->appsArray enumerateObjectsUsingBlock:^(AppList *obj, NSUInteger idx, BOOL * _Nonnull stop) {
[applications enumerateObjectsUsingBlock:^(AppList *newobj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.bundleIdentifier isEqualToString:newobj.bundleIdentifier]) {
[tmpArray addObject:newobj];
*stop = YES;
}
}];
}];

[tmpArray addObjectsFromArray:newapps];
self->appsArray = tmpArray.copy;
}

self->appsArray = appsSortedByName;
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadSearch];
[self.tableView reloadData];
});
}

#pragma mark - Table view data source
Expand Down Expand Up @@ -206,23 +293,32 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N

AppList* app = isFiltered? filteredApps[indexPath.row] : appsArray[indexPath.row];

UIImage *image = app.icon;
cell.imageView.image = [self imageWithImage:image scaledToSize:CGSizeMake(40, 40)];
if(!app.isHiddenApp) {
UIImage *image = app.icon;
cell.imageView.image = [self imageWithImage:image scaledToSize:CGSizeMake(40, 40)];
cell.textLabel.text = app.name;
} else {
cell.textLabel.text = app.bundleIdentifier;
}

cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.bundleIdentifier;

UISwitch *theSwitch = [[UISwitch alloc] init];

if([unsupportedBundleIDs containsObject:app.bundleIdentifier])
theSwitch.enabled = NO;

struct stat st;
BOOL enabled = lstat([app.bundleURL.path stringByAppendingPathComponent:@".jbroot"].fileSystemRepresentation, &st)==0;
[theSwitch setOn:enabled];
[theSwitch setOn:[self tweakEnabled:app]];
[theSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];

cell.accessoryView = theSwitch;

UILongPressGestureRecognizer *gest = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(cellLongPress:)];
[cell.contentView addGestureRecognizer:gest];
gest.view.tag = indexPath.row | indexPath.section<<32;
gest.minimumPressDuration = 1;

return cell;
}

Expand All @@ -249,20 +345,32 @@ - (void)switchChanged:(id)sender {
}

if(status != 0) {
[AppDelegate showMesage:[NSString stringWithFormat:@"%@\n\nstderr:\n%@",log,err] title:[NSString stringWithFormat:@"code(%d)",status]];
[AppDelegate showMesage:[NSString stringWithFormat:@"%@\nstderr:\n%@",log,err] title:[NSString stringWithFormat:@"error(%d)",status]];
}

killAllForApp(app.bundleURL.path.UTF8String);

//refresh app cache list
[self updateData];
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadSearch];
[self.tableView reloadData];
});
[self updateData:NO];

[AppDelegate dismissHud];

});
}

- (void)cellLongPress:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
long tag = recognizer.view.tag;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:tag&0xFFFFFFFF inSection:tag>>32];

AppList* app = isFiltered? filteredApps[indexPath.row] : appsArray[indexPath.row];

dispatch_async(dispatch_get_global_queue(0, 0), ^{
PrivateApi_LSApplicationWorkspace* _workspace = [NSClassFromString(@"LSApplicationWorkspace") new];
[_workspace openApplicationWithBundleID:app.bundleIdentifier];
});
}
}
@end
Loading

0 comments on commit 527e7a9

Please sign in to comment.