//
//  ViewController.m
//  g0blinTV
//
//  Created by Kevin Bradley on 1/15/18.
//  Copyright © 2018 Sticktron. All rights reserved.
//

#import "ViewController.h"

#include <string.h>
#include "v0rtex2.h"
#include "common2.h"
#include "offsets2.h"
#include "kernel.h"
#include "kpp.h"
#include "remount.h"
#include "bootstrap.h"
#include <sys/utsname.h>
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
#import "SVProgressHUD.h"

#import "UIColor+Additions.h"

//#define GRAPE [UIColor colorWithRed:0.5 green:0 blue:1 alpha:1]
#define GRAPE [UIColor colorFromHex:@"25b24a"]

@implementation FocusedButton

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        self.layer.cornerRadius = 5;
        self.backgroundColor = [UIColor lightGrayColor];
        self.focusColor = [UIColor redColor];
        self.unfocusedColor = [UIColor lightGrayColor];
    }
    return self;
}

- (void)buttonColors:(UIColor *)focusedColor andUnfocused:(UIColor *)unfocusedColor {
    
    self.focusColor = focusedColor;
    self.unfocusedColor = unfocusedColor;
    self.backgroundColor = unfocusedColor;
}

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
    
    [coordinator addCoordinatedAnimations:^{
        
        if (self.focused) {
            
            self.backgroundColor = self.focusColor;
            self.transform = CGAffineTransformMakeScale(1.1, 1.1);
            self.layer.shadowColor = [UIColor blackColor].CGColor;
            self.layer.shadowOffset = CGSizeMake(0, 27);
            self.layer.shadowOpacity = 0.25;
            self.layer.shadowRadius = 10;
            self.clipsToBounds = NO;
            
        }
        else {
            
            self.transform = CGAffineTransformIdentity;
            self.backgroundColor = self.unfocusedColor;
            self.clipsToBounds = YES;
        }
        
    } completion:nil];
}


@end


@interface ViewController ()
{
    BOOL _force;
    BOOL _disabled;
}
@property (weak, nonatomic) IBOutlet UIImageView *logoView;
@property (weak, nonatomic) IBOutlet FocusedButton *goButton;
@property (weak, nonatomic) IBOutlet FocusedButton *reinstallBootstrap;
@property (weak, nonatomic) IBOutlet UITextView *consoleView;
@property (weak, nonatomic) IBOutlet UIButton *settingsButton;
@property (weak, nonatomic) IBOutlet UILabel *reinstallBootstrapLabel;
@end


static task_t tfp0;
static uint64_t kslide;
static uint64_t kbase;
static uint64_t kcred;

BOOL respringNeeded;
BOOL fun;
AVPlayer *player;
AVPlayerViewController *cont;


@implementation ViewController

- (IBAction)toggleForce:(id)sender
{
  
    _force = !_force;
    if (_force)
    {
        [self.reinstallBootstrap setTitle:@"Force Reinstall Bootstrap" forState:UIControlStateNormal];
        [self.reinstallBootstrap setTitle:@"Force Reinstall Bootstrap" forState:UIControlStateFocused];
    } else {
        [self.reinstallBootstrap setTitle:@"Dont Reinstall Bootstrap" forState:UIControlStateNormal];
        [self.reinstallBootstrap setTitle:@"Dont Reinstall Bootstrap" forState:UIControlStateFocused];
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [self.goButton buttonColors:GRAPE
                       andUnfocused:[UIColor darkTextColor]];
    
    [self.reinstallBootstrap buttonColors:GRAPE
                   andUnfocused:[UIColor darkTextColor]];
    
    
    //self.consoleView.layer.cornerRadius = 6;
    self.consoleView.text = nil;
    self.consoleView.editable = false;
    self.consoleView.userInteractionEnabled = true;
    self.consoleView.layoutManager.allowsNonContiguousLayout = NO;
    self.goButton.layer.cornerRadius = 16;
    
    _force = NO;
    //self.reinstallBootstrapLabel.hidden = YES;
    
    
    // print kernel version
    struct utsname u;
    uname(&u);
    [self log:[NSString stringWithFormat:@"%s \n", u.version]];
    _disabled = NO;
    // abort if already jailbroken
    //if (strstr(u.version, "MarijuanARM")) {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithUTF8String:u.version] message:nil preferredStyle:UIAlertControllerStyleAlert];
    [self presentViewController:alert animated:YES completion:nil];
    if ([[NSString stringWithUTF8String:u.version] containsString:@"MarijuanARM"]){
    self.goButton.enabled = NO;
        _disabled = YES;
        //self.goButton.backgroundColor = UIColor.darkGrayColor;
        [self.goButton setTitle:@"jailbroke yo!" forState:UIControlStateDisabled];
        [self.goButton buttonColors:UIColor.darkGrayColor
                       andUnfocused:[UIColor darkGrayColor]];
    }
    
    // try to load offsets for device
 /*
    if (init_offsets() == KERN_SUCCESS) {
        [self log:@"Ready. \n"];
    } else {
        self.goButton.enabled = NO;
        self.goButton.backgroundColor = UIColor.darkGrayColor;
        [self.goButton setTitle:@"device not supported" forState:UIControlStateDisabled];
    }
    */
    // fun
    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fun:)];
    doubleTap.delaysTouchesBegan = YES;
    doubleTap.numberOfTapsRequired = 3;
    [self.logoView addGestureRecognizer:doubleTap];
    self.logoView.userInteractionEnabled = YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)log:(NSString *)text {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView setAnimationsEnabled:NO];
        
        self.consoleView.text = [NSString stringWithFormat:@"%@%@ \n", self.consoleView.text, text];
        [self.consoleView scrollRangeToVisible:NSMakeRange([self.consoleView.text length], 0)];
        
        [UIView setAnimationsEnabled:YES];
    });
  
}

