blob: abaa3a0129a9e524c786cb7373a13a42b9cd7d44 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "Uninstaller.h"
#include <Security/Security.h>
#import "DialogsController.h"
#include <string>
#include "base/init.h"
#include "base/mac_util.h"
#include "base/url.h"
#include "base/version.h"
namespace {
NSURL *kUninstallSurveyUrl = nil;
NSString *kUninstallerScriptPath = nil;
void InitializeGlobalVariables() {
string url;
mozc::URL::GetUninstallationSurveyURL(mozc::Version::GetMozcVersion(), &url);
NSString *uninstallUrl = [[NSString alloc] initWithBytes:url.data()
length:url.size()
encoding:NSUTF8StringEncoding];
kUninstallSurveyUrl = [NSURL URLWithString:uninstallUrl];
kUninstallerScriptPath =
[[NSBundle mainBundle] pathForResource:@"uninstaller"
ofType:@"py"
inDirectory:nil];
}
REGISTER_MODULE_INITIALIZER(GoogleJapaneseInputUninstall,
InitializeGlobalVariables());
bool GetPrevilegeRights(AuthorizationRef *auth) {
OSStatus status;
AuthorizationFlags authFlags = kAuthorizationFlagDefaults;
status = AuthorizationCreate(
NULL, kAuthorizationEmptyEnvironment, authFlags, auth);
if (status != errAuthorizationSuccess) {
return status;
}
AuthorizationItem *newItem = new AuthorizationItem();
if (!newItem) {
return false;
}
AuthorizationItem item = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rights = {1, &item};
authFlags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
status = AuthorizationCopyRights(*auth, &rights, NULL, authFlags, NULL);
return status == errAuthorizationSuccess;
}
bool OpenUninstallSurvey() {
return [[NSWorkspace sharedWorkspace] openURL:kUninstallSurveyUrl];
}
bool DeleteFiles(const AuthorizationRef &auth) {
const char *kRmPath = "/bin/rm";
const char *rmArgs[] = {"-rf", NULL, NULL};
#ifdef GOOGLE_JAPANESE_INPUT_BUILD
const char *kRemovePaths[] = {
"/Library/Input Methods/GoogleJapaneseInput.app",
"/Library/LaunchAgents/com.google.inputmethod.Japanese.Converter.plist",
"/Library/LaunchAgents/com.google.inputmethod.Japanese.Renderer.plist",
"/Applications/GoogleJapaneseInput.localized",
NULL};
#else // GOOGLE_JAPANESE_INPUT_BUILD
const char *kRemovePaths[] = {
"/Library/Input Methods/Mozc.app",
"/Library/LaunchAgents/org.mozc.inputmethod.Japanese.Converter.plist",
"/Library/LaunchAgents/org.mozc.inputmethod.Japanese.Renderer.plist",
"/Applications/Mozc",
NULL};
#endif // GOOGLE_JAPANESE_INPUT_BUILD
for (int i = 0; kRemovePaths[i] != NULL; ++i) {
rmArgs[1] = kRemovePaths[i];
OSStatus status = AuthorizationExecuteWithPrivileges(
auth, kRmPath, kAuthorizationFlagDefaults,
const_cast<char * const *>(rmArgs), NULL);
if (status != errAuthorizationSuccess) {
NSLog(@"Failed to remove path: %s", kRemovePaths[i]);
return false;
}
}
return true;
}
bool UnregisterKeystoneTicket(const AuthorizationRef &auth) {
const char *kKsadminPath = "/Library/Google/GoogleSoftwareUpdate/"
"GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin";
const char *kKsadminArgs[] =
{"--delete", "--productid", "com.google.JapaneseIME", NULL};
OSStatus status = AuthorizationExecuteWithPrivileges(
auth, kKsadminPath, kAuthorizationFlagDefaults,
const_cast<char * const *>(kKsadminArgs), NULL);
return status == errAuthorizationSuccess;
}
bool RunReboot(const AuthorizationRef &auth) {
// TODO(mukai): Use OS-specific API instead of calling reboot command.
const char *rebootPath = "/sbin/reboot";
char * args[] = {NULL};
OSStatus status = AuthorizationExecuteWithPrivileges(
auth, rebootPath, kAuthorizationFlagDefaults, args, NULL);
return status == errAuthorizationSuccess;
}
} // anonymous namespace
@implementation Uninstaller
+ (void)doUninstall:(DialogsController *)dialogs {
mozc::MacUtil::RemovePrelauncherLoginItem();
AuthorizationRef auth;
if (!GetPrevilegeRights(&auth)) {
[dialogs reportAuthError];
AuthorizationFree(auth, kAuthorizationFlagDefaults);
return;
}
#ifdef GOOGLE_JAPANESE_INPUT_BUILD
if (OpenUninstallSurvey() &&
DeleteFiles(auth) && UnregisterKeystoneTicket(auth)) {
#else // GOOGLE_JAPANESE_INPUT_BUILD
if (DeleteFiles(auth)) {
#endif // GOOGLE_JAPANESE_INPUT_BUILD
if ([dialogs reportUninstallSuccess]) {
RunReboot(auth);
}
} else {
[dialogs reportUninstallError];
}
AuthorizationFree(auth, kAuthorizationFlagDefaults);
}
@end