//
//  ViewController.m
//  meridianTV
//
//  Created by Kevin Bradley on 6/24/18.
//  Copyright © 2018 Ben Sparkes. All rights reserved.
//

#import "ViewController.h"
#import "helpers.h"
#import "jailbreak.h"
#import <sys/utsname.h>
#import <Foundation/Foundation.h>
#import "PureLayout.h"
#import "UIColor+Additions.h"
#import <CoreFoundation/CoreFoundation.h>
#import "Reachability.h"
//electra

#import "NSTask.h"

#include "async_wake.h"
#include "fun.h"
#include "codesign.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>

CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
CF_EXPORT CFDictionaryRef _CFCopyServerVersionDictionary(void);

static ViewController *currentViewController;

typedef NS_ENUM(NSInteger, BSInstallType) {
    
    BSInstallTypeUnsupported,
    BSInstallTypeMeridian,
    BSInstallTypeElectra,
};

@interface ViewController () {
    
    BOOL _respringReady;
    UITapGestureRecognizer *playPauseOrMenuDoubleTapRecognizer;
    BOOL kppless;
}


@property (readwrite, assign) BSInstallType installMode; //0 = meridian 1 = electra
@property (strong, nonatomic) UIButton *goButton;
@property (strong, nonatomic) UIButton *creditsButton;
@property (strong, nonatomic) UIButton *websiteButton;
@property (strong, nonatomic) UIActivityIndicatorView *progressSpinner;
@property (strong, nonatomic) UITextView *textArea;
@property (strong, nonatomic) UILabel *versionLabel;
@end

NSString *Version = @"backr00m: 1.2.1";
NSOperatingSystemVersion osVersion;

id thisClass;

bool has_run_once = false;
bool jailbreak_has_run = false;

@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;
}

