1 | from Authorization import Authorization, kAuthorizationFlagDestroyRights |
---|
2 | from Foundation import NSBundle, NSNotificationCenter, NSObject, NSURL |
---|
3 | from AppKit import NSWorkspace |
---|
4 | from appscript import app, k, its |
---|
5 | from appscript.specifier import CommandError |
---|
6 | import os, osax, subprocess, tempfile, SCNetworkReachability |
---|
7 | |
---|
8 | appBundle = NSBundle.mainBundle() |
---|
9 | |
---|
10 | def ensureKerberosPrincipalsValid(principals): |
---|
11 | kerberosApp = app(id='edu.mit.Kerberos.KerberosApp') |
---|
12 | validPrincipals = kerberosApp.caches.filter( \ |
---|
13 | (its.time_remaining.startswith('Expired').NOT) \ |
---|
14 | .AND(its.time_remaining.startswith('Not Valid').NOT)) \ |
---|
15 | .principal.get() |
---|
16 | for principal in principals: |
---|
17 | if principal not in validPrincipals: |
---|
18 | # XXX make these async |
---|
19 | kerberosApp.renew_tickets(for_principal=principal) |
---|
20 | # kerberosApp.get_tickets(for_principal=principal) |
---|
21 | |
---|
22 | def setVolumePercent(percent): |
---|
23 | # XXX should use CoreAudio: see Pester/Source/NJRSoundManager.m |
---|
24 | osax.setvolume(int(7 * percent)) |
---|
25 | |
---|
26 | def _openURL(url): |
---|
27 | ws = NSWorkspace.sharedWorkspace() |
---|
28 | url = NSURL.URLWithString_(url) |
---|
29 | return ws.openURL_(url) |
---|
30 | |
---|
31 | class _LDURLWatcher(NSObject): |
---|
32 | def URLIsReachable_(self, notification): |
---|
33 | _openURL(notification.object()) |
---|
34 | |
---|
35 | _urlWatcher = _LDURLWatcher.alloc().init() |
---|
36 | NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( |
---|
37 | _urlWatcher, 'URLIsReachable:', SCNetworkReachability.SCNetworkIsReachable, |
---|
38 | None) |
---|
39 | |
---|
40 | def openURL(url): |
---|
41 | hostname = NSURL.URLWithString_(url).host() |
---|
42 | if not SCNetworkReachability.notify_when_reachable(hostname, url): |
---|
43 | _openURL(url) |
---|
44 | |
---|
45 | def openInBackground(path): |
---|
46 | ws = NSWorkspace.sharedWorkspace() |
---|
47 | path = os.path.expanduser(path) |
---|
48 | return ws.openFile_withApplication_andDeactivate_(path, None, False) |
---|
49 | |
---|
50 | def openApplicationWithName(name): |
---|
51 | ws = NSWorkspace.sharedWorkspace() |
---|
52 | return ws.launchApplication_(name) |
---|
53 | |
---|
54 | def setDisplayBrightnessPercent(percent): |
---|
55 | # XXX create brightness module |
---|
56 | pathToBrightness = appBundle.pathForResource_ofType_('brightness', None) |
---|
57 | subprocess.call([pathToBrightness, str(percent)], stderr=subprocess.STDOUT, |
---|
58 | stdout=subprocess.PIPE) |
---|
59 | |
---|
60 | def setAdiumStatus(status): |
---|
61 | # XXX doesn't match preset status if available |
---|
62 | adiumApp = app(id='com.adiumX.adiumX') |
---|
63 | if adiumApp.my_status_type() == k.offline: |
---|
64 | adiumApp.my_status.status_message_string.set(status) |
---|
65 | else: |
---|
66 | adiumApp.my_status_message.set(status) |
---|
67 | adiumApp.my_status_type.set(k.available) |
---|
68 | |
---|
69 | def moveWindow(appName, windowName, position, ifSizeMatches=None): |
---|
70 | # XXX support locations relative to screen corners |
---|
71 | systemEventsApp = app(id='com.apple.systemevents') |
---|
72 | try: |
---|
73 | if ifSizeMatches: |
---|
74 | size = tuple(systemEventsApp.processes[appName].windows[windowName].size()) |
---|
75 | ifSizeMatches = tuple(ifSizeMatches) |
---|
76 | if size != ifSizeMatches: |
---|
77 | print "Window size didn't match: %s in %s" % (windowName, appName) |
---|
78 | print "- Wanted %s, got %s" % (ifSizeMatches, size) |
---|
79 | return False |
---|
80 | systemEventsApp.processes[appName].windows[windowName].position.set(position) |
---|
81 | except CommandError: |
---|
82 | print "CommandError: %s in %s" % (windowName, appName) |
---|
83 | return False |
---|
84 | return True |
---|
85 | |
---|
86 | def terminalDo(command): |
---|
87 | # XXX if this launches Terminal, it inherits our PYTHONPATH |
---|
88 | terminalApp = app(id='com.apple.Terminal') |
---|
89 | # XXX this does not create a new Terminal window; fix |
---|
90 | terminalApp.do_script(command + '; exit') |
---|
91 | |
---|
92 | _auth = None |
---|
93 | |
---|
94 | def authorizationDo(command, *args): |
---|
95 | global _auth |
---|
96 | if not _auth: |
---|
97 | _auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,)) |
---|
98 | return _auth.executeWithPrivileges(command, *args) |
---|
99 | |
---|
100 | def stopVPNC(): |
---|
101 | # killall uses your uid, not your euid to determine which user's |
---|
102 | # processes to kill; without '-u root', this fails |
---|
103 | authorizationDo('/usr/bin/killall', '-u', 'root', 'vpnc') |
---|
104 | |
---|
105 | def startVPNC(vpncProfile=None): |
---|
106 | stopVPNC() |
---|
107 | args = ['--kernel-ipsec'] |
---|
108 | if vpncProfile: |
---|
109 | args.append('/etc/vpnc/%s.conf' % vpncProfile) |
---|
110 | # XXX get password from keychain, then use: |
---|
111 | # authorizationDo('/usr/local/sbin/vpnc', *args) |
---|
112 | terminalDo('sudo /usr/local/sbin/vpnc %s' % ' '.join(args)) |
---|