- (IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
    //segue exit marker
    
    //SettingsController *settingsController = segue.sourceViewController;
    //self.reinstallBootstrapLabel.hidden = !settingsController.reinstallBootstrapSwitch.on;
}

- (IBAction)go:(UIButton *)sender {
    
    if (_disabled) {
        
        LOG("Already jailbroken bro!");
        
        return;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        
        [self.consoleView.layoutManager ensureLayoutForTextContainer:self.consoleView.textContainer];
    });
    
    
    if (respringNeeded == YES) {
        [self restart];
        return;
    }
    
    self.goButton.enabled = NO;
    self.goButton.backgroundColor = UIColor.darkGrayColor;
    [self.goButton setTitle:@"jailbreaking" forState:UIControlStateDisabled];
    
    [self log:@"exploiting kernel"];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        
        offsets_t *off = get_offsets();
        if (off)
        {
            kern_return_t ret = v0rtex(off, &tfp0, &kslide, &kcred);
            if (ret != KERN_SUCCESS) {
                self.goButton.enabled = YES;
                self.goButton.backgroundColor = GRAPE;
                [self.goButton setTitle:@"try again" forState:UIControlStateNormal];
                
                [self log:@"ERROR: exploit failed \n"];
                [self kludgeBoot];
                return;
            }
            LOG("v0rtex was successful");
            
            LOG("tfp0 -> %x", tfp0);
            LOG("slide -> 0x%llx", kslide);
            kbase = kslide + 0xFFFFFFF007004000;
            LOG("kern base -> 0x%llx", kbase);
            LOG("kern cred -> 0x%llx", kcred);
            
            [self bypassKPP];
        }
        
  
    });
}

- (void)bypassKPP {
    [self log:@"pwning kernel"];
    
    if (do_kpp(1, 0, kbase, kslide, tfp0) == KERN_SUCCESS) {
        LOG("you down with kpp? yeah you know me");
        [self remount];
    } else {
        [self log:@"ERROR: kpp bypass failed \n"];
    }
}

- (void)remount {
    [self log:@"remounting"];
    
    if (do_remount(kslide) == KERN_SUCCESS) {
        [self bootstrap];
    } else {
        [self log:@"ERROR: failed to remount system partition \n"];
    }
}

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
    if (context.nextFocusedView == self.goButton)
    {
        // set background color
        self.goButton.backgroundColor = [UIColor redColor];
    }
    
}

//we know retrying with reboot, and if we detect faiure wait a few seconds and try again to force a reboot
- (void)kludgeBoot
{
    LOG("== Auto retrying in 3 seconds to trigger reboot ==");
    [self log:@"== Auto retrying in 3 seconds to trigger reboot =="];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
       
        [self go:nil];
    });
}

- (void)bootstrap {
    [self log:@"bootstrapping"];
    
   // _force = NO;
    if (_force == YES) {
        //_force = YES;
        [self log:@"(forcing reinstall)"];
    }
    
    if (do_bootstrap(_force) == KERN_SUCCESS) {
        [self finish];
    } else {
        [self log:@"ERROR: failed to bootstrap \n"];
    }
}

+ (NSString *)returnForProcess:(NSString *)call
{
    if (call==nil)
        return 0;
    char line[200];
    NSLog(@"running process: %@", call);
    FILE* fp = popen([call UTF8String], "r");
    NSMutableArray *lines = [[NSMutableArray alloc]init];
    if (fp)
    {
        while (fgets(line, sizeof line, fp))
        {
            NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
            s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            [lines addObject:s];
        }
    }
    pclose(fp);
    return [lines componentsJoinedByString:@"\n"];
}

- (void)finish {
    
    NSString *hostname = [ViewController returnForProcess:@"/bin/hostname"];
    
    [self log:@"device is now jailbroken!"];
    [self log:@""];
    [self log:[NSString stringWithFormat:@"SSH server is ready at %@.local", hostname]];
    [self log:@"change your root/mobile passwords"];
    [self log:@""];
    [self log:@"reloading daemons..."];
    
    sleep(2);
    RunCmd("/usr/libexec/substrate");
    LOG("reloading daemons...");
    pid_t pid;
    posix_spawn(&pid, "/bin/launchctl", 0, 0, (char**)&(const char*[]){"/bin/launchctl", "load", "/Library/LaunchDaemons/0.reload.plist", NULL}, NULL);
    waitpid(pid, 0, 0);
    
    sleep(2);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        respringNeeded = YES;
        [self log:@""];
        [self log:@"respring to reload Applications and Tweaks"];
        [self.goButton setTitle:@"respring" forState:UIControlStateNormal];
        self.goButton.enabled = YES;
    });

}

- (void)showHUD
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [SVProgressHUD setBackgroundColor:[UIColor clearColor]];
        [SVProgressHUD show];
     });
}


- (void)restart {
    
    [self showHUD];
    LOG("Running uicache...");
    
    //pid_t pd;
    //const char* args[] = { "PineBoard", "HeadBoard", "lsd", NULL };
    //posix_spawn(&pid, "/jb/usr/bin/killall", NULL, NULL, (char* const*)args, NULL);
    //posix_spawn(&pd, "/usr/bin/uicache", 0, 0, (char**)&(const char*[]){"/usr/bin/uicache", NULL}, NULL);
    //waitpid(pd, 0, 0);
    RunCmd("/usr/libexec/substrate");
    RunCmd("/usr/bin/uicache");
}




@end