- (id)initForAutoLayout {
    
    self = [super initForAutoLayout];
    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

@implementation ViewController

#define NB [NSBundle mainBundle]
#define UD [NSUserDefaults standardUserDefaults]
#define FM [NSFileManager defaultManager]
#define LAST_UPDATE_CHECK @"lastSelfUpdateCheck"

#define UPDATE_CHECK_FREQUENCY @"updateCheckFrequency"
#define NOW_STRING        [df stringFromDate:[NSDate date]]
#define CHECK_FREQ        [UD integerForKey:UPDATE_CHECK_FREQUENCY]

- (void)_updateDateCheck
{
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    [df setDateFormat:@"MMddyy_HHmmss"];
    NSString *newDate = [df stringFromDate:[NSDate date]];
    [UD setObject:newDate forKey:LAST_UPDATE_CHECK];
}
- (NSString *)_lastCheckDate
{
    return [UD objectForKey:LAST_UPDATE_CHECK];
}

- (NSString *)updatePlist {
    
    return [[ViewController documentsFolder] stringByAppendingPathComponent:@"meridian.plist"];
    
}

-(BOOL)_shouldCheckUpdate
{
    // return true;
    
    if (![FM fileExistsAtPath:[self updatePlist]])
    {
        return YES;
    }
    
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    [df setDateFormat:@"MMddyy_HHmmss"];
    NSDate *myDate = [df dateFromString: [self _lastCheckDate]];
    NSDate *now = [df dateFromString:NOW_STRING];
    double timeSinceDate = [now timeIntervalSinceDate:myDate];
    
    double minutes = (timeSinceDate / 60);
    
    NSLog(@"minutes: %g",minutes ); //minutes since check
    
    if (minutes > CHECK_FREQ) //FIXME change to higher value!!!
    {
        [self _updateDateCheck];
        return YES;
        
    }
    
    return NO;
}

+ (NSString *)documentsFolder
{
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    return [paths objectAtIndex:0];
}

- (NSURL *)remotePathURL { return [NSURL URLWithString:@"https://nitosoft.com/ATV4/backr00m/meridian.plist"]; };

//us.scw.afctwoadd

+ (void)validateFile:(NSString *)inputFile withChecksum:(NSString *)checksum completion:(void(^)(BOOL valid))completeBlock
{
    //LOG_SELF;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSTask *sslTask = [[NSTask alloc] init];
        NSPipe *sspipe = [[NSPipe alloc] init];
        NSFileHandle *ssh = [sspipe fileHandleForReading];
        
        [sslTask setLaunchPath:@"/usr/bin/openssl"];
        
        [sslTask setArguments:[NSArray arrayWithObjects:@"sha1", inputFile, nil]];
        [sslTask setStandardOutput:sspipe];
        [sslTask setStandardError:sspipe];
        [sslTask launch];
        
        NSData *outData = [ssh readDataToEndOfFile];
        
        [sslTask waitUntilExit];
        
        NSString *outputString = [[[NSString alloc] initWithData:outData
                                                        encoding:NSASCIIStringEncoding]
                                  stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        
        //example outputString: SHA1(~/Documents/Tether/AppleTV2,1_4.3_8F455_Restore.ipsw)= b6a2b0baae79daf95f75044c12946839c662d01d
        
        //b6a2b0baae79daf95f75044c12946839c662d01d cleaned up
        NSString *outputSHA = [[[outputString componentsSeparatedByString:@"="] lastObject] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        
        
        
        NSLog(@"sha1: %@ against: %@", outputSHA, checksum);
        if ([outputSHA isEqualToString:checksum])
        {
            sslTask = nil;
            sspipe = nil;
            completeBlock(YES);
        }
        sslTask = nil;
        sspipe = nil;
        completeBlock(NO);
    });
   
}

- (void)checkForUpdate //change update check to look for a typedef int rather than bool, then we can say 0 == update nitotv 1 = update essential 2 = both -1 = none?
{
    @autoreleasepool {
        if ([self _shouldCheckUpdate] == FALSE)
        {
            NSLog(@"checked within the last %li minutes!!!", (long)CHECK_FREQ);
            //return nil;
            
            return;
        }
        
        BOOL internetAvailable = [Reachability isInternetReachable];
        if (internetAvailable == TRUE)
        {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
                 NSDictionary *theDict = [NSDictionary dictionaryWithContentsOfURL:[self remotePathURL]];
                
                //lastUpdated
                //bootstrapInfo
                //meridianToolsInfo
                //electraToolsInfo
                
                //version
                //link
                //SHA1
                
                NSLog(@"science bro: %@", theDict);
                
            });
            
           
            
//            NSArray *updateArray = [packageManagement basicEssentialUpdatesAvailable];
//            if ([updateArray count] > 0)
//            {
//                [[NSNotificationCenter defaultCenter] postNotificationName:@"CheckForUpdate" object:updateArray userInfo:nil];
//                //return updateArray;
//            }
        }
    }
    //return nil;
    
}

- (void)checkForUpdatedPayloads {
    
    
}

+ (instancetype)currentViewController {
    return currentViewController;
}

    #define PROC_PIDPATHINFO_MAXSIZE  (4*MAXPATHLEN)

