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 |