| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "AppDelegate.h" | |
| 6 | |
| 7 #include <Security/Security.h> | |
| 8 | |
| 9 #import "Downloader.h" | |
| 10 #import "InstallerWindowController.h" | |
| 11 #import "NSError+ChromeInstallerAdditions.h" | |
| 12 #import "NSAlert+ChromeInstallerAdditions.h" | |
| 13 #import "AuthorizedInstall.h" | |
| 14 #import "OmahaCommunication.h" | |
| 15 #import "Unpacker.h" | |
| 16 | |
| 17 @interface NSAlert () | |
| 18 - (void)beginSheetModalForWindow:(NSWindow*)sheetWindow | |
| 19 completionHandler: | |
| 20 (void (^__nullable)(NSModalResponse returnCode))handler; | |
| 21 @end | |
| 22 | |
| 23 @interface AppDelegate ()<NSWindowDelegate, | |
| 24 OmahaCommunicationDelegate, | |
| 25 DownloaderDelegate, | |
| 26 UnpackDelegate> { | |
| 27 InstallerWindowController* installerWindowController_; | |
| 28 AuthorizedInstall* authorizedInstall_; | |
| 29 BOOL preventTermination_; | |
| 30 } | |
| 31 @property(strong) NSWindow* window; | |
| 32 - (void)exit; | |
| 33 @end | |
| 34 | |
| 35 @implementation AppDelegate | |
| 36 @synthesize window = window_; | |
| 37 | |
| 38 // Sets up the main window and begins the downloading process. | |
| 39 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { | |
| 40 // TODO: fix UI not loading until after asking for authorization. | |
| 41 window_.delegate = self; | |
| 42 installerWindowController_ = | |
| 43 [[InstallerWindowController alloc] initWithWindow:window_]; | |
| 44 authorizedInstall_ = [[AuthorizedInstall alloc] init]; | |
| 45 if ([authorizedInstall_ loadInstallationTool]) { | |
| 46 [self startDownload]; | |
| 47 } else { | |
| 48 [self onLoadInstallationToolFailure]; | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 - (void)applicationWillTerminate:(NSNotification*)aNotification { | |
| 53 } | |
| 54 | |
| 55 - (NSApplicationTerminateReply)applicationShouldTerminate: | |
| 56 (NSApplication*)sender { | |
| 57 return preventTermination_ ? NSTerminateCancel : NSTerminateNow; | |
| 58 } | |
| 59 | |
| 60 // This function effectively takes the place of | |
| 61 // applicationShouldTerminateAfterLastWindowClosed: to make sure that the | |
| 62 // application does correctly terminate after closing the installer, but does | |
| 63 // not terminate when we call orderOut: to hide the installer during its | |
| 64 // tear-down steps. | |
| 65 - (BOOL)windowShouldClose:(id)sender { | |
| 66 [self exit]; | |
| 67 return YES; | |
| 68 } | |
| 69 | |
| 70 - (void)exit { | |
| 71 preventTermination_ = NO; | |
| 72 [NSApp terminate:nil]; | |
| 73 } | |
| 74 | |
| 75 - (void)startDownload { | |
| 76 [installerWindowController_ updateStatusDescription:@"Initializing..."]; | |
| 77 | |
| 78 OmahaCommunication* omahaMessenger = [[OmahaCommunication alloc] init]; | |
| 79 omahaMessenger.delegate = self; | |
| 80 [omahaMessenger fetchDownloadURLs]; | |
| 81 } | |
| 82 | |
| 83 - (void)onLoadInstallationToolFailure { | |
| 84 NSError* loadToolError = [NSError | |
| 85 errorForAlerts:@"Could not load installion tool" | |
| 86 withDescription: | |
| 87 @"Your Chrome Installer may be corrupted. Download and try again." | |
| 88 isRecoverable:NO]; | |
| 89 [self displayError:loadToolError]; | |
| 90 } | |
| 91 | |
| 92 - (void)omahaCommunication:(OmahaCommunication*)messenger | |
| 93 onSuccess:(NSArray*)URLs { | |
| 94 [installerWindowController_ updateStatusDescription:@"Downloading..."]; | |
| 95 | |
| 96 Downloader* download = [[Downloader alloc] init]; | |
| 97 download.delegate = self; | |
| 98 [download downloadChromeImageFrom:[URLs firstObject]]; | |
| 99 } | |
| 100 | |
| 101 - (void)omahaCommunication:(OmahaCommunication*)messenger | |
| 102 onFailure:(NSError*)error { | |
| 103 NSError* networkError = | |
| 104 [NSError errorForAlerts:@"Network Error" | |
| 105 withDescription:@"Could not connect to Chrome server." | |
| 106 isRecoverable:YES]; | |
| 107 [self displayError:networkError]; | |
| 108 } | |
| 109 | |
| 110 // Bridge method from Downloader to InstallerWindowController. Allows Downloader | |
| 111 // to update the progressbar without having direct access to any UI obejcts. | |
| 112 - (void)downloader:(Downloader*)download percentProgress:(double)percentage { | |
| 113 [installerWindowController_ updateDownloadProgress:(double)percentage]; | |
| 114 } | |
| 115 | |
| 116 - (void)downloader:(Downloader*)download onSuccess:(NSURL*)diskImageURL { | |
| 117 [installerWindowController_ updateStatusDescription:@"Installing..."]; | |
| 118 [installerWindowController_ enableLaunchButton]; | |
| 119 | |
| 120 Unpacker* unpacker = [[Unpacker alloc] init]; | |
| 121 unpacker.delegate = self; | |
| 122 [unpacker mountDMGFromURL:diskImageURL]; | |
| 123 } | |
| 124 | |
| 125 - (void)downloader:(Downloader*)download onFailure:(NSError*)error { | |
| 126 NSError* downloadError = | |
| 127 [NSError errorForAlerts:@"Download Failure" | |
| 128 withDescription:@"Unable to download Google Chrome." | |
| 129 isRecoverable:NO]; | |
| 130 [self displayError:downloadError]; | |
| 131 } | |
| 132 | |
| 133 - (void)unpacker:(Unpacker*)unpacker onMountSuccess:(NSString*)tempAppPath { | |
| 134 SecStaticCodeRef diskStaticCode; | |
| 135 SecRequirementRef diskRequirement; | |
| 136 // TODO: flush out error handling more | |
| 137 OSStatus oserror; | |
| 138 oserror = SecStaticCodeCreateWithPath( | |
| 139 (__bridge CFURLRef)[NSURL fileURLWithPath:tempAppPath isDirectory:NO], | |
| 140 kSecCSDefaultFlags, &diskStaticCode); | |
| 141 if (oserror != errSecSuccess) | |
| 142 NSLog(@"code %d", oserror); | |
| 143 // TODO: add in a more specific code sign requirement | |
| 144 oserror = | |
| 145 SecRequirementCreateWithString((CFStringRef) @"anchor apple generic", | |
| 146 kSecCSDefaultFlags, &diskRequirement); | |
| 147 if (oserror != errSecSuccess) | |
| 148 NSLog(@"requirement %d", oserror); | |
| 149 oserror = SecStaticCodeCheckValidity(diskStaticCode, kSecCSDefaultFlags, | |
| 150 diskRequirement); | |
| 151 if (oserror != errSecSuccess) | |
| 152 NSLog(@"static code %d", oserror); | |
| 153 | |
| 154 // Calling this function will change the progress bar into an indeterminate | |
| 155 // one. We won't need to update the progress bar any more after this point. | |
| 156 [installerWindowController_ updateDownloadProgress:-1.0]; | |
| 157 // By disabling closing the window or quitting, we can tell the user that | |
| 158 // closing the application at this point is not a good idea. | |
| 159 window_.styleMask &= ~NSClosableWindowMask; | |
| 160 preventTermination_ = YES; | |
| 161 | |
| 162 NSString* chromeInApplicationsFolder = | |
| 163 [authorizedInstall_ startInstall:tempAppPath]; | |
| 164 | |
| 165 NSError* error = nil; | |
| 166 [[NSWorkspace sharedWorkspace] | |
| 167 launchApplicationAtURL:[NSURL fileURLWithPath:chromeInApplicationsFolder | |
| 168 isDirectory:NO] | |
| 169 options:NSWorkspaceLaunchDefault | |
| 170 configuration:@{} | |
| 171 error:&error]; | |
| 172 if (error) { | |
| 173 NSLog(@"Chrome failed to launch: %@", error); | |
| 174 } | |
| 175 | |
| 176 // Begin teardown stuff! | |
| 177 dispatch_async(dispatch_get_main_queue(), ^{ | |
| 178 [window_ orderOut:nil]; | |
| 179 }); | |
| 180 | |
| 181 [unpacker unmountDMG]; | |
| 182 } | |
| 183 | |
| 184 - (void)unpacker:(Unpacker*)unpacker onMountFailure:(NSError*)error { | |
| 185 NSError* extractError = | |
| 186 [NSError errorForAlerts:@"Install Failure" | |
| 187 withDescription:@"Unable to add Google Chrome to Applications." | |
| 188 isRecoverable:NO]; | |
| 189 [self displayError:extractError]; | |
| 190 } | |
| 191 | |
| 192 - (void)unpacker:(Unpacker*)unpacker onUnmountSuccess:(NSString*)mountpath { | |
| 193 NSLog(@"we're done here!"); | |
| 194 [self exit]; | |
| 195 } | |
| 196 | |
| 197 - (void)unpacker:(Unpacker*)unpacker onUnmountFailure:(NSError*)error { | |
| 198 NSLog(@"error unmounting"); | |
| 199 // NOTE: since we are not deleting the temporary folder if the unmount fails, | |
| 200 // we'll just leave it up to the computer to delete the temporary folder on | |
| 201 // its own time, and to unmount the disk during a restart at some point. There | |
| 202 // is no other work to be done in the mean time. | |
| 203 [self exit]; | |
| 204 } | |
| 205 | |
| 206 // Displays an alert on the main window using the contents of the passed in | |
| 207 // error. | |
| 208 - (void)displayError:(NSError*)error { | |
| 209 NSAlert* alertForUser = [NSAlert alertWithError:error]; | |
| 210 dispatch_async(dispatch_get_main_queue(), ^{ | |
| 211 [alertForUser beginSheetModalForWindow:window_ | |
| 212 completionHandler:^(NSModalResponse returnCode) { | |
| 213 if (returnCode != [alertForUser quitResponse]) { | |
| 214 [self startDownload]; | |
| 215 } else { | |
| 216 [NSApp terminate:nil]; | |
| 217 } | |
| 218 }]; | |
| 219 }); | |
| 220 } | |
| 221 | |
| 222 @end | |
| OLD | NEW |