- (void)customTVSetup {
    
    //redudant
    /*
    if (kCFCoreFoundationVersionNumber < 1443 || kCFCoreFoundationVersionNumber > 1445.32){
        [self.goButton setEnabled:NO];
        [self.goButton setTitle:@"Version Error" forState:UIControlStateNormal];
        
    }
    uint32_t flags;
    csops(getpid(), CS_OPS_STATUS, &flags, 0);
    if ((flags & CS_PLATFORM_BINARY)){
        [self.goButton setEnabled:NO];
        [self.goButton setTitle:@"Already Jailbroken" forState:UIControlStateNormal];

        
    }
    */
    

    
    _respringReady = FALSE;
    
    [self writeTextPlain:@">"];
    
    self.installMode = BSInstallTypeUnsupported;
    if (SYSTEM_VERSION_EQUAL_TO(@"10.2.2")){
        self.installMode = BSInstallTypeMeridian;
        [self writeTextPlain:@"> 10.2.2 detected, using Meridian based jailbreak. Thanks PsychoTea!"];
    } else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0") && SYSTEM_VERSION_LESS_THAN(@"11.2") ){
        self.installMode = BSInstallTypeElectra;
        [self writeTextPlain:@"> 11.x detected, using Electra based jailbreak. Thanks Electra Team!"];
    }
    
    UIImageView *bgImageView = [[UIImageView alloc] initForAutoLayout];
    [self.view addSubview:bgImageView];
    [bgImageView autoPinEdgesToSuperviewEdges];
    [bgImageView setImage:[UIImage imageNamed:@"bgTemp.jpg"]];
    
    UILabel *meridianTV = [[UILabel alloc] initForAutoLayout];
    [self.view addSubview:meridianTV];
    [meridianTV autoAlignAxisToSuperviewAxis:ALAxisVertical];
    [meridianTV autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:160];
    [meridianTV setFont:[UIFont systemFontOfSize:205 weight:UIFontWeightHeavy]];
    [meridianTV setText:@"backr00m"];
    [meridianTV setTextColor:[UIColor whiteColor]];
    
    self.goButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.goButton autoSetDimensionsToSize:CGSizeMake(250, 100)];
    [self.goButton setTitle:@"jailbreak" forState:UIControlStateFocused];
    //[self.goButton buttonColors:[UIColor colorFromHex:@"DB1E00"]
      //             andUnfocused:[UIColor darkTextColor]];
    [self.view addSubview:self.goButton];
    [self.goButton autoCenterInSuperview];
    [self.goButton addTarget:self action:@selector(goButtonPressed:) forControlEvents:UIControlEventPrimaryActionTriggered];
    
    self.versionLabel = [[UILabel alloc] initForAutoLayout];
    self.versionLabel.textColor = [UIColor whiteColor];
    [self.view addSubview:self.versionLabel];
    [self.versionLabel autoAlignAxisToSuperviewAxis:ALAxisVertical];
    [self.versionLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.goButton withOffset:10];
    
    self.textArea = [[UITextView alloc] initForAutoLayout];
    [self.view addSubview:self.textArea];
    [self.textArea autoAlignAxisToSuperviewAxis:ALAxisVertical];
    [self.textArea autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.versionLabel];
    [self.textArea autoMatchDimension:ALDimensionWidth toDimension:ALDimensionWidth ofView:self.view withMultiplier:0.8];
    [self.textArea autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:40];
    //[self.goButton setBackgroundColor:[UIColor colorFromHex:@"DB1E00"]];
    [self.goButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [self.goButton setTitleColor:[UIColor blackColor] forState:UIControlStateDisabled];
    [self.goButton setTitleColor:[UIColor blackColor] forState:UIControlStateFocused];
    [self.textArea setBackgroundColor:[UIColor blackColor]];
    [self.textArea setTextColor:[UIColor colorFromHex:@"CACBCA"]];
    //DB1E00
    //CACBCA
    
    
    
    
}


