AppDelegate.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. //
  2. // AppDelegate.m
  3. // nitoTV4Installer
  4. //
  5. // Created by Kevin Bradley on 1/24/18.
  6. // Copyright © 2018 Kevin Bradley. All rights reserved.
  7. //
  8. #import "AppDelegate.h"
  9. #import "ATVDeviceController.h"
  10. @interface AppDelegate ()
  11. @property (weak) IBOutlet NSWindow *window;
  12. //@property (readwrite, assign) BOOL atvAvailable;
  13. //@property (nonatomic, strong) NSString *bundleID;
  14. @property (nonatomic, strong) IBOutlet ATVDeviceController *deviceController;
  15. @end
  16. @implementation AppDelegate
  17. @synthesize window,workingPath, sshSession, isSending, downloading, downloadFile;
  18. @synthesize deviceController;
  19. static NSString *appleTVAddress = nil;
  20. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  21. // Insert code here to initialize your application
  22. //deviceController = [[ATVDeviceController alloc] init];
  23. //variables that was a carry over from code i migrated this from, can probably be pruned, dont think i use it.
  24. appleTVAddress = APPLE_TV_ADDRESS;
  25. NSLog(@"SELECTED_VALUE: %@", SELECTED_VALUE);
  26. if (SELECTED_VALUE != nil)
  27. {
  28. [deviceController.theComboBox selectItemWithTitle:SELECTED_VALUE];
  29. [deviceController menuItemSelected:deviceController.theComboBox];
  30. }
  31. NSLog(@"appleTVAddress: %@", appleTVAddress);
  32. if ([[appleTVAddress componentsSeparatedByString:@":"] count] < 2)
  33. {
  34. [self resetServerSettings];
  35. }
  36. }
  37. - (NSString *)appSupportFolder
  38. {
  39. NSString *supportFolder = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/nitoDownload"];
  40. NSLog(@"dir: %@", supportFolder);
  41. if (![FM fileExistsAtPath:supportFolder])
  42. {
  43. [FM createDirectoryAtPath:supportFolder withIntermediateDirectories:YES attributes:nil error:nil];
  44. }
  45. return supportFolder;
  46. }
  47. + (NSArray *)returnForProcess:(NSString *)call
  48. {
  49. if (call==nil)
  50. return 0;
  51. char line[200];
  52. FILE* fp = popen([call UTF8String], "r");
  53. NSMutableArray *lines = [[NSMutableArray alloc]init];
  54. if (fp)
  55. {
  56. while (fgets(line, sizeof line, fp))
  57. {
  58. NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
  59. s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  60. [lines addObject:s];
  61. }
  62. }
  63. pclose(fp);
  64. return lines;
  65. }
  66. //59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39
  67. - (BOOL)verifyChecksum:(NSString *)checksum onFile:(NSString *)file
  68. {
  69. NSString *processReturn = [[AppDelegate returnForProcess:[NSString stringWithFormat:@"/usr/bin/shasum -a 256 \"%@\" | cut -c 1-64", file]] componentsJoinedByString:@"\n"];
  70. NSLog(@"return: -%@-", processReturn);
  71. return [processReturn isEqualToString:checksum];
  72. }
  73. - (void)downloadFile:(NSString *)thePayload
  74. {
  75. //we're already downloading, cancel
  76. //TODO: make downloading NSOperation/NSOperationQueue based
  77. if (self.downloading == true)
  78. {
  79. [downloadFile cancel];
  80. self.downloading = false;
  81. statusLabel.stringValue = @"";
  82. [self.progressInd setDoubleValue:0];
  83. [self.progressInd setHidden:TRUE];
  84. self.mainButton.enabled = TRUE;
  85. return;
  86. }
  87. //create instance of downloader class
  88. downloadFile = [KBDownloadFile new];;
  89. //self.downloadButton.title = @"Cancel";
  90. self.downloading = true;
  91. NSString *downloadLocation = [[self appSupportFolder] stringByAppendingPathComponent:[thePayload lastPathComponent]];
  92. if ([FM fileExistsAtPath:downloadLocation])
  93. {
  94. NSString *hash = @"59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39";
  95. switch (self.versionState) {
  96. case KBInstallVersionStateEleven:
  97. case KBInstallVersionStateTenTwo:
  98. hash = @"59293ffbdee698488aba799a96fcfe95e1165fa10f760362a2ce910576134e39";
  99. break;
  100. case KBInstallVersionStateNine:
  101. case KBInstallVersionStateTenOne:
  102. hash = @"327c7cea49282109e23c9415d318121f91b79ca7bb813c8724a1c0752e8311af";
  103. break;
  104. default:
  105. break;
  106. }
  107. if( [self verifyChecksum:hash onFile:downloadLocation])
  108. {
  109. NSLog(@"file validated!");
  110. [self handleDownloadFile:downloadLocation];
  111. return;
  112. //completion(true);
  113. }
  114. }
  115. statusLabel.stringValue = [NSString stringWithFormat:@"Downloading: %@...", thePayload.lastPathComponent];
  116. //get the stream we want to download
  117. [downloadFile downloadFileWithURL:[NSURL URLWithString:thePayload] toLocation:downloadLocation progress:^(double percentComplete) {
  118. [self setDownloadProgress:percentComplete];
  119. } completed:^(NSString *downloadedFile) {
  120. //[self hideProgress];
  121. self.downloading = false;
  122. [self setDownloadProgress:0];
  123. statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", downloadedFile.lastPathComponent];
  124. [self handleDownloadFile:downloadedFile];
  125. //[self uploadFile:downloadedFile];
  126. }];
  127. }
  128. - (void)handleDownloadFile:(NSString *)downloadedFile
  129. {
  130. [self setDownloadProgress:0];
  131. statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", downloadedFile.lastPathComponent];
  132. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  133. [self uploadFile:downloadedFile withCompletion:^(BOOL success) {
  134. dispatch_async(dispatch_get_main_queue(), ^{
  135. if (success)
  136. {
  137. NSLog(@"file uploaded successfully!");
  138. statusLabel.stringValue = @"File uploaded successfully!";
  139. statusLabel.stringValue = @"Installing payload...";
  140. NSString *tarString = [self tarPathForCurrentMode];
  141. NSString *bootstrap = [NSString stringWithFormat:@"/var/mobile/Documents/%@", downloadedFile.lastPathComponent];
  142. NSString *installCommand = [NSString stringWithFormat:@"%@ fxpv %@ -C / ; /usr/libexec/substrate ; /usr/bin/uicache ; /bin/bash /usr/libexec/nito/firmware.sh ; rm /var/mobile/Library/Preferences/Featured.plist", tarString, bootstrap];
  143. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  144. NSString *returnString = [self sendCommandString:installCommand];
  145. NSLog(@"return string: %@", returnString);
  146. NSString *logFile = [[self appSupportFolder] stringByAppendingPathComponent:@"install.log"];
  147. [returnString writeToFile:logFile atomically:YES encoding:NSUTF8StringEncoding error:nil];
  148. if (self.versionState == KBInstallVersionStateNine)
  149. {
  150. [self sendCommandString:@"/usr/bin/killall -9 PineBoard HeadBoard lsd nitoTV"];
  151. }
  152. dispatch_async(dispatch_get_main_queue(), ^{
  153. [[NSWorkspace sharedWorkspace] openFile:logFile];
  154. statusLabel.stringValue = @"Payload installed?!?!";
  155. [self.progressInd stopAnimation:nil];
  156. self.mainButton.enabled = TRUE;
  157. });
  158. });
  159. } else {
  160. statusLabel.stringValue = @"File failed to upload!";
  161. [self.progressInd stopAnimation:nil];
  162. self.mainButton.enabled = TRUE;
  163. }
  164. });
  165. }];
  166. });
  167. }
  168. - (void)hideProgress
  169. {
  170. dispatch_async(dispatch_get_main_queue(), ^{
  171. statusLabel.stringValue = @"";
  172. [[self progressInd] stopAnimation:nil];
  173. [[self progressInd] setDoubleValue:0];
  174. [[self progressInd] setHidden:true];
  175. });
  176. }
  177. - (void)setDownloadProgress:(double)theProgress
  178. {
  179. dispatch_async(dispatch_get_main_queue(), ^{
  180. if (theProgress == 0)
  181. {
  182. [self.progressInd setIndeterminate:TRUE];
  183. [self.progressInd setHidden:FALSE];
  184. [self.progressInd setNeedsDisplay:YES];
  185. [self.progressInd setUsesThreadedAnimation:YES];
  186. [self.progressInd startAnimation:self];
  187. return;
  188. }
  189. [self.progressInd setIndeterminate:FALSE];
  190. [self.progressInd startAnimation:self];
  191. [self.progressInd setHidden:FALSE];
  192. [self.progressInd setNeedsDisplay:YES];
  193. [self.progressInd setDoubleValue:theProgress];
  194. });
  195. }
  196. - (void)setStatusText:(NSString *)statusText
  197. {
  198. dispatch_async(dispatch_get_main_queue(), ^{
  199. statusLabel.stringValue = statusText;
  200. });
  201. }
  202. - (BOOL)atvAvailable
  203. {
  204. return _atvAvailable;
  205. }
  206. - (void)setAtvAvailable:(BOOL)atvAvailable
  207. {
  208. _atvAvailable = atvAvailable;
  209. }
  210. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  211. // Insert code here to tear down your application
  212. }
  213. - (NSString *)tarPathForCurrentMode
  214. {
  215. NSString *path = nil;
  216. switch (self.versionState) {
  217. case KBInstallVersionStateNine:
  218. path= @"/usr/bin/tar";
  219. break;
  220. case KBInstallVersionStateTenOne:
  221. path= @"/tmp/usr/bin/tar"; //need to verify
  222. break;
  223. case KBInstallVersionStateTenTwo:
  224. path= @"/usr/bin/tar";
  225. break;
  226. case KBInstallVersionStateEleven:
  227. path= @"/jb/usr/bin/tar";
  228. break;
  229. default:
  230. break;
  231. }
  232. return path;
  233. }
  234. - (NSString *)lsPathForCurrentMode
  235. {
  236. NSString *path = nil;
  237. switch (self.versionState) {
  238. case KBInstallVersionStateNine:
  239. path= @"/bin/ls";
  240. break;
  241. case KBInstallVersionStateTenOne:
  242. path= @"/tmp/bin/ls";
  243. break;
  244. case KBInstallVersionStateTenTwo:
  245. path= @"/usr/bin/ls";
  246. break;
  247. case KBInstallVersionStateEleven:
  248. path= @"/jb/bin/ls";
  249. break;
  250. default:
  251. break;
  252. }
  253. return path;
  254. }
  255. - (BOOL)checkForFile:(NSString *)file
  256. {
  257. //@"ls /usr/bin/ | grep appinst"
  258. NSString *lsCommand = [self lsPathForCurrentMode];
  259. NSString *path = [file stringByDeletingLastPathComponent];
  260. NSString *bareFile = [file lastPathComponent];
  261. NSString *theReturn = [self sendCommandString:[NSString stringWithFormat:@"%@ -a %@ | grep %@", lsCommand, path, bareFile]];
  262. if (theReturn != nil)
  263. { return (TRUE);
  264. } else { return (FALSE);}
  265. return (FALSE);
  266. }
  267. - (void)checkJailbreakWithCompletion:(void(^)(BOOL jailbroken))completion
  268. {
  269. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  270. BOOL jb = [self isJailbroken];
  271. if (jb)
  272. {
  273. NSLog(@"we outchea");
  274. } else {
  275. NSLog(@"nope!");
  276. }
  277. completion(jb);
  278. });
  279. }
  280. - (BOOL)isJailbroken
  281. {
  282. NSError *error = nil;
  283. if (APPLE_TV_ADDRESS != nil)
  284. {
  285. ObjSSH *ssh = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:@"alpine" error:&error];
  286. if (error)
  287. {
  288. NSLog(@"error: %@", [error localizedDescription]);
  289. if ([[error localizedDescription] isEqualToString:@"Failed to connect"])
  290. {
  291. [ssh disconnect];
  292. return (FALSE);
  293. }
  294. [ssh disconnect];
  295. }
  296. } else {
  297. return (FALSE);
  298. }
  299. return (TRUE);
  300. }
  301. //if they changed password from alpine this is used to get the proper password to connect
  302. - (void)resetServerSettings
  303. {
  304. [DEFAULTS removeObjectForKey:@"appleTVHost"];
  305. [DEFAULTS removeObjectForKey:ATV_OS];
  306. [DEFAULTS removeObjectForKey:ATV_API];
  307. [DEFAULTS setObject:@"Choose Apple TV" forKey:@"selectedValue"];
  308. appleTVAddress = nil;
  309. [[self progressInd] stopAnimation:nil];
  310. }
  311. - (NSString *)secureInput: (NSString *)prompt defaultValue: (NSString *)defaultValue {
  312. NSAlert *alert = [NSAlert alertWithMessageText: prompt
  313. defaultButton:@"OK"
  314. alternateButton:@"Cancel"
  315. otherButton:nil
  316. informativeTextWithFormat:@""];
  317. NSSecureTextField *input = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
  318. [input setStringValue:defaultValue];
  319. [alert setAccessoryView:input];
  320. NSInteger button = [alert runModal];
  321. if (button == NSAlertDefaultReturn) {
  322. [input validateEditing];
  323. NSString *inputString = [input stringValue];
  324. return inputString;
  325. } else if (button == NSAlertAlternateReturn) {
  326. return nil;
  327. } else {
  328. return nil;
  329. }
  330. }
  331. //add a password to the keychain for SSH
  332. - (void)addkeychainPassword:(NSString *)password
  333. {
  334. //kSecProtocolTypeSSH
  335. [EMInternetKeychainItem addInternetKeychainItemForServer:APPLE_TV_ADDRESS withUsername:@"root" password:password path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
  336. }
  337. - (void)removeKeychainForHost:(NSString *)ipAddress
  338. {
  339. //kSecProtocolTypeSSH
  340. EMInternetKeychainItem *keychainItem = [EMInternetKeychainItem internetKeychainItemForServer:ipAddress withUsername:@"root" path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
  341. [keychainItem removeFromKeychain];
  342. }
  343. //fetch the password from the keychain for the specified ip address
  344. - (NSString *)passwordForHost:(NSString *)ipAddress
  345. {
  346. EMInternetKeychainItem *keychainItem = [EMInternetKeychainItem internetKeychainItemForServer:ipAddress withUsername:@"root" path:@"/usr/bin/ssh" port:22 protocol:kSecProtocolTypeSSH];
  347. //Grab the password.
  348. if (keychainItem != nil)
  349. {
  350. //Grab the password.
  351. NSString *password = keychainItem.password;
  352. return password;
  353. }
  354. NSLog(@"nothing!");
  355. return nil;
  356. }
  357. - (BOOL)uploadFile:(NSString *)theFile toPath:(NSString *)newPath
  358. {
  359. NSLog(@"uploading file: %@", theFile);
  360. NSError *error = nil;
  361. BOOL getSession = [self connectToSSH];
  362. if (getSession == FALSE)
  363. {
  364. NSLog(@"failed to get session!");
  365. return (FALSE);
  366. }
  367. NSString *finalPath = [newPath stringByAppendingPathComponent:[theFile lastPathComponent]];
  368. BOOL uploadFile = [sshSession uploadFile:theFile to:finalPath error:&error];
  369. if (error)
  370. {
  371. NSLog(@"ERROR!: %@", error);
  372. }
  373. return (uploadFile);
  374. }
  375. - (void)uploadFile:(NSString *)theFile withCompletion:(void(^)(BOOL success))completion
  376. {
  377. NSLog(@"uploading file: %@", theFile);
  378. NSError *error = nil;
  379. BOOL getSession = [self connectToSSH];
  380. if (getSession == FALSE)
  381. {
  382. NSLog(@"failed to get session!");
  383. completion(false);
  384. }
  385. BOOL uploadFile = [self uploadFile:theFile toPath:@"/var/mobile/Documents"];
  386. completion(uploadFile);
  387. }
  388. //upload a file over SSH to the selected AppleTV
  389. - (BOOL)uploadFile:(NSString *)theFile
  390. {
  391. NSLog(@"uploading file: %@", theFile);
  392. NSError *error = nil;
  393. BOOL getSession = [self connectToSSH];
  394. if (getSession == FALSE)
  395. {
  396. NSLog(@"failed to get session!");
  397. return (FALSE);
  398. }
  399. statusLabel.stringValue = [NSString stringWithFormat:@"Uploading file %@....", theFile.lastPathComponent];
  400. BOOL uploadFile = [sshSession uploadFile:theFile to:[theFile lastPathComponent] error:&error];
  401. if (error)
  402. {
  403. NSLog(@"ERROR!: %@", error);
  404. }
  405. return (uploadFile);
  406. }
  407. //used to send basic commands to the jailbroken AppleTV over SSH
  408. - (NSString *)sendCommandString:(NSString *)theCommand
  409. {
  410. NSLog(@"%@", theCommand);
  411. NSError *error = nil;
  412. BOOL getSession = [self connectToSSH];
  413. if (getSession == FALSE)
  414. {
  415. NSLog(@"failed to get session!");
  416. return nil;
  417. }
  418. NSString *response = [sshSession execute:theCommand error:&error];
  419. return response;
  420. }
  421. //open the SSH session
  422. - (BOOL)connectToSSH
  423. {
  424. NSError *error = nil;
  425. if (sshSession == nil)
  426. {
  427. //NSLog(@"APPLE_TV_ADDRESS: %@", APPLE_TV_ADDRESS);
  428. sshSession = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:@"alpine" error:&error];
  429. if (sshSession == nil)
  430. {
  431. NSLog(@"error: %@ get password!", error);
  432. NSString *passwordForHost = [self passwordForHost:APPLE_TV_ADDRESS];
  433. NSString *output = nil;
  434. if (passwordForHost != nil)
  435. {
  436. output = passwordForHost;
  437. } else {
  438. output = [self secureInput:@"Enter Password" defaultValue:@""];
  439. }
  440. if ([output length] == 0)
  441. {
  442. NSLog(@"no password to send!! return!");
  443. return (FALSE);
  444. } else {
  445. error = nil;
  446. sshSession = [ObjSSH connectToHost:APPLE_TV_ADDRESS withUsername:@"root" password:output error:&error];
  447. if (error != nil)
  448. {
  449. NSLog(@"error: %@ password failed!", error);
  450. [self removeKeychainForHost:APPLE_TV_ADDRESS];
  451. return (FALSE);
  452. } else {
  453. [self addkeychainPassword:output];
  454. }
  455. }
  456. }
  457. }
  458. if (sshSession != nil)
  459. return (TRUE);
  460. return (FALSE);
  461. }
  462. - (void)showNotJailbrokenWarning
  463. {
  464. NSAlert *alert = [NSAlert alertWithMessageText:@"This Apple TV isn't jailbroken, please jailbreak it first! Would you like to visit our web site for further assistance?"
  465. defaultButton:@"Yes"
  466. alternateButton:@"Cancel"
  467. otherButton:nil
  468. informativeTextWithFormat:@""];
  469. NSInteger button = [alert runModal];
  470. if (button == NSAlertDefaultReturn) {
  471. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://wiki.awkwardtv.org"]];
  472. }
  473. }
  474. - (void)showATVWarning
  475. {
  476. NSAlert *alert = [NSAlert alertWithMessageText:@"Only the AppleTV 4 is supported"
  477. defaultButton:@"OK"
  478. alternateButton:nil
  479. otherButton:nil
  480. informativeTextWithFormat:@""];
  481. [alert runModal];
  482. }
  483. - (NSString *)logLocation
  484. {
  485. NSString *location = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Logs/iReSign"];
  486. if (![FM fileExistsAtPath:location])
  487. {
  488. [FM createDirectoryAtPath:location withIntermediateDirectories:true attributes:nil error:nil];
  489. }
  490. return location;
  491. }
  492. //versions
  493. //265.5 = 9.0
  494. //301.44.3 = 10.0
  495. //310.17 = 10.1 (either .0 or .1)
  496. //320.20.1 = 10.2.2
  497. //353.50 = 11.1
  498. - (IBAction)doIt:(id)sender
  499. {
  500. [self.mainButton setEnabled:NO];
  501. NSString *bootstrapPath = @"http://nitosoft.com/ATV4/bootstraps/";
  502. NSString *swVers = self.deviceDict[@"osvers"];
  503. if (swVers.length > 0)
  504. {
  505. NSLog(@"our sw vers: %@", swVers);
  506. if ([swVers floatValue] >= 11)
  507. {
  508. self.versionState = KBInstallVersionStateEleven;
  509. NSLog(@"11 payload!");
  510. bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"];
  511. }
  512. } else {
  513. swVers = self.deviceDict[@"srcvers"];
  514. {
  515. NSLog(@"our src vers: %@", swVers);
  516. if([swVers compare:@"320.20.1" options:NSNumericSearch] != NSOrderedAscending)
  517. {
  518. self.versionState = KBInstallVersionStateTenTwo;
  519. NSLog(@"10.2.2 or greater!");
  520. bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap11.tar"];
  521. } else if([swVers compare:@"301.44.3" options:NSNumericSearch] != NSOrderedAscending)
  522. {
  523. NSLog(@"10.0 or greater");
  524. self.versionState = KBInstallVersionStateTenOne;
  525. bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap9.tar"];
  526. } else {
  527. NSLog(@"9.0 or greater?");
  528. self.versionState = KBInstallVersionStateNine;
  529. bootstrapPath = [bootstrapPath stringByAppendingString:@"bootstrap9.tar"];
  530. }
  531. }
  532. }
  533. if ([self connectToSSH])
  534. {
  535. // if ([self checkForFile:@"/var/root/.bootstrapped"]){
  536. // NSLog(@"were already bootstrapped!");
  537. //} else {
  538. [self downloadFile:bootstrapPath];
  539. // }
  540. //
  541. //[self downloadSyslogAndShow:YES];
  542. } else {
  543. self.mainButton.enabled = TRUE;
  544. }
  545. }
  546. - (BOOL)downloadSyslogAndShow:(BOOL)show
  547. {
  548. BOOL getSession = [self connectToSSH];
  549. if (getSession == FALSE)
  550. {
  551. NSLog(@"failed to get session!");
  552. return false;
  553. }
  554. NSError *error = nil;
  555. NSString *newSyslog = [[self logLocation] stringByAppendingPathComponent:@"syslog.log"];
  556. if ([[NSFileManager defaultManager] fileExistsAtPath:newSyslog])
  557. {
  558. [[NSFileManager defaultManager] removeItemAtPath:newSyslog error:nil];
  559. }
  560. //echo latest syslog output to a new file for downloadin..
  561. [self sendCommandString:@"/jb/usr/bin/syslog > syslog.log"];
  562. BOOL downloadFile = [sshSession downloadFile:@"/var/root/syslog.log" to:newSyslog error:&error];
  563. if (downloadFile)
  564. {
  565. NSLog(@"File downloaded Successfully!");
  566. if (show == true)
  567. {
  568. [[NSWorkspace sharedWorkspace] openFile:newSyslog];
  569. }
  570. return true;
  571. }
  572. return false;
  573. }
  574. - (void)installFile:(NSString *)theFile withCompletionBlock:(void(^)(BOOL success))completionBlock
  575. {
  576. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
  577. @autoreleasepool {
  578. BOOL success = FALSE;
  579. if ([self uploadFile:finalDestination toPath:@"/var/mobile/Documents"] == true)
  580. {
  581. dispatch_async(dispatch_get_main_queue(), ^{
  582. [statusLabel setStringValue:[NSString stringWithFormat:@"Installing file %@...", fileName]];
  583. });
  584. /*
  585. Massively kludgy but it works, for some reason when appinst runs it doesnt go to stdout (or something) so i /need/ to redirect it to a text file, then cat that text file to check for whether or not "installed <bundle_id>" exists
  586. */
  587. NSString *checkResponse = [NSString stringWithFormat:@"installed %@", self.bundleID];
  588. NSString *runLine = [NSString stringWithFormat:@"/usr/bin/appinst /var/mobile/Documents/%@ 2> install.txt ; cat install.txt", fileName];
  589. //NSString *runLine = [NSString stringWithFormat:@"/usr/bin/appinst /var/mobile/Documents/%@", fileName];
  590. NSString *response = [self sendCommandString:runLine];
  591. //using rangeOfString because containsString is too new for backwards compat.
  592. if ([response rangeOfString:checkResponse].location == NSNotFound)
  593. {
  594. NSString *errorLog = [[self logLocation] stringByAppendingFormat:@"/%@.log", self.bundleID];
  595. //remove old copies
  596. if ([FM fileExistsAtPath:errorLog])
  597. {
  598. [FM removeItemAtPath:errorLog error:nil];
  599. }
  600. //the response above has a bunch of garbled text in it, download the install file "proper" to get the cleaner version
  601. BOOL downloadFile = [sshSession downloadFile:@"/var/root/install.txt" to:errorLog error:nil];
  602. if (downloadFile == false)
  603. {
  604. //if that fails for some reason write the version with the garbage at the end
  605. [response writeToFile:errorLog atomically:true encoding:NSUTF8StringEncoding error:nil];
  606. }
  607. response = [NSString stringWithContentsOfFile:errorLog encoding:NSUTF8StringEncoding error:nil];
  608. NSLog(@"INSTALLATION FAILED WITH LOG: %@", response);
  609. //grab latest relevant syslog chunk
  610. [self downloadSyslogAndShow:false];
  611. } else {
  612. runLine = [NSString stringWithFormat:@"/bin/rm /var/mobile/Documents/%@", fileName];
  613. [self sendCommandString:runLine];
  614. success = true;
  615. }
  616. }
  617. dispatch_async(dispatch_get_main_queue(), ^{
  618. completionBlock(success);
  619. });
  620. }
  621. });
  622. }
  623. @end