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