-(void)handleDoubleTapMenuOrPlayPause:(UITapGestureRecognizer *)sender {
    

    if (sender.state == UIGestureRecognizerStateEnded) {
        
        
        NSString *kpplessString = @"KPPless: OFF";
        if (kppless) {
            
            kpplessString = @"KPPless: ON";
            
        }
        
        UIAlertController *alertController = [UIAlertController
                                              alertControllerWithTitle:@"KPPless?"
                                              message:@"Turn KPPless on if you are attempting to futurerestore to a newer version"
                                              preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *kpplessAction = [UIAlertAction
                                             actionWithTitle:kpplessString
                                             style:UIAlertActionStyleDefault
                                             handler:^(UIAlertAction *action)
                                             {
                                                 
                                                 kppless = !kppless;
                                                 
                                             }];
        
        [alertController addAction:kpplessAction];
        
        UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
        
        [alertController addAction:cancel];
        
        [self presentViewController:alertController animated:YES completion:nil];
        
        
    }
}



- (IBAction)tappedOnSetNonce:(id)sender {
    __block NSString *generatorToSet = nil;
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Set the system boot nonce on jailbreak") message:localize(@"Enter the generator for the nonce you want the system to generate on boot") preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:localize(@"Cancel") style:UIAlertActionStyleDefault handler:nil]];
    UIAlertAction *set = [UIAlertAction actionWithTitle:localize(@"Set") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        const char *generatorInput = [alertController.textFields.firstObject.text UTF8String];
        char compareString[22];
        uint64_t rawGeneratorValue;
        sscanf(generatorInput, "0x%16llx",&rawGeneratorValue);
        sprintf(compareString, "0x%016llx", rawGeneratorValue);
        if(strcmp(compareString, generatorInput) != 0) {
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"Failed to validate generator") preferredStyle:UIAlertControllerStyleAlert];
            [alertController addAction:[UIAlertAction actionWithTitle:localize(@"OK") style:UIAlertActionStyleDefault handler:nil]];
            [self presentViewController:alertController animated:YES completion:nil];
            return;
        }
        generatorToSet = [NSString stringWithUTF8String:generatorInput];
        [userDefaults setObject:generatorToSet forKey:@K_GENERATOR];
        [userDefaults synchronize];
        uint32_t flags;
        csops(getpid(), CS_OPS_STATUS, &flags, 0);
        UIAlertController *alertController = nil;
        if ((flags & CS_PLATFORM_BINARY)) {
            alertController = [UIAlertController alertControllerWithTitle:localize(@"Notice") message:localize(@"The system boot nonce will be set the next time you enable your jailbreak") preferredStyle:UIAlertControllerStyleAlert];
        } else {
            alertController = [UIAlertController alertControllerWithTitle:localize(@"Notice") message:localize(@"The system boot nonce will be set once you enable the jailbreak") preferredStyle:UIAlertControllerStyleAlert];
        }
        [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
        [self presentViewController:alertController animated:YES completion:nil];
    }];
    [alertController addAction:set];
    [alertController setPreferredAction:set];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.placeholder = [NSString stringWithFormat:@"%s", genToSet()];
    }];
    [self presentViewController:alertController animated:YES completion:nil];
}

- (void)setGoButtonTitle:(NSString *)title {
    
    dispatch_async(dispatch_get_main_queue(), ^{
  
        [self.goButton setTitle:title forState:UIControlStateNormal];
        [self.goButton setTitle:title forState:UIControlStateDisabled];
        [self.goButton setTitle:title forState:UIControlStateFocused];
        
    });
    
}


- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    kppless = FALSE;
    
    thisClass = self;
    
    
    [self customTVSetup];
    
    [self.goButton.layer setCornerRadius:5];
    [self.creditsButton.layer setCornerRadius:5];
    [self.websiteButton.layer setCornerRadius:5];
    
    NSString *buildDate = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%s", bundled_file("build_time")]
                                                    encoding:NSUTF8StringEncoding
                                                       error:nil];
    [self.versionLabel setText:[NSString stringWithFormat:@"%@: %@", Version, buildDate]];
    

    
    jailbreak_has_run = check_for_jailbreak();
    
    //[self doUpdateCheck];
    
    // Log current device and version info
    osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
    
    [self writeTextPlain:[NSString stringWithFormat:@"> %@", Version]];
    
    [self writeTextPlain:@"> Includes software / explots by Ian Beer, CoolStar, Jaywalker, nitoTV, nullpixel, PsychoTea, pwn20wnd, Siguza, stek29 and tihmstar" ];
    
    if (self.installMode == BSInstallTypeUnsupported) {
        
        [self writeTextPlain:@"> backr00m does not work on versions of iOS other than iOS 10.2.2, 11.0 and 11.1."];
        [self writeTextPlain:[NSString stringWithFormat:@"> found iOS version %@", [self getVersionString]]];
        [self.goButton setHidden:YES];
        return;
        
    }
    
    /*
    if (osVersion.majorVersion != 10) {
        [self writeTextPlain:@"> Meridian does not work on versions of iOS other than iOS 10."];
        [self writeTextPlain:[NSString stringWithFormat:@"> found iOS version %@", [self getVersionString]]];
        [self.goButton setHidden:YES];
        return;
    }
    */
    
    
    [self writeTextPlain:[NSString stringWithFormat:@"> %s on iOS %@ (Build %@)",
                          [self getDeviceIdentifier],
                          [self getVersionString],
                          [self getBuildString]]];
    
    
    
    
    
    if (jailbreak_has_run) {
        [self writeTextPlain:@"> already jailbroken."];
        
        // set done button
        [self.goButton setTitle:@"done" forState:UIControlStateFocused];
        
        // aaaaand grey it out
        [self.goButton setEnabled:NO];
        [self.goButton setAlpha:0.5];
        
        return;
    }
    /*
    // Device is already jailbroken, but not with Meridian
    if (file_exists("/private/var/lib/dpkg/status") == 0 &&
        file_exists("/meridian/.installed") != 0) {
        [self writeTextPlain:@"this device has already been jailbroken with another tool. please run Cydia Eraser to wipe this device to ensure you encounter no issues with Meridian"];
        
        [self.goButton setTitle:@"please erase" forState:UIControlStateFocused];
        [self.goButton setEnabled:NO];
        [self.goButton setAlpha:0.5];
        
        return;
    }
    */
    
    
    if (self.installMode == BSInstallTypeMeridian) {
        
        /*
        
        playPauseOrMenuDoubleTapRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTapMenuOrPlayPause:)];
        playPauseOrMenuDoubleTapRecognizer.numberOfTapsRequired = 2;
        playPauseOrMenuDoubleTapRecognizer.allowedPressTypes = @[[NSNumber numberWithInteger:UIPressTypePlayPause], [NSNumber numberWithInteger:UIPressTypeMenu]];
        [self.view addGestureRecognizer:playPauseOrMenuDoubleTapRecognizer];
        */
        if ([self shouldShowTimer]){
            
            [self showWaitTimer];
            
        }
        
    } else {
        
        playPauseOrMenuDoubleTapRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tappedOnSetNonce:)];
        playPauseOrMenuDoubleTapRecognizer.numberOfTapsRequired = 2;
        playPauseOrMenuDoubleTapRecognizer.allowedPressTypes = @[[NSNumber numberWithInteger:UIPressTypePlayPause], [NSNumber numberWithInteger:UIPressTypeMenu]];
        [self.view addGestureRecognizer:playPauseOrMenuDoubleTapRecognizer];
        
    }
    

    NSLog(@"App bundle directory: %s", bundle_path());
    
}

- (BOOL)shouldShowTimer {
    
    int waitTime;
    return ((waitTime = 90 - uptime()) > 0);
}


- (void)showWaitTimer {

    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
        int waitTime;
        while ((waitTime = 90 - uptime()) > 0) {
            dispatch_async(dispatch_get_main_queue(), ^{
                
                NSString *wt2 = [NSString stringWithFormat:@"wait: %d", waitTime];
                //NSLog(@"%@", wt2);
                
                [self.goButton setTitle:wt2 forState:UIControlStateFocused];
                [self.goButton setTitle:wt2 forState:UIControlStateNormal];
                [self.goButton setTitle:wt2 forState:UIControlStateDisabled];
                [self.goButton setTitle:wt2 forState:UIControlStateHighlighted];
                
                //[self.goButton setEnabled:false];
                [self.goButton setAlpha:0.6];
            });
            
            sleep(1);
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.goButton setTitle:@"go" forState:UIControlStateFocused];
            [self.goButton setEnabled:true];
            [self.goButton setAlpha:1];
            
            [self writeTextPlain:@"> ready."];
        });
    });
    
    
}

