Last active
October 26, 2017 21:24
-
-
Save sheagcraig/4fa2a11b7f1738e11c79 to your computer and use it in GitHub Desktop.
Munki/Outset/PyObjC Self Service Set Apple Mail as Default Handler for mailto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Put this at /usr/local/share/luggage/luggage.local to reap the rewards. | |
USE_PKGBUILD=1 | |
l_usr_local_outset: l_usr_local | |
@sudo mkdir -p ${WORK_D}/usr/local/outset/{firstboot-packages,firstboot-scripts,everyboot-scripts,login-every,login-once,on-demand} | |
@sudo chown -R root:wheel ${WORK_D}/usr/local/outset | |
@sudo chmod -R 755 ${WORK_D}/usr/local/outset | |
pack-outset-firstboot-packages-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/firstboot-packages | |
pack-outset-firstboot-scripts-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/firstboot-scripts | |
pack-outset-everyboot-scripts-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/everyboot-scripts | |
pack-outset-login-every-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/login-every | |
pack-outset-login-once-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/login-once | |
pack-outset-on-demand-%: % l_usr_local_outset | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/outset/on-demand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
include /usr/local/share/luggage/luggage.make | |
TITLE=set_apple_mail_handler | |
REVERSE_DOMAIN=com.sas | |
PAYLOAD=\ | |
pack-usr-local-sas-sas.png \ | |
pack-script-postinstall \ | |
pack-outset-on-demand-set_apple_mail_handler.py \ | |
PACKAGE_VERSION=1.0.0 | |
pack-usr-local-sas-%: % l_usr_local_sas | |
@sudo ${INSTALL} -m 755 -g wheel -o root "${<}" ${WORK_D}/usr/local/sas | |
l_usr_local_sas: l_usr_local | |
@sudo mkdir -p ${WORK_D}/usr/local/sas | |
@sudo chown -R root:wheel ${WORK_D}/usr/local/sas | |
@sudo chmod -R 755 ${WORK_D}/usr/local/sas |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
touch /private/tmp/.com.github.outset.ondemand.launchd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>OnDemand</key> | |
<true/> | |
<key>_metadata</key> | |
<dict> | |
<key>created_by</key> | |
<string>shcrai</string> | |
<key>creation_date</key> | |
<date>2016-01-21T21:03:53Z</date> | |
<key>munki_version</key> | |
<string>2.4.0.2561</string> | |
<key>os_version</key> | |
<string>10.11.3</string> | |
</dict> | |
<key>autoremove</key> | |
<false/> | |
<key>catalogs</key> | |
<array> | |
<string>production</string> | |
</array> | |
<key>category</key> | |
<string>Self Service</string> | |
<key>description</key> | |
<string>Due to a bug in OS X, the default mail handler cannot be set by the user after it has been set once without programmatic intervention. To restore Apple Mail as the default mail reader, run this item, and then following the prompting instructions, quit all of your open applications prior to continuing. This change requires a logout.</string> | |
<key>developer</key> | |
<string>SAS</string> | |
<key>display_name</key> | |
<string>Set Default Mail Handler to Apple Mail</string> | |
<key>icon_hash</key> | |
<string>d422e6891ec0affeecfb018fde3d64754c6ef5b71bc61e1cac18dc254cb7a25e</string> | |
<key>icon_name</key> | |
<string>sas.png</string> | |
<key>installed_size</key> | |
<integer>20</integer> | |
<key>installer_item_hash</key> | |
<string>4adab62eac994be227667bc855885aa74a3fefc2d33c74e5fba0fbe76f22f12e</string> | |
<key>installer_item_location</key> | |
<string>config/set_apple_mail_handler-1.0.0.pkg</string> | |
<key>installer_item_size</key> | |
<integer>12</integer> | |
<key>minimum_os_version</key> | |
<string>10.5.0</string> | |
<key>name</key> | |
<string>set_apple_mail_handler</string> | |
<key>receipts</key> | |
<array> | |
<dict> | |
<key>installed_size</key> | |
<integer>20</integer> | |
<key>packageid</key> | |
<string>com.sas.set_apple_mail_handler</string> | |
<key>version</key> | |
<string>1.0.0</string> | |
</dict> | |
</array> | |
<key>requires</key> | |
<array> | |
<string>outset</string> | |
</array> | |
<key>uninstall_method</key> | |
<string>removepackages</string> | |
<key>uninstallable</key> | |
<true/> | |
<key>version</key> | |
<string>1.0.0</string> | |
</dict> | |
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# Copyright (C) 2016 Shea G Craig | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
Help user set default mail client despite OS X bug. | |
Sets the default mail reader to Apple Mail (because we configure our | |
clients to use Outlook) using the LaunchServices framework, and then | |
immediately log out. | |
Displays a dialog instructing user to close open applications that may | |
block logout from occuring, rather than execute a Volcanic Instant Death | |
Logout. | |
This uses code from my auto_logout project for the applescripting and | |
alert dialog presentation. | |
This was written to be part of an OnDemand item in Munki's Managed | |
Software Center, using outset's on-demand feature to run as the current | |
console user (as it would otherwise run as root and not work). | |
""" | |
import os | |
import subprocess | |
import sys | |
# pylint: disable=no-name-in-module | |
from AppKit import (NSImage, NSAlert, NSTimer, NSRunLoop, NSApplication, | |
NSSound, NSModalPanelRunLoopMode, NSApp, | |
NSRunAbortedResponse, NSAlertFirstButtonReturn) | |
# pylint: enable=no-name-in-module | |
from LaunchServices import LSSetDefaultHandlerForURLScheme | |
from SystemConfiguration import SCDynamicStoreCopyConsoleUser | |
# Sound played when alert is presented. See README. | |
ALERT_SOUND = "Submarine" | |
# Icon used in the alerts. If not present, the Python rocket is used | |
# instead. | |
ICON = "/usr/local/sas/sas.png" | |
# Methods are named according to PyObjC/Cocoa style. | |
# pylint: disable=invalid-name | |
class Alert(NSAlert): | |
"""Subclasses NSAlert to include a timeout.""" | |
def init(self): # pylint: disable=super-on-old-class | |
"""Add an instance variable for our timer.""" | |
self = super(Alert, self).init() | |
self.timer = None | |
self.alert_sound = None | |
return self | |
def setIconWithContentsOfFile_(self, path): | |
"""Convenience method for adding an icon. | |
Args: | |
path: String path to a valid NSImage filetype (png) | |
""" | |
icon = NSImage.alloc().initWithContentsOfFile_(path) | |
self.setIcon_(icon) # pylint: disable=no-member | |
def setAlertSound_(self, name): | |
"""Set the sound to play when alert is presented. | |
Args: | |
name: String name of a system sound. See the README. | |
""" | |
self.alert_sound = name | |
def setTimeToGiveUp_(self, time): | |
"""Configure alert to give up after time seconds.""" | |
# Cocoa objects must use class func alloc().init(), so pylint | |
# doesn't see our init(). | |
# pylint: disable=attribute-defined-outside-init | |
self.timer = \ | |
NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_( | |
time, self, "_killWindow", None, False) | |
# pylint: enable=attribute-defined-outside-init | |
def present(self): | |
"""Present the Alert, giving up after configured time.. | |
Returns: Int result code, based on PyObjC enums. See NSAlert | |
Class reference, but result should be one of: | |
User clicked the cancel button: | |
NSAlertFirstButtonReturn = 1000 | |
Alert timed out: | |
NSRunAbortedResponse = -1001 | |
""" | |
if self.timer: | |
NSRunLoop.currentRunLoop().addTimer_forMode_( | |
self.timer, NSModalPanelRunLoopMode) | |
# Start a Cocoa application by getting the shared app object. | |
# Make the python app the active app so alert is noticed. | |
app = NSApplication.sharedApplication() | |
app.activateIgnoringOtherApps_(True) | |
if self.alert_sound: | |
sound = NSSound.soundNamed_(self.alert_sound).play() | |
result = self.runModal() # pylint: disable=no-member | |
print result | |
return result | |
# pylint: disable=no-self-use | |
def _killWindow(self): | |
"""Abort the modal window as managed by NSApp.""" | |
NSApp.abortModal() | |
# pylint: enable=no-self-use | |
# pylint: enable=no-init | |
# pylint: enable=invalid-name | |
def build_alert(): | |
"""Build an alert for auto-logout notifications.""" | |
alert = Alert.alloc().init() # pylint: disable=no-member | |
alert.setMessageText_( | |
"Setting the default mail reader requires an immediate logout " | |
"due to a bug in OS X.") | |
alert.setInformativeText_("Please quit all applications and hit 'Okay'. " | |
"Your computer will then logout.") | |
alert.addButtonWithTitle_("Okay") | |
alert.addButtonWithTitle_("Cancel") | |
alert.setIconWithContentsOfFile_(ICON) | |
alert.setAlertSound_(ALERT_SOUND) | |
return alert | |
def set_mail_reader(bundle_id): | |
"""Use LaunchServices to set mailto handler. | |
There is a bug in OS X that allows you to set this only once. | |
Afterwards, if you set it again, it will revert to the previous | |
setting within about 10 seconds. Until this is fixed, logging out | |
really quickly seems to work around it. | |
Args: | |
bundle_id (String): Bundle Identifier for the app to handle | |
mail. Caps do not seem to matter. | |
Returns: | |
Integer return code (0 is a success) as per | |
https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/ | |
""" | |
return LSSetDefaultHandlerForURLScheme("mailto", bundle_id) | |
def really_log_out(): | |
"""Log out without the prompt. Will still ask about open apps.""" | |
run_applescript('tell application "loginwindow" to «event aevtrlgo»') | |
def run_applescript(script): | |
"""Run an applescript""" | |
process = subprocess.Popen(['osascript', '-'], stdout=subprocess.PIPE, | |
stdin=subprocess.PIPE, stderr=subprocess.PIPE) | |
result, err = process.communicate(script) | |
if err: | |
raise Exception(err) | |
return process.returncode | |
def build_abort_alert(): | |
"""Build an alert for letting user know it failed.""" | |
alert = Alert.alloc().init() # pylint: disable=no-member | |
alert.setMessageText_( | |
"Failed to set default mail handler") | |
alert.setInformativeText_("Please contact the Helpdesk.") | |
alert.addButtonWithTitle_("Okay") | |
alert.addButtonWithTitle_("Cancel") | |
alert.setIconWithContentsOfFile_(ICON) | |
alert.setAlertSound_(ALERT_SOUND) | |
return alert | |
def main(): | |
alert = build_alert() | |
if alert.present() != NSAlertFirstButtonReturn: | |
print "User Cancelled" | |
sys.exit() | |
else: | |
if set_mail_reader("com.apple.mail") == 0: | |
really_log_out() | |
else: | |
abort_alert = build_abort_alert() | |
alert.present() | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment