From c63cfa0cb35c4898e510cce93d6bd029540add8e Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Mon, 22 Mar 2010 00:33:38 +0100 Subject: Installable overlay for OSX. --- macx/osax/osax.plist | 2 + macx/scripts/osxdist.py | 35 ++++++++++++-- src/mumble/Overlay.cpp | 37 +++++++++++++-- src/mumble/Overlay.h | 6 ++- src/mumble/Overlay.ui | 28 +++++++++-- src/mumble/Overlay_macx.mm | 113 ++++++++++++++++++++++++++++++++++++++++++-- src/mumble/Overlay_unix.cpp | 11 ++++- src/mumble/Overlay_win.cpp | 11 ++++- src/mumble/mumble.plist | 10 +++- 9 files changed, 233 insertions(+), 20 deletions(-) diff --git a/macx/osax/osax.plist b/macx/osax/osax.plist index 52fe7801f..c514e2e8f 100644 --- a/macx/osax/osax.plist +++ b/macx/osax/osax.plist @@ -35,5 +35,7 @@ + MumbleOverlayLoaderVersion + 1 diff --git a/macx/scripts/osxdist.py b/macx/scripts/osxdist.py index ba04666de..8f1693b55 100755 --- a/macx/scripts/osxdist.py +++ b/macx/scripts/osxdist.py @@ -7,7 +7,7 @@ # by Thomas Keller). # -import sys, os, string, re, shutil, plistlib, tempfile, exceptions, datetime +import sys, os, string, re, shutil, plistlib, tempfile, exceptions, datetime, tarfile from subprocess import Popen, PIPE from optparse import OptionParser @@ -31,6 +31,30 @@ def codesign(id, path): return retval return 0 +def create_overlay_tarball(sign=None): + print '* Creating overlay loader installation tarball' + + bundle = os.path.join('release', 'MumbleOverlay.osax') + if sign: + codesign(sign, bundle) + contents = [] + for e in os.walk(bundle): + root, dirs, files = e + contents.extend([root]+[os.path.join(root, f) for f in files]) + + tar = tarfile.open(os.path.join('release', 'MumbleOverlay.tar.bz2'), 'w:bz2') + for c in contents: + info = tar.gettarinfo(c) + info.name = c[len('release/'):] + info.uname = 'root' + info.gname = 'admin' + f = None + if info.isfile(): + f = open(c) + tar.addfile(info, f) + tar.close() + + class AppBundle(object): def is_system_lib(self, lib): @@ -226,6 +250,7 @@ class AppBundle(object): os.makedirs(dst) shutil.copy('release/libmumbleoverlay.dylib', dst) shutil.copy('release/mumble-overlay', dst) + shutil.copy('release/MumbleOverlay.tar.bz2', dst) def copy_codecs(self): ''' @@ -435,6 +460,9 @@ if __name__ == '__main__': # Fix .ini files os.system('cd scripts && sh mkini.sh') + # Fix overlay 'installer' tarball + create_overlay_tarball(options.codesign) + # Do the finishing touches to our Application bundle before release a = AppBundle('release/Mumble.app', ver) if not options.universal: @@ -461,7 +489,6 @@ if __name__ == '__main__': else: c.set_min_macosx_version('10.4.8') c.copy_g15helper() - c.copy_overlay() c.copy_plugins() c.copy_qt_plugins() c.handle_libs() @@ -477,11 +504,11 @@ if __name__ == '__main__': 'release/Mumble.app', 'release/Mumble.app/Contents/MacOS/murmurd', 'release/Mumble.app/Contents/MacOS/mumble-g15-helper', - 'release/Mumble.app/Contents/Overlay/mumble-overlay', 'release/Mumble.app/Contents/Plugins/liblink.dylib', 'release/Mumble.app/Contents/Plugins/libmanual.dylib', - 'release/Mumble.app/Contents/Overlay/libmumbleoverlay.dylib', 'release/Mumble.app/Contents/Codecs/libcelt0.0.7.0.dylib', + 'release/Mumble.app/Contents/Overlay/mumble-overlay', + 'release/Mumble.app/Contents/Overlay/libmumbleoverlay.dylib', # 1.1.x 'release/Mumble11x.app/', 'release/Mumble11x.app/Contents/MacOS/mumble-g15-helper', diff --git a/src/mumble/Overlay.cpp b/src/mumble/Overlay.cpp index fd49e31b2..717691551 100644 --- a/src/mumble/Overlay.cpp +++ b/src/mumble/Overlay.cpp @@ -48,13 +48,15 @@ OverlayConfig::OverlayConfig(Settings &st) : ConfigWidget(st) { setupUi(this); if (! Overlay::isInstalled()) { - if (Overlay::needsUpgrade()) { - qswOverlayPage->setCurrentWidget(qwOverlayUpgrade); - } else { - qswOverlayPage->setCurrentWidget(qwOverlayInstall); - } + qswOverlayPage->setCurrentWidget(qwOverlayInstall); + } else if (Overlay::needsUpgrade()) { + qswOverlayPage->setCurrentWidget(qwOverlayUpgrade); + } else { + qswOverlayPage->setCurrentWidget(qwOverlayConfig); } + qpbUninstall->setVisible(Overlay::supportsInstallableOverlay()); + qgs.setBackgroundBrush(QColor(128, 128, 128, 255)); qpScreen = QPixmap::grabWindow(QApplication::desktop()->winId()); @@ -91,6 +93,11 @@ OverlayConfig::OverlayConfig(Settings &st) : ConfigWidget(st) { qgvView->setScene(&qgs); qgvView->installEventFilter(this); + + // Attach the upgrade button to the install click handler. Currently, the + // actions they perform are the same. The distinction is only there to inform + // users as to what's actually going on. + connect(qpbUpgrade, SIGNAL(clicked()), this, SLOT(on_qpbInstall_clicked())); } void OverlayConfig::load(const Settings &r) { @@ -194,6 +201,26 @@ void OverlayConfig::on_qcbBlacklist_toggled(bool checked) { qswBlackWhiteList->setCurrentWidget(checked ? qwBlack : qwWhite); } +void OverlayConfig::on_qpbInstall_clicked() { + qpbInstall->setEnabled(false); + + if (Overlay::installFiles()) { + qswOverlayPage->setCurrentWidget(qwOverlayConfig); + } + + qpbInstall->setEnabled(true); +} + +void OverlayConfig::on_qpbUninstall_clicked() { + + qpbUninstall->setEnabled(false); + + if (Overlay::uninstallFiles()) { + qswOverlayPage->setCurrentWidget(qwOverlayInstall); + } + + qpbUninstall->setEnabled(true); +} OverlayEditorScene::OverlayEditorScene(QObject *p, const OverlaySettings &srcos) : QGraphicsScene(p), os(srcos) { diff --git a/src/mumble/Overlay.h b/src/mumble/Overlay.h index 4f6bd81fa..69b692a67 100644 --- a/src/mumble/Overlay.h +++ b/src/mumble/Overlay.h @@ -232,6 +232,8 @@ class OverlayConfig : public ConfigWidget, public Ui::OverlayConfig { bool eventFilter(QObject *, QEvent *); protected slots: + void on_qpbInstall_clicked(); + void on_qpbUninstall_clicked(); void on_qpbAdd_clicked(); void on_qpbRemove_clicked(); void on_qcbBlacklist_toggled(bool); @@ -329,9 +331,11 @@ class Overlay : public QObject { void platformInit(); + static bool supportsInstallableOverlay(); static bool isInstalled(); static bool needsUpgrade(); - static void installFiles(); + static bool installFiles(); + static bool uninstallFiles(); QLocalServer *qlsServer; QList qlClients; diff --git a/src/mumble/Overlay.ui b/src/mumble/Overlay.ui index d47fe487c..52a6147a8 100644 --- a/src/mumble/Overlay.ui +++ b/src/mumble/Overlay.ui @@ -6,8 +6,8 @@ 0 0 - 563 - 777 + 508 + 571 @@ -29,7 +29,7 @@ Options - + @@ -43,6 +43,26 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Uninstall Overlay + + + @@ -328,7 +348,7 @@ To upgrade these files to their latest versions, click the button below. - + Upgrade Mumble Overlay diff --git a/src/mumble/Overlay_macx.mm b/src/mumble/Overlay_macx.mm index 7a73ada1a..b077499d6 100644 --- a/src/mumble/Overlay_macx.mm +++ b/src/mumble/Overlay_macx.mm @@ -96,13 +96,120 @@ void Overlay::setActive(bool act) { static_cast(d)->setActive(act); } +bool Overlay::supportsInstallableOverlay() { + return true; +} + bool Overlay::isInstalled() { - return false; + bool ret = false; + + // Get the path where we expect the overlay loader to be installed. + NSString *path = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayLoaderBundle"]; + if (! path) { + qWarning("Overlay_macx: No MumbleOverlayLoaderPath specified in Info.plist. " + "Pretending overlay is already installed."); + return false; + } + + // Determine if the installed bundle is correctly installed (i.e. it's loadable) + NSBundle *bundle = [NSBundle bundleWithPath:path]; + return [bundle preflightAndReturnError:NULL]; } bool Overlay::needsUpgrade() { - return false; + // Get required version from our own Info.plist + NSUInteger reqVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayLoaderRequiredVersion"] unsignedIntegerValue]; + + // Load the overlay loader bundle. + NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayLoaderBundle"]]; + if (! bundle) { + qWarning("Overlay_macx: Unable to load the installed OSAX bundle. This shouldn't happen."); + return false; + } + + // Get its version. + NSUInteger curVersion = [[bundle objectForInfoDictionaryKey:@"MumbleOverlayLoaderVersion"] unsignedIntegerValue]; + + // If the two versions do not match up, we need to upgrade. + return curVersion != reqVersion; +} + +bool Overlay::installFiles() { + AuthorizationRef auth; + bool ret = false; + int pid, status; + OSStatus err; + + // Get the tarball that we should install. + NSString *tarballPath = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayInstallPayload"]]; + + // And the destination we should install it to. + NSString *destination = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayInstallDestintaion"]; + + if (! tarballPath || ! destination) { + qWarning("Overlay_macx: Info.plist does not specify installation parameters."); + return false; + } + + // Request an AuthorizationRef. This is a mechanism in Mac OS X that allows a parent program + // to spawn child processes with euid = 0. + // + // When attempting to launch the child process, a dialog will pop up requesting the user to + // authorize the launch by logging in with as a user with admin privileges. + err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth); + if (err == errAuthorizationSuccess) { + // This is the tar command that we execute to install our support files. + const char *argv[] = { "/usr/bin/tar", "-jxf", [tarballPath UTF8String], "-C", [destination UTF8String], NULL }; + // Launch the child process. + err = AuthorizationExecuteWithPrivileges(auth, argv[0], kAuthorizationFlagDefaults, const_cast(&argv[1]), NULL); + if (err == errAuthorizationSuccess) { + // And wait until it is dead. + do { + pid = wait(&status); + } while (pid == -1 && errno == EINTR); + ret = (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0); + } else + qWarning("Overlay_macx: Failed to AuthorizeExecuteWithPrivileges. (err=%i)", err); + } else + qWarning("Overlay_macx: Failed to acquire AuthorizationRef. (err=%i)", err); + + if (! ret) { + qWarning("Overlay_macx: Failure in installer process. (pid=%i, exited=%u, exitStatus=%u)", + pid, WIFEXITED(status), WEXITSTATUS(status)); + } + + // Free the AuthorizationRef that we acquired earlier. We're done launching priviledged children. + AuthorizationFree(auth, kAuthorizationFlagDefaults); + return ret; } -void Overlay::installFiles() { +bool Overlay::uninstallFiles() { + // Get the absolute path of our overlay loader bundle. + NSString *path = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MumbleOverlayLoaderBundle"]; + // Make sure it is unloaded, at least from ourselves, before we trash it. + [[NSBundle bundleWithPath:path] unload]; + + // This is a tiny AppleScript that kindly asks the Finder to throw + // the currently installed loader bundle into the trash. + // + // This is nice because we don't need to have any privileged code in + // Mumble itself that does it, and because it goes into the user's + // trash. + NSString *uninstallScript = [NSString stringWithFormat:@"" + "set bundlePath to POSIX file \"%@\"\n" + "tell application \"Finder\" to delete bundlePath\n", path]; + + // Execute the script, and hope for the best. + NSError *error = nil; + NSString *script = [[NSAppleScript alloc] initWithSource:uninstallScript]; + [script executeAndReturnError:&error]; + [script release]; + + if (error) { + NSLog(@"%@", error); + return false; + } + + return true; } diff --git a/src/mumble/Overlay_unix.cpp b/src/mumble/Overlay_unix.cpp index 920e41355..de0f2c153 100644 --- a/src/mumble/Overlay_unix.cpp +++ b/src/mumble/Overlay_unix.cpp @@ -37,6 +37,10 @@ void Overlay::platformInit() { void Overlay::setActive(bool) { } +bool Overlay::supportsInstallableOverlay() { + return false; +} + bool Overlay::isInstalled() { return true; } @@ -45,5 +49,10 @@ bool Overlay::needsUpgrade() { return false; } -void Overlay::installFiles() { +bool Overlay::installFiles() { + return false; +} + +bool Overlay::uninstallFiles() { + return false; } diff --git a/src/mumble/Overlay_win.cpp b/src/mumble/Overlay_win.cpp index 3111e1f0a..d7139cebf 100644 --- a/src/mumble/Overlay_win.cpp +++ b/src/mumble/Overlay_win.cpp @@ -104,6 +104,10 @@ void Overlay::setActive(bool act) { static_cast(d)->setActive(act); } +bool Overlay::supportsInstallableOverlay() { + return false; +} + bool Overlay::isInstalled() { return true; } @@ -112,5 +116,10 @@ bool Overlay::needsUpgrade() { return false; } -void Overlay::installFiles() { +bool Overlay::installFiles() { + return false; +} + +bool Overlay::uninstallFiles() { + return false; } diff --git a/src/mumble/mumble.plist b/src/mumble/mumble.plist index 4fcee5a7e..a9f053f01 100644 --- a/src/mumble/mumble.plist +++ b/src/mumble/mumble.plist @@ -1,5 +1,5 @@ - + CFBundleExecutable @@ -31,5 +31,13 @@ Copyright (c) 2005-2010 Thorvald Natvig <slicer@users.sourceforge.net> MumbleOverlayLibrary Contents/Overlay/libmumbleoverlay.dylib + MumbleOverlayInstallPayload + Contents/Overlay/MumbleOverlay.tar.bz2 + MumbleOverlayInstallDestintaion + /Library/ScriptingAdditions/ + MumbleOverlayLoaderBundle + /Library/ScriptingAdditions/MumbleOverlay.osax + MumbleOverlayLoaderRequiredVersion + 1 -- cgit v1.2.3