- (IBAction)goButtonPressed:(UIButton *)sender {
    
    if (self.installMode == BSInstallTypeElectra) {
        
        [self doit:nil];
        return;
        
    }
    
    if (sender.alpha != 1) {
        return;
    }
    // when jailbreak runs, 'go' button is
    // turned to 'respring'
    if (jailbreak_has_run) {
        int rv = respring();
        if (rv != 0) {
            [self writeTextPlain:@"failed to respring."];
        }
        return;
    }
    
    //not sure what this is all about, why would we want to reboot?
    
    /*
    
    // if we've run once, just reboot
    if (has_run_once) {
        [self.goButton setHidden:YES];
        restart_device();
        return;
    }
     
     */
    
    // set up the UI to 'running' state
    [self.goButton setEnabled:NO];
    [self.goButton setHidden:YES];
    [self.creditsButton setEnabled:NO];
    self.creditsButton.alpha = 0.5;
    [self.websiteButton setEnabled:NO];
    self.websiteButton.alpha = 0.5;
    [self.progressSpinner startAnimating];
    
    has_run_once = true;
    
    // background thread so we can update the UI
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
        int ret = makeShitHappen(self, kppless);
        
        if (ret != 0) {
            NSLog(@"MERIDIAN HAS FAILED TO RUN :(");
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [self exploitFailed];
            });
            
            return;
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self exploitSucceeded];
        });
    });
    
}

- (IBAction)websiteButtonPressed:(UIButton *)sender {
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://meridian.sparkes.zone"]
                                       options:@{}
                             completionHandler:nil];
}

- (char *)getDeviceIdentifier {
    static struct utsname u;
    uname(&u);
    return u.machine;
}

- (NSString *)getVersionString {
    return [NSString stringWithFormat:@"%ld.%ld.%ld",
            (long)osVersion.majorVersion,
            (long)osVersion.minorVersion,
            (long)osVersion.patchVersion];
}

- (NSString *)getBuildString {
    
    CFDictionaryRef dict= NULL;
    CFStringRef str = NULL;
    
    dict = _CFCopyServerVersionDictionary();
    if (dict == NULL)
        dict = _CFCopySystemVersionDictionary();
    
    str = CFDictionaryGetValue(dict, CFSTR("ProductBuildVersion"));
    //NSLog(@"pbv: %@", str);
    return (__bridge NSString *)str;

}

- (void)exploitSucceeded {
    jailbreak_has_run = true;
    
    [self writeTextPlain:@"\n> your device has been freed! \n"];
    
    [self writeTextPlain:@"note: please click 'respring' to get this party started :) \n"];
    
    
    //if you want to run anything post process on device you can do it here for diagnostic purposes.
    
    
     execprog("/bin/ps", (const char **)&(const char *[]) {
        "/bin/ps",
        "awwwx",
        NULL
    });
    
    execprog("/usr/bin/ls", (const char **)&(const char *[]) {
        "/usr/bin/ls",
        "-alR",
        "/usr",
        NULL
    });
    
    /*
    execprog("/bin/bash", (const char **)&(const char *[]) {
        "/bin/bash",
        NULL
    });
    */
    [self.progressSpinner stopAnimating];
    
    [self.goButton setEnabled:YES];
    [self.goButton setHidden:NO];
    [self.goButton setTitle:@"respring" forState:UIControlStateFocused];
    
    [self.creditsButton setEnabled:YES];
    self.creditsButton.alpha = 1;
    [self.websiteButton setEnabled:YES];
    self.websiteButton.alpha = 1;
}

- (void)exploitFailed {
    [self writeTextPlain:@"exploit failed. please reboot & try again."];
    
    
    
    
    [self.goButton setEnabled:YES];
    [self.goButton setHidden:NO];
    [self.goButton setTitle:@"reboot" forState:UIControlStateFocused];
    
    [self.creditsButton setEnabled:YES];
    [self.creditsButton setAlpha:1];
    
    [self.websiteButton setEnabled:YES];
    [self.websiteButton setAlpha:1];
    
    [self.progressSpinner stopAnimating];
}

