# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Chromoting helper to install/uninstall host and replace pref pane."""
import abc
import os
import shutil
import sys
import subprocess
class ChromotingHelper(object):
"""Chromoting helper base class."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def InstallHost(self, bin_dir):
"""Installs the chromoting host"""
return
@abc.abstractmethod
def UninstallHost(self, bin_dir):
"""Uninstalls the chromoting host"""
return
class ChromotingHelperMac(ChromotingHelper):
"""Chromoting Helper class for Mac.
Installs/uninstalls host and replace the pref pane for testing purpose.
"""
def InstallHost(self, bin_dir):
"""Installs host on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
# Run most of the steps here with login user
login_uid = os.getuid()
os.seteuid(login_uid)
# Change the working dir to the dir that has the host zip file
current_dir = os.getcwd()
pyautolib_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(bin_dir)
host_dir = 'remoting-me2me-host-mac'
output_dir = os.path.join(host_dir, 'output')
# Remove remoting-me2me-host-mac dir just in case
shutil.rmtree(host_dir, True)
# Unzip the host archive and prepare the files/dirs
subprocess.call('unzip remoting-me2me-host-mac.zip', shell=True)
subprocess.call('mkdir ' + output_dir, shell=True)
# Prepare security identity for code signing purpose
os.seteuid(0)
key_chain = '/Library/Keychains/ChromotingTest'
password = '1111'
key = os.path.join(pyautolib_dir, 'chromoting_key.p12')
cert = os.path.join(pyautolib_dir, 'chromoting_cert.p12')
subprocess.call(['security', 'delete-keychain', key_chain])
subprocess.call(['security', 'create-keychain', '-p',
password, key_chain])
subprocess.call(['security', 'import', key,
'-k', key_chain, '-P', password, '-A'])
subprocess.call(['security', 'import', cert,
'-k', key_chain, '-P', password])
os.seteuid(login_uid)
# Sign the host
do_signing = os.path.join(host_dir, 'do_signing.sh')
subprocess.call(do_signing + ' ' + output_dir + ' ' + host_dir + ' ' +
key_chain + ' "Chromoting Test"', shell=True)
# Remove security identify
os.seteuid(0)
subprocess.call(['security', 'delete-keychain', key_chain])
os.seteuid(login_uid)
# Figure out the dmg name
version = ""
for output_file in os.listdir(output_dir):
if output_file.endswith('.dmg'):
version = os.path.basename(output_file)[len('ChromotingHost-'):-4]
# Mount before installation
dmg = os.path.join(output_dir, 'ChromotingHost-' + version + '.dmg')
subprocess.call('hdiutil' + ' mount ' + dmg, shell=True)
# Install host
os.seteuid(0)
mpkg = os.path.join('/Volumes', 'Chromoting Host ' + version,
'Chromoting Host.pkg')
subprocess.call(['/usr/sbin/installer', '-pkg',
mpkg, '-target', '/'])
os.seteuid(login_uid)
# Unmount after installation
mounted = os.path.join('/Volumes', 'Chromoting Host ' + version)
subprocess.call('hdiutil detach "' + mounted + '"', shell=True)
# Clean up remoting-me2me-host-mac dir
shutil.rmtree(host_dir, True)
# Resume the original working dir
os.chdir(current_dir)
def UninstallHost(self, bin_dir):
"""Uninstalls host on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
uninstall_app = os.path.join('/', 'Applications',
'Chromoting Host Uninstaller.app',
'Contents', 'MacOS',
'remoting_host_uninstaller')
subprocess.call([uninstall_app, '--no-ui'])
def ReplacePrefPaneMac(self, operation):
"""Constructs mock pref pane to replace the actual pref pane on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
pref_pane_dir = os.path.join('/Library', 'PreferencePanes')
mock_pref_pane = os.path.join(pref_pane_dir, 'mock_pref_pane')
pref_pane = os.path.join(pref_pane_dir,
'org.chromium.chromoting.prefPane')
mock_pref_pane_python = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'mock_pref_pane.py')
# When the symlink from real pref pane to mock pref pane exists,
# mock pref pane will be modified to be a dir when the host is installed.
# After the host is installed and mock pref pane is modified to be a file,
# it will be a file until next host installation.
if os.path.isdir(mock_pref_pane):
shutil.rmtree(mock_pref_pane, True)
elif os.path.isfile(mock_pref_pane):
os.remove(mock_pref_pane)
mock_pref_pane_file = open(mock_pref_pane, 'w')
mock_pref_pane_file.write('#!/bin/bash\n')
mock_pref_pane_file.write('\n')
mock_pref_pane_file.write('suid-python' +
' ' + mock_pref_pane_python + ' ' + operation)
mock_pref_pane_file.close()
subprocess.call(['chmod', 'a+x', mock_pref_pane])
# The real pref pane is a dir if the host is installed on a clean machine.
# Once the test is run on the machine, real pref pane will be replaced to
# a symlink.
if os.path.isdir(pref_pane):
shutil.rmtree(pref_pane, True)
elif os.path.isfile(pref_pane):
os.remove(pref_pane)
subprocess.call(['ln', '-s', mock_pref_pane, pref_pane])
class ChromotingHelperWindows(ChromotingHelper):
"""Chromoting Helper class for Windows for installing/uninstalling host."""
def InstallHost(self, bin_dir):
"""Installs host on Windows."""
host_msi = os.path.join(bin_dir, 'chromoting.msi')
subprocess.Popen(['msiexec', '/i', host_msi, '/passive']).wait()
def UninstallHost(self, bin_dir):
"""Uninstalls host on Windows."""
host_msi = os.path.join(bin_dir, 'chromoting.msi')
subprocess.Popen(['msiexec', '/x', host_msi, '/passive']).wait()
def Main():
"""Main function to dispatch operations."""
assert sys.platform.startswith('win') or \
sys.platform.startswith('darwin'), \
'Only support Windows and Mac'
if sys.platform.startswith('win'):
helper = ChromotingHelperWindows()
elif sys.platform.startswith('darwin'):
helper = ChromotingHelperMac()
if sys.argv[1] == 'install':
helper.InstallHost(sys.argv[2])
elif sys.argv[1] == 'uninstall':
helper.UninstallHost(sys.argv[2])
elif sys.argv[1] in ['enable', 'disable', 'changepin']:
assert sys.platform.startswith('darwin'), \
'Replacing pref pane is Mac specific'
helper.ReplacePrefPaneMac(sys.argv[1])
else:
print >>sys.stderr, 'Invalid syntax'
return 1
if __name__ == '__main__':
Main()