-
-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add builtin tweak signer & more refactor
- Loading branch information
1 parent
350b99d
commit 191d9ab
Showing
12 changed files
with
685 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
@import UniformTypeIdentifiers; | ||
|
||
#import "LCTweakListViewController.h" | ||
#import "UIKitPrivate.h" | ||
#import "UIViewController+LCAlert.h" | ||
|
||
@interface LCTweakListViewController() | ||
@property(nonatomic) NSString *path; | ||
@property(nonatomic) NSMutableArray *objects; | ||
@end | ||
|
||
@implementation LCTweakListViewController | ||
|
||
- (void)loadView { | ||
[super loadView]; | ||
|
||
if (!self.path) { | ||
NSString *docPath = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject.path; | ||
self.path = [docPath stringByAppendingPathComponent:@"Tweaks"]; | ||
} | ||
[self loadPath]; | ||
|
||
UIMenu *addMenu = [UIMenu menuWithTitle:@"" image:nil identifier:nil | ||
options:UIMenuOptionsDisplayInline | ||
children:@[ | ||
[UIAction | ||
actionWithTitle:@"Tweak" | ||
image:[UIImage systemImageNamed:@"doc"] | ||
identifier:nil handler:^(UIAction *action) { | ||
[self addDylibButtonTapped]; | ||
}], | ||
[UIAction | ||
actionWithTitle:@"Folder" | ||
image:[UIImage systemImageNamed:@"folder"] | ||
identifier:nil handler:^(UIAction *action) { | ||
[self addDirectoryButtonTapped]; | ||
}] | ||
]]; | ||
self.navigationItem.rightBarButtonItem = | ||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd menu:addMenu]; | ||
} | ||
|
||
- (void)loadPath { | ||
BOOL reload = self.objects != nil; | ||
|
||
NSMutableArray *directories = [NSMutableArray new]; | ||
NSArray *files = [[NSFileManager.defaultManager contentsOfDirectoryAtPath:self.path error:nil] filteredArrayUsingPredicate: | ||
[NSPredicate predicateWithBlock:^BOOL(NSString *name, NSDictionary *bindings) { | ||
BOOL isDir; | ||
NSString *path = [self.path stringByAppendingPathComponent:name]; | ||
[NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDir]; | ||
if (isDir) { | ||
[directories addObject:name]; | ||
} | ||
return !isDir && [name hasSuffix:@".dylib"]; | ||
return YES; | ||
}]]; | ||
|
||
self.objects = [NSMutableArray new]; | ||
[self.objects addObjectsFromArray:[directories sortedArrayUsingSelector:@selector(compare:)]]; | ||
[self.objects addObjectsFromArray:[files sortedArrayUsingSelector:@selector(compare:)]]; | ||
|
||
if (reload) { | ||
[self.tableView reloadData]; | ||
} | ||
} | ||
|
||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { | ||
return 1; | ||
} | ||
|
||
/* | ||
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { | ||
return @"N items"; | ||
} | ||
*/ | ||
|
||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { | ||
return self.objects.count; | ||
} | ||
|
||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | ||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; | ||
if (!cell) { | ||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"]; | ||
} | ||
|
||
UIListContentConfiguration *config = cell.defaultContentConfiguration; | ||
config.text = self.objects[indexPath.row]; | ||
|
||
BOOL isDir; | ||
NSString *path = [self.path stringByAppendingPathComponent:self.objects[indexPath.row]]; | ||
[NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDir]; | ||
config.image = [UIImage systemImageNamed:(isDir ? @"folder.fill" : @"doc")]; | ||
|
||
if (isDir) { | ||
config.secondaryText = @"folder"; | ||
} else { | ||
NSDictionary *attrs = [NSFileManager.defaultManager attributesOfItemAtPath:path error:nil]; | ||
NSNumber *size = attrs[NSFileSize]; | ||
config.secondaryText = [NSByteCountFormatter stringFromByteCount:size.unsignedLongLongValue | ||
countStyle:NSByteCountFormatterCountStyleFile]; | ||
} | ||
|
||
cell.contentConfiguration = config; | ||
cell.selectionStyle = isDir ? | ||
UITableViewCellSelectionStyleDefault : | ||
UITableViewCellSelectionStyleNone; | ||
return cell; | ||
} | ||
|
||
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { | ||
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; | ||
if (cell.selectionStyle == UITableViewCellSelectionStyleNone) { | ||
return nil; | ||
} else { | ||
return indexPath; | ||
} | ||
} | ||
|
||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | ||
[tableView deselectRowAtIndexPath:indexPath animated:NO]; | ||
NSString *name = self.objects[indexPath.row]; | ||
LCTweakListViewController *childVC = [LCTweakListViewController new]; | ||
childVC.path = [self.path stringByAppendingPathComponent:name]; | ||
childVC.title = name; | ||
[self.navigationController pushViewController:childVC animated:YES]; | ||
} | ||
|
||
- (void)deleteItemAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void(^)(BOOL actionPerformed))handler { | ||
NSString *name = self.objects[indexPath.row]; | ||
NSString *path = [self.path stringByAppendingPathComponent:name]; | ||
[self showConfirmationDialogTitle:@"Confirm" | ||
message:[NSString stringWithFormat:@"Are you sure you want to delete %@?", name] | ||
confirmButtonTitle:@"Delete" | ||
handler:^(UIAlertAction * action) { | ||
if (action.style == UIAlertActionStyleCancel) return; | ||
NSError *error = nil; | ||
[NSFileManager.defaultManager removeItemAtPath:path error:&error]; | ||
if (error) { | ||
[self showDialogTitle:@"Error" message:error.localizedDescription]; | ||
} else { | ||
[self.objects removeObjectAtIndex:indexPath.row]; | ||
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; | ||
} | ||
handler(YES); | ||
}]; | ||
} | ||
|
||
- (UISwipeActionsConfiguration *) tableView:(UITableView *)tableView | ||
trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath { | ||
return [UISwipeActionsConfiguration configurationWithActions:@[ | ||
[UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive | ||
title:@"Delete" handler:^(UIContextualAction *action, __kindof UIView *sourceView, void (^completionHandler)(BOOL actionPerformed)) { | ||
[self deleteItemAtIndexPath:indexPath completionHandler:completionHandler]; | ||
}] | ||
]]; | ||
} | ||
|
||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { | ||
if ([keyPath isEqualToString:@"fractionCompleted"]) { | ||
NSProgress *progress = (NSProgress *)object; | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
self.progressView.progress = progress.fractionCompleted; | ||
}); | ||
} else { | ||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; | ||
} | ||
} | ||
|
||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { | ||
NSError *error; | ||
NSString *path = self.path; | ||
if (LCUtils.certificatePassword) { | ||
// Move them to a tmp folder to sign them | ||
path = [self.path stringByAppendingPathComponent:@".tmp"]; | ||
[NSFileManager.defaultManager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:@{} error:&error]; | ||
if (error) { | ||
[self showDialogTitle:@"Error" message:error.localizedDescription]; | ||
return; | ||
} | ||
} | ||
|
||
for (NSURL *url in urls) { | ||
NSString *filePath = [path stringByAppendingPathComponent:url.path.lastPathComponent]; | ||
[NSFileManager.defaultManager moveItemAtPath:url.path toPath:filePath error:&error]; | ||
if (error) { | ||
[self showDialogTitle:@"Error" message:error.localizedDescription]; | ||
return; | ||
} | ||
} | ||
|
||
if (!LCUtils.certificatePassword) { | ||
// JIT stop here | ||
return; | ||
} | ||
|
||
// Setup a fake app bundle for signing | ||
NSString *tmpExecPath = [path stringByAppendingPathComponent:@"LiveContainer.tmp"]; | ||
NSString *tmpInfoPath = [path stringByAppendingPathComponent:@"Info.plist"]; | ||
[NSFileManager.defaultManager copyItemAtPath:NSBundle.mainBundle.executablePath toPath:tmpExecPath error:nil]; | ||
NSMutableDictionary *info = NSBundle.mainBundle.infoDictionary.mutableCopy; | ||
info[@"CFBundleExecutable"] = @"LiveContainer.tmp"; | ||
[info writeToFile:tmpInfoPath atomically:YES]; | ||
|
||
dispatch_block_t handler = ^{ | ||
if (error) { | ||
[self showDialogTitle:@"Error while signing tweaks" message:error.localizedDescription]; | ||
} else { | ||
// Move tweaks back | ||
for (NSURL *url in urls) { | ||
NSString *fromPath = [path stringByAppendingPathComponent:url.path.lastPathComponent]; | ||
NSString *toPath = [self.path stringByAppendingPathComponent:url.path.lastPathComponent]; | ||
[NSFileManager.defaultManager moveItemAtPath:fromPath toPath:toPath error:&error]; | ||
} | ||
} | ||
|
||
// Remove tmp folder | ||
[NSFileManager.defaultManager removeItemAtPath:path error:nil]; | ||
[progress removeObserver:self forKeyPath:@"fractionCompleted"]; | ||
[self.progressView removeFromSuperview]; | ||
[self loadPath]; | ||
}; | ||
|
||
NSProgress *progress = [LCUtils signAppBundle:appPathURL | ||
completionHandler:^(BOOL success, NSError *_Nullable signError) { | ||
error = signError; | ||
dispatch_async(dispatch_get_main_queue(), handler); | ||
}]; | ||
|
||
if (progress) { | ||
[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; | ||
} | ||
} | ||
|
||
- (void)addDirectoryButtonTapped { | ||
[self showInputDialogTitle:@"Add folder" message:@"Enter name" placeholder:@"Name" callback:^(NSString *name) { | ||
NSError *error; | ||
NSString *path = [self.path stringByAppendingPathComponent:name]; | ||
[NSFileManager.defaultManager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:@{} error:&error]; | ||
[self loadPath]; | ||
return error.localizedDescription; | ||
}]; | ||
} | ||
|
||
- (void)addDylibButtonTapped { | ||
UIDocumentPickerViewController *documentPickerVC = [[UIDocumentPickerViewController alloc] | ||
initForOpeningContentTypes:@[[UTType typeWithFilenameExtension:@"dylib" conformingToType:UTTypeData]] | ||
asCopy:YES]; | ||
documentPickerVC.allowsMultipleSelection = YES; | ||
documentPickerVC.delegate = self; | ||
[self presentViewController:documentPickerVC animated:YES completion:nil]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@import UIKit; | ||
|
||
@interface LCTweakListViewController : UITableViewController <UIDocumentPickerDelegate> | ||
@end |
Oops, something went wrong.