- (void)doUpdateCheck {
    // skip the version check if we're running an internal build
    if ([Version containsString:@"Internal"]) {
        return;
    }
    
    NSURL *url = [NSURL URLWithString:@"https://meridian.sparkes.zone/latest"];
    
    NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
                                          dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *header, NSError *error) {
                                              if (error != nil) {
                                                  NSLog(@"failed to get information from the update server.");
                                                  return;
                                              }
                                              
                                              NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                              if (response != Version) {
                                                  [self doUpdatePopup:response];
                                              }
                                          }];
    
    [downloadTask resume];
}

- (void)doUpdatePopup:(NSString *)update {
    NSString *message = [NSString stringWithFormat:@"An update is available for Meridian: %@It can be downloaded from the website.", update];
    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Meridian Update"
                                                                   message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *websiteAction = [UIAlertAction actionWithTitle:@"Website" style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction * action) {
                                                              [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://meridian.sparkes.zone"]
                                                                                                 options:@{}
                                                                                       completionHandler:nil];
                                                          }];
    
    UIAlertAction *closeAction = [UIAlertAction actionWithTitle:@"Close"
                                                          style:UIAlertActionStyleCancel
                                                        handler:nil];
    
    [alert addAction:websiteAction];
    [alert addAction:closeAction];
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)writeText:(NSString *)message {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (![message  isEqual: @"done!"] && ![message isEqual:@"failed!"]) {
            NSLog(@"%@", message);
            _textArea.text = [_textArea.text stringByAppendingString:[NSString stringWithFormat:@"%@ ", message]];
        } else {
            _textArea.text = [_textArea.text stringByAppendingString:[NSString stringWithFormat:@"%@\n", message]];
        }
        NSRange bottom = NSMakeRange(_textArea.text.length - 1, 1);
        [self.textArea scrollRangeToVisible:bottom];
    });
}

- (void)writeTextPlain:(NSString *)message, ... {
    va_list args;
    va_start(args, message);
    message = [[NSString alloc] initWithFormat:[message stringByAppendingString:@"\n"] arguments:args];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        _textArea.text = [_textArea.text stringByAppendingString:message];
        NSRange bottom = NSMakeRange(_textArea.text.length - 1, 1);
        [self.textArea scrollRangeToVisible:bottom];
        NSLog(@"%@", message);
    });
    va_end(args);
}

// kinda dumb, kinda lazy, ¯\_(ツ)_/¯
void log_message(NSString *message) {
    [thisClass writeTextPlain:message];
}

#pragma mark ELECTRA

- (IBAction)doit:(id)sender {
    
    
    if (_respringReady) {
        
        
        [self writeText:@"Respringing please wait..."];
        
        [NSTask launchedTaskWithLaunchPath:@"/usr/bin/killall" arguments:@[@"-9", @"backboardd"]];
        
        
        return;
        
        
    }
    
    NSLog(@"DO IT!");
    
    [self.goButton setEnabled:NO];
    [self.goButton setHidden:YES];
    
    [self writeText:@"Starting jailbreak..."];
    
    currentViewController = self;
    
    [self.goButton setTitle:@"Please Wait (1/3)" forState:UIControlStateNormal];
    
    BOOL shouldEnableTweaks = YES;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{
        mach_port_t user_client;
        mach_port_t tfp0 = get_tfp0(&user_client);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.goButton setTitle:@"Please Wait (2/3)" forState:UIControlStateNormal];
        });
        
        int jailbreakstatus = begin_fun(tfp0, user_client, shouldEnableTweaks);
        if (jailbreakstatus == 0){
            
            [self setGoButtonTitle:@"Respring"];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                //[self.goButton setTitle:@"Jailbroken" forState:UIControlStateNormal];
                
                [self writeText:@"\nFREEEEEEEEDOM!!!!!\n"];
                [self writeText:@"\nPlease select respring to continue!\n"];
                [self.goButton setEnabled:TRUE];
                [self.goButton setHidden:FALSE];
                _respringReady = TRUE;
                /*
                UIAlertController *dropbearRunning = [UIAlertController alertControllerWithTitle:@"Dropbear Running" message:@"Dropbear is now running! Enjoy." preferredStyle:UIAlertControllerStyleAlert];
                [dropbearRunning addAction:[UIAlertAction actionWithTitle:@"Exit" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                    [dropbearRunning dismissViewControllerAnimated:YES completion:nil];
                    //exit(0);
                    [NSTask launchedTaskWithLaunchPath:@"/usr/bin/killall" arguments:@[@"-9", @"backboardd"]];
                }]];
                [self presentViewController:dropbearRunning animated:YES completion:nil];
                 */
            });
        } else if (jailbreakstatus == -1) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.goButton setTitle:@"Already Jailbroken" forState:UIControlStateNormal];
            });
        } else if (jailbreakstatus == -2) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.goButton setTitle:@"Error: topanga" forState:UIControlStateNormal];
            });
        } else if (jailbreakstatus == -3) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.goButton setTitle:@"Error: amfid patch" forState:UIControlStateNormal];
            });
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.goButton setTitle:@"Error Jailbreaking" forState:UIControlStateNormal];
            });
        }
        
        //NSLog(@" ♫ KPP never bothered me anyway... ♫ ");
        [self writeText:@"\n ♫ KPP never bothered me anyway... ♫ \n"];
    });
}

