| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h" | 5 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 | 8 |
| 9 #include "base/mac/scoped_authorizationref.h" | 9 #include "base/mac/scoped_authorizationref.h" |
| 10 #include "base/mac/scoped_cftyperef.h" | |
| 11 #include "remoting/host/constants_mac.h" | 10 #include "remoting/host/constants_mac.h" |
| 12 | 11 |
| 13 @implementation RemotingUninstallerAppDelegate | |
| 14 | 12 |
| 15 NSString* const kLaunchAgentsDir = @"/Library/LaunchAgents"; | 13 void logOutput(FILE* pipe) { |
| 16 NSString* const kPrefPaneDir = @"/Library/PreferencePanes"; | |
| 17 NSString* const kHelperToolsDir = @"/Library/PrivilegedHelperTools"; | |
| 18 NSString* const kApplicationDir = @"/Applications"; | |
| 19 | |
| 20 NSString* const kPrefPaneName = @kServiceName ".prefPane"; | |
| 21 NSString* const kUninstallerName = | |
| 22 @"Chrome Remote Desktop Host Uninstaller.app"; | |
| 23 | |
| 24 // Keystone | |
| 25 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/" | |
| 26 "GoogleSoftwareUpdate.bundle/Contents/MacOS/" | |
| 27 "ksadmin"; | |
| 28 const char kKeystonePID[] = "com.google.chrome_remote_desktop"; | |
| 29 | |
| 30 - (void)dealloc { | |
| 31 [super dealloc]; | |
| 32 } | |
| 33 | |
| 34 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { | |
| 35 } | |
| 36 | |
| 37 - (void)logOutput:(FILE*) pipe { | |
| 38 char readBuffer[128]; | 14 char readBuffer[128]; |
| 39 for (;;) { | 15 for (;;) { |
| 40 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1); | 16 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1); |
| 41 if (bytesRead < 1) | 17 if (bytesRead < 1) |
| 42 break; | 18 break; |
| 43 readBuffer[bytesRead] = '\0'; | 19 readBuffer[bytesRead] = '\0'; |
| 44 NSLog(@"%s", readBuffer); | 20 NSLog(@"%s", readBuffer); |
| 45 } | 21 } |
| 46 } | 22 } |
| 47 | 23 |
| 48 - (void)messageBox:(const char*)message { | 24 NSArray* convertToNSArray(const char** array) { |
| 49 base::mac::ScopedCFTypeRef<CFStringRef> message_ref( | 25 NSMutableArray* ns_array = [[[NSMutableArray alloc] init] autorelease]; |
| 50 CFStringCreateWithCString(NULL, message, (int)strlen(message))); | 26 int i = 0; |
| 51 CFOptionFlags result; | 27 const char* element = array[i++]; |
| 52 CFUserNotificationDisplayAlert(0, kCFUserNotificationNoteAlertLevel, | 28 while (element != NULL) { |
| 53 NULL, NULL, NULL, | 29 [ns_array addObject:[NSString stringWithUTF8String:element]]; |
| 54 CFSTR("Chrome Remote Desktop Uninstaller"), | 30 element = array[i++]; |
| 55 message_ref, NULL, NULL, NULL, &result); | 31 } |
| 32 return ns_array; |
| 56 } | 33 } |
| 57 | 34 |
| 58 -(void)runCommand:(NSString*)cmd | 35 @implementation RemotingUninstaller |
| 59 withArguments:(NSArray*)args { | 36 |
| 37 // Keystone |
| 38 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/" |
| 39 "GoogleSoftwareUpdate.bundle/Contents/MacOS/" |
| 40 "ksadmin"; |
| 41 const char kKeystonePID[] = "com.google.chrome_remote_desktop"; |
| 42 |
| 43 - (void)runCommand:(const char*)cmd |
| 44 withArguments:(const char**)args { |
| 60 NSTask* task; | 45 NSTask* task; |
| 61 NSPipe* output = [NSPipe pipe]; | 46 NSPipe* output = [NSPipe pipe]; |
| 62 NSString* result; | 47 NSString* result; |
| 63 | 48 |
| 64 NSLog(@"Executing: %@ %@", cmd, [args componentsJoinedByString:@" "]); | 49 NSArray* arg_array = convertToNSArray(args); |
| 50 NSLog(@"Executing: %s %@", cmd, [arg_array componentsJoinedByString:@" "]); |
| 65 | 51 |
| 66 @try { | 52 @try { |
| 67 task = [[[NSTask alloc] init] autorelease]; | 53 task = [[[NSTask alloc] init] autorelease]; |
| 68 [task setLaunchPath:cmd]; | 54 [task setLaunchPath:[NSString stringWithUTF8String:cmd]]; |
| 69 [task setArguments:args]; | 55 [task setArguments:arg_array]; |
| 70 [task setStandardInput:[NSPipe pipe]]; | 56 [task setStandardInput:[NSPipe pipe]]; |
| 71 [task setStandardOutput:output]; | 57 [task setStandardOutput:output]; |
| 72 [task launch]; | 58 [task launch]; |
| 73 | 59 |
| 74 NSData* data = [[output fileHandleForReading] readDataToEndOfFile]; | 60 NSData* data = [[output fileHandleForReading] readDataToEndOfFile]; |
| 75 | 61 |
| 76 [task waitUntilExit]; | 62 [task waitUntilExit]; |
| 77 | 63 |
| 78 if ([task terminationStatus] != 0) { | 64 if ([task terminationStatus] != 0) { |
| 79 // TODO(garykac): When we switch to sdk_10.6, show the | 65 // TODO(garykac): When we switch to sdk_10.6, show the |
| 80 // [task terminationReason] as well. | 66 // [task terminationReason] as well. |
| 81 NSLog(@"Command terminated status=%d", [task terminationStatus]); | 67 NSLog(@"Command terminated status=%d", [task terminationStatus]); |
| 82 } | 68 } |
| 83 | 69 |
| 84 result = [[[NSString alloc] initWithData:data | 70 result = [[[NSString alloc] initWithData:data |
| 85 encoding:NSUTF8StringEncoding] | 71 encoding:NSUTF8StringEncoding] |
| 86 autorelease]; | 72 autorelease]; |
| 87 if ([result length] != 0) { | 73 if ([result length] != 0) { |
| 88 NSLog(@"Result: %@", result); | 74 NSLog(@"Result: %@", result); |
| 89 } | 75 } |
| 90 } | 76 } |
| 91 @catch (NSException* exception) { | 77 @catch (NSException* exception) { |
| 92 NSLog(@"Exception %@ %@", [exception name], [exception reason]); | 78 NSLog(@"Exception %@ %@", [exception name], [exception reason]); |
| 93 } | 79 } |
| 94 } | 80 } |
| 95 | 81 |
| 96 - (void)sudoCommand:(const char*)cmd | 82 - (void)sudoCommand:(const char*)cmd |
| 97 withArguments:(const char**)args | 83 withArguments:(const char**)args |
| 98 usingAuth:(AuthorizationRef)authRef { | 84 usingAuth:(AuthorizationRef)authRef { |
| 99 | 85 NSArray* arg_array = convertToNSArray(args); |
| 100 NSMutableArray* arg_array = [[[NSMutableArray alloc] init] autorelease]; | |
| 101 int i = 0; | |
| 102 const char* arg = args[i++]; | |
| 103 while (arg != NULL) { | |
| 104 [arg_array addObject:[NSString stringWithUTF8String:arg]]; | |
| 105 arg = args[i++]; | |
| 106 } | |
| 107 NSLog(@"Executing (as Admin): %s %@", cmd, | 86 NSLog(@"Executing (as Admin): %s %@", cmd, |
| 108 [arg_array componentsJoinedByString:@" "]); | 87 [arg_array componentsJoinedByString:@" "]); |
| 109 FILE* pipe = NULL; | 88 FILE* pipe = NULL; |
| 110 OSStatus status; | 89 OSStatus status; |
| 111 status = AuthorizationExecuteWithPrivileges(authRef, cmd, | 90 status = AuthorizationExecuteWithPrivileges(authRef, cmd, |
| 112 kAuthorizationFlagDefaults, | 91 kAuthorizationFlagDefaults, |
| 113 (char* const*)args, | 92 (char* const*)args, |
| 114 &pipe); | 93 &pipe); |
| 115 | 94 |
| 116 if (status == errAuthorizationToolExecuteFailure) { | 95 if (status == errAuthorizationToolExecuteFailure) { |
| 117 NSLog(@"Error errAuthorizationToolExecuteFailure"); | 96 NSLog(@"Error errAuthorizationToolExecuteFailure"); |
| 118 } else if (status != errAuthorizationSuccess) { | 97 } else if (status != errAuthorizationSuccess) { |
| 119 NSLog(@"Error while executing %s. Status=%lx", cmd, status); | 98 NSLog(@"Error while executing %s. Status=%lx", cmd, status); |
| 120 } else { | 99 } else { |
| 121 [self logOutput:pipe]; | 100 logOutput(pipe); |
| 122 } | 101 } |
| 123 | 102 |
| 124 if (pipe != NULL) | 103 if (pipe != NULL) |
| 125 fclose(pipe); | 104 fclose(pipe); |
| 126 } | 105 } |
| 127 | 106 |
| 128 - (void)sudoDelete:(const char*)filename | 107 - (void)sudoDelete:(const char*)filename |
| 129 usingAuth:(AuthorizationRef)authRef { | 108 usingAuth:(AuthorizationRef)authRef { |
| 130 const char* args[] = { "-rf", filename, NULL }; | 109 const char* args[] = { "-rf", filename, NULL }; |
| 131 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef]; | 110 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef]; |
| 132 } | 111 } |
| 133 | 112 |
| 134 -(void)shutdownService { | 113 - (void)shutdownService { |
| 135 NSString* launchCtl = @"/bin/launchctl"; | 114 const char* launchCtl = "/bin/launchctl"; |
| 136 NSArray* argsStop = [NSArray arrayWithObjects:@"stop", | 115 const char* argsStop[] = { "stop", remoting::kServiceName, NULL }; |
| 137 @kServiceName, nil]; | |
| 138 [self runCommand:launchCtl withArguments:argsStop]; | 116 [self runCommand:launchCtl withArguments:argsStop]; |
| 139 | 117 |
| 140 NSString* plist = [NSString stringWithFormat:@"%@/%@.plist", | 118 if ([[NSFileManager defaultManager] fileExistsAtPath: |
| 141 kLaunchAgentsDir, @kServiceName]; | 119 [NSString stringWithUTF8String:remoting::kServicePlistPath]]) { |
| 142 if ([[NSFileManager defaultManager] fileExistsAtPath:plist]) { | 120 const char* argsUnload[] = { "unload", "-w", "-S", "Aqua", |
| 143 NSArray* argsUnload = [NSArray arrayWithObjects:@"unload", | 121 remoting::kServicePlistPath, NULL }; |
| 144 @"-w", @"-S", @"Aqua", plist, nil]; | |
| 145 [self runCommand:launchCtl withArguments:argsUnload]; | 122 [self runCommand:launchCtl withArguments:argsUnload]; |
| 146 } | 123 } |
| 147 } | 124 } |
| 148 | 125 |
| 149 -(void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef { | 126 - (void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef { |
| 150 const char* args[] = { "--delete", "--productid", kKeystonePID, "-S", NULL }; | 127 const char* args[] = { "--delete", "--productid", kKeystonePID, "-S", NULL }; |
| 151 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef]; | 128 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef]; |
| 152 } | 129 } |
| 153 | 130 |
| 154 -(void)remotingUninstallUsingAuth:(AuthorizationRef)authRef { | 131 - (void)remotingUninstallUsingAuth:(AuthorizationRef)authRef { |
| 155 NSString* host_enabled = [NSString stringWithFormat:@"%@/%@.me2me_enabled", | 132 // Remove the enabled file before shutting down the service or else it might |
| 156 kHelperToolsDir, @kServiceName]; | 133 // restart itself. |
| 157 [self sudoDelete:[host_enabled UTF8String] usingAuth:authRef]; | 134 [self sudoDelete:remoting::kHostEnabledPath usingAuth:authRef]; |
| 158 | 135 |
| 159 [self shutdownService]; | 136 [self shutdownService]; |
| 160 | 137 |
| 161 NSString* plist = [NSString stringWithFormat:@"%@/%@.plist", | 138 [self sudoDelete:remoting::kServicePlistPath usingAuth:authRef]; |
| 162 kLaunchAgentsDir, @kServiceName]; | 139 [self sudoDelete:remoting::kHostBinaryPath usingAuth:authRef]; |
| 163 [self sudoDelete:[plist UTF8String] usingAuth:authRef]; | 140 [self sudoDelete:remoting::kHostHelperScriptPath usingAuth:authRef]; |
| 164 | 141 [self sudoDelete:remoting::kHostConfigFilePath usingAuth:authRef]; |
| 165 NSString* host_binary = [NSString stringWithFormat:@"%@/%@.me2me_host.app", | 142 [self sudoDelete:remoting::kPrefPaneFilePath usingAuth:authRef]; |
| 166 kHelperToolsDir, @kServiceName]; | 143 [self sudoDelete:remoting::kBrandedUninstallerPath usingAuth:authRef]; |
| 167 [self sudoDelete:[host_binary UTF8String] usingAuth:authRef]; | 144 [self sudoDelete:remoting::kUnbrandedUninstallerPath usingAuth:authRef]; |
| 168 | |
| 169 NSString* host_script = [NSString stringWithFormat:@"%@/%@.me2me.sh", | |
| 170 kHelperToolsDir, @kServiceName]; | |
| 171 [self sudoDelete:[host_script UTF8String] usingAuth:authRef]; | |
| 172 | |
| 173 NSString* auth = [NSString stringWithFormat:@"%@/%@.json", | |
| 174 kHelperToolsDir, @kServiceName]; | |
| 175 [self sudoDelete:[auth UTF8String] usingAuth:authRef]; | |
| 176 | |
| 177 NSString* prefpane = [NSString stringWithFormat:@"%@/%@", | |
| 178 kPrefPaneDir, kPrefPaneName]; | |
| 179 [self sudoDelete:[prefpane UTF8String] usingAuth:authRef]; | |
| 180 | |
| 181 NSString* uninstaller = [NSString stringWithFormat:@"%@/%@", | |
| 182 kApplicationDir, kUninstallerName]; | |
| 183 [self sudoDelete:[uninstaller UTF8String] usingAuth:authRef]; | |
| 184 | 145 |
| 185 [self keystoneUnregisterUsingAuth:authRef]; | 146 [self keystoneUnregisterUsingAuth:authRef]; |
| 186 } | 147 } |
| 187 | 148 |
| 188 - (IBAction)uninstall:(NSButton*)sender { | 149 - (OSStatus)remotingUninstall { |
| 189 base::mac::ScopedAuthorizationRef authRef; | 150 base::mac::ScopedAuthorizationRef authRef; |
| 190 | 151 OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, |
| 191 NSLog(@"Chrome Remote Desktop uninstall starting."); | 152 kAuthorizationFlagDefaults, &authRef); |
| 192 | 153 if (status != errAuthorizationSuccess) { |
| 193 @try { | 154 [NSException raise:@"AuthorizationCreate Failure" |
| 194 OSStatus status; | 155 format:@"Error during AuthorizationCreate status=%ld", status]; |
| 195 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, | |
| 196 kAuthorizationFlagDefaults, &authRef); | |
| 197 if (status != errAuthorizationSuccess) { | |
| 198 [NSException raise:@"AuthorizationCreate Failure" | |
| 199 format:@"Error during AuthorizationCreate status=%ld", status]; | |
| 200 } | |
| 201 | |
| 202 AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0}; | |
| 203 AuthorizationRights rights = {1, &right}; | |
| 204 AuthorizationFlags flags = kAuthorizationFlagDefaults | | |
| 205 kAuthorizationFlagInteractionAllowed | | |
| 206 kAuthorizationFlagPreAuthorize | | |
| 207 kAuthorizationFlagExtendRights; | |
| 208 status = AuthorizationCopyRights(authRef, &rights, NULL, flags, NULL); | |
| 209 if (status == errAuthorizationCanceled) { | |
| 210 NSLog(@"Chrome Remote Desktop Host uninstall canceled."); | |
| 211 const char* message = "Chrome Remote Desktop Host uninstall canceled."; | |
| 212 [self messageBox:message]; | |
| 213 } else if (status == errAuthorizationSuccess) { | |
| 214 [self remotingUninstallUsingAuth:authRef]; | |
| 215 | |
| 216 NSLog(@"Chrome Remote Desktop Host uninstall complete."); | |
| 217 const char* message = | |
| 218 "Chrome Remote Desktop Host was successfully uninstalled."; | |
| 219 [self messageBox:message]; | |
| 220 } else { | |
| 221 [NSException raise:@"AuthorizationCopyRights Failure" | |
| 222 format:@"Error during AuthorizationCopyRights status=%ld", status]; | |
| 223 } | |
| 224 } | |
| 225 @catch (NSException* exception) { | |
| 226 NSLog(@"Exception %@ %@", [exception name], [exception reason]); | |
| 227 const char* message = | |
| 228 "Error! Unable to uninstall Chrome Remote Desktop Host."; | |
| 229 [self messageBox:message]; | |
| 230 } | 156 } |
| 231 | 157 |
| 232 [NSApp terminate:self]; | 158 AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0}; |
| 233 } | 159 AuthorizationRights rights = {1, &right}; |
| 234 | 160 AuthorizationFlags flags = kAuthorizationFlagDefaults | |
| 235 - (IBAction)cancel:(id)sender { | 161 kAuthorizationFlagInteractionAllowed | |
| 236 [NSApp terminate:self]; | 162 kAuthorizationFlagPreAuthorize | |
| 237 } | 163 kAuthorizationFlagExtendRights; |
| 238 | 164 status = AuthorizationCopyRights(authRef, &rights, NULL, flags, NULL); |
| 239 - (IBAction)handleMenuClose:(NSMenuItem*)sender { | 165 if (status == errAuthorizationSuccess) { |
| 240 [NSApp terminate:self]; | 166 RemotingUninstaller* uninstaller = |
| 167 [[[RemotingUninstaller alloc] init] autorelease]; |
| 168 [uninstaller remotingUninstallUsingAuth:authRef]; |
| 169 } |
| 170 return status; |
| 241 } | 171 } |
| 242 | 172 |
| 243 @end | 173 @end |
| 244 | |
| 245 int main(int argc, char* argv[]) | |
| 246 { | |
| 247 return NSApplicationMain(argc, (const char**)argv); | |
| 248 } | |
| 249 | |
| OLD | NEW |