- (void)removingLiberiOS {
    dispatch_async(dispatch_get_main_queue(), ^{
        //[self.goButton setTitle:@"Removing liberiOS" forState:UIControlStateNormal];
        [self writeText:@"Removing liberiOS"];
    });
}

- (void)removingElectraBeta {
    dispatch_async(dispatch_get_main_queue(), ^{
        //[self.goButton setTitle:@"Removing beta" forState:UIControlStateNormal];
         [self writeText:@"Removing beta"];
    });
}

- (void)installingNitoTV {
    dispatch_async(dispatch_get_main_queue(), ^{
        //[self.goButton setTitle:@"Installing Cydia" forState:UIControlStateNormal];
        [self writeText:@"Installing nitoTV\n"];
    });
}

- (void)nitoTVDone {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.goButton setTitle:@"Please Wait (2/3)" forState:UIControlStateNormal];
    });
}

- (void)displaySnapshotNotice {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.goButton setTitle:@"user prompt" forState:UIControlStateNormal];
        UIAlertController *apfsNoticeController = [UIAlertController alertControllerWithTitle:@"APFS Snapshot Created" message:@"An APFS Snapshot has been successfully created! You may be able to use SemiRestore to restore your phone to this snapshot in the future." preferredStyle:UIAlertControllerStyleAlert];
        [apfsNoticeController addAction:[UIAlertAction actionWithTitle:@"Continue Jailbreak" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [self.goButton setTitle:@"Please Wait (2/3)" forState:UIControlStateNormal];
            snapshotWarningRead();
        }]];
        [self presentViewController:apfsNoticeController animated:YES completion:nil];
    });
}

- (void)displaySnapshotWarning {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.goButton setTitle:@"user prompt" forState:UIControlStateNormal];
        UIAlertController *apfsWarningController = [UIAlertController alertControllerWithTitle:@"APFS Snapshot Not Found" message:@"Warning: Your device was bootstrapped using a pre-release version of Electra and thus does not have an APFS Snapshot present. While Electra may work fine, you will not be able to use SemiRestore to restore to stock if you need to. Please clean your device and re-bootstrap with this version of Electra to create a snapshot." preferredStyle:UIAlertControllerStyleAlert];
        [apfsWarningController addAction:[UIAlertAction actionWithTitle:@"Continue Jailbreak" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [self.goButton setTitle:@"Please Wait (2/3)" forState:UIControlStateNormal];
            snapshotWarningRead();
        }]];
        [self presentViewController:apfsWarningController animated:YES completion:nil];
    });
}


- (UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}



@end
