Mac OS X menu item for quickly enabling/disabling HTTP proxy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 rivejä
7.4 KiB

  1. #!/usr/bin/env python
  2. from Foundation import NSLog, kCFRunLoopCommonModes, kCFAllocatorDefault, CFDictionaryGetValue, CFRunLoopAddSource
  3. from AppKit import NSObject, NSImage, NSStatusBar, NSVariableStatusItemLength, NSMenu, NSMenuItem, NSRunLoop, NSOnState, NSApp, NSLog, NSOffState, NSApplication
  4. from SystemConfiguration import kSCNetworkProtocolTypeProxies, kSCPropNetProxiesFTPEnable, kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesSOCKSEnable, kSCNetworkProtocolTypeProxies
  5. from SystemConfiguration import SCDynamicStoreCreate, SCNetworkServiceCopyProtocol, SCNetworkProtocolGetConfiguration, SCDynamicStoreCopyValue, SCPreferencesCreate, SCNetworkServiceCopyAll, SCNetworkServiceGetInterface, SCNetworkInterfaceGetBSDName, SCDynamicStoreSetNotificationKeys, SCDynamicStoreCreateRunLoopSource, SCDynamicStoreCopyProxies, SCNetworkServiceGetName
  6. import commands, re
  7. class ToggleProxy(NSObject):
  8. # This is a dictionary of the proxy-types we support, each with a
  9. # dictionary of some unique attributes for each, namely:
  10. #
  11. # 'pref' : This is a constant defining which preference itemmarks if this proxy is enabled
  12. # 'title' : This is what will appear in the menu
  13. # 'action' : This is the method that will be called if the user toggles this proxies menuitem
  14. # 'keyEquivalent' : Self-explanatory, but unused
  15. # 'menuitem' : This will store the menu item for this proxy once it is created
  16. proxies = {
  17. 'ftp' : { 'pref': kSCPropNetProxiesFTPEnable, 'title': 'FTP Proxy', 'action': 'toggleFtpProxy:', 'keyEquivalent': "", 'menuitem': None },
  18. 'http' : { 'pref': kSCPropNetProxiesHTTPEnable, 'title': 'HTTP Proxy', 'action': 'toggleHttpProxy:', 'keyEquivalent': "", 'menuitem': None },
  19. 'https': { 'pref': kSCPropNetProxiesHTTPSEnable, 'title': 'HTTPS Proxy', 'action': 'toggleHttpsProxy:', 'keyEquivalent': "", 'menuitem': None },
  20. 'rtsp' : { 'pref': kSCPropNetProxiesRTSPEnable, 'title': 'RTSP Proxy', 'action': 'toggleRtspProxy:', 'keyEquivalent': "", 'menuitem': None },
  21. 'socks': { 'pref': kSCPropNetProxiesSOCKSEnable, 'title': 'SOCKS Proxy', 'action': 'toggleSocksProxy:', 'keyEquivalent': "", 'menuitem': None },
  22. }
  23. def applicationDidFinishLaunching_(self, notification):
  24. # load icon files
  25. self.active_image = NSImage.imageNamed_("active")
  26. self.inactive_image = NSImage.imageNamed_("inactive")
  27. # make status bar item
  28. self.statusitem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength)
  29. self.statusitem.retain()
  30. self.statusitem.setHighlightMode_(False)
  31. self.statusitem.setEnabled_(True)
  32. # insert a menu into the status bar item
  33. self.menu = NSMenu.alloc().init()
  34. self.statusitem.setMenu_(self.menu)
  35. # open connection to the dynamic (configuration) store
  36. self.store = SCDynamicStoreCreate(None, "name.klep.toggleproxy", self.dynamicStoreCallback, None)
  37. proxyRef = SCNetworkServiceCopyProtocol(self.service, kSCNetworkProtocolTypeProxies)
  38. prefDict = SCNetworkProtocolGetConfiguration(proxyRef)
  39. separatorRequired = False
  40. # For each of the proxies we are concerned with, check to see if any
  41. # are configured. If so (even if not enabled), create a menuitem for
  42. # that proxy type.
  43. for proxy in self.proxies.values():
  44. enabled = CFDictionaryGetValue(prefDict, proxy['pref'])
  45. if enabled is not None:
  46. proxy['menuitem'] = self.menu.addItemWithTitle_action_keyEquivalent_(
  47. proxy['title'],
  48. proxy['action'],
  49. proxy['keyEquivalent']
  50. )
  51. separatorRequired = True
  52. else:
  53. proxy['menuitem'] = None
  54. if separatorRequired:
  55. self.menu.addItem_(NSMenuItem.separatorItem())
  56. # Need a way to quit
  57. self.menu.addItemWithTitle_action_keyEquivalent_("Quit", "quitApp:", "")
  58. # Start working
  59. # self.loadNetworkServices()
  60. self.watchForProxyChanges()
  61. self.updateProxyStatus()
  62. @property
  63. def interface(self):
  64. # get primary interface
  65. return SCDynamicStoreCopyValue(self.store, 'State:/Network/Global/IPv4')['PrimaryInterface']
  66. @property
  67. def service(self):
  68. """ Returns the service relating to self.interface """
  69. prefs = SCPreferencesCreate(kCFAllocatorDefault, 'PRG', None)
  70. # Fetch the list of services
  71. for serviceRef in SCNetworkServiceCopyAll(prefs):
  72. interface = SCNetworkServiceGetInterface(serviceRef)
  73. if self.interface == SCNetworkInterfaceGetBSDName(interface):
  74. return serviceRef
  75. return None
  76. def watchForProxyChanges(self):
  77. """ install a watcher for proxy changes """
  78. SCDynamicStoreSetNotificationKeys(self.store, None, [ 'State:/Network/Global/Proxies' ])
  79. source = SCDynamicStoreCreateRunLoopSource(None, self.store, 0)
  80. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  81. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  82. def dynamicStoreCallback(self, store, keys, info):
  83. """ callback for watcher """
  84. self.updateProxyStatus()
  85. def updateProxyStatus(self):
  86. """ update proxy status """
  87. # load proxy dictionary
  88. proxydict = SCDynamicStoreCopyProxies(None)
  89. # get status for primary interface
  90. status = proxydict['__SCOPED__'][self.interface]
  91. # Are any proxies active now?
  92. anyProxyEnabled = False
  93. # update menu items according to their related proxy state
  94. for proxy in self.proxies.values():
  95. if proxy['menuitem']:
  96. proxy['menuitem'].setState_(status.get(proxy['pref'], False) and NSOnState or NSOffState)
  97. if status.get(proxy['pref'], False):
  98. anyProxyEnabled = True
  99. # set image
  100. self.statusitem.setImage_(anyProxyEnabled and self.active_image or self.inactive_image)
  101. def quitApp_(self, sender):
  102. NSApp.terminate_(self)
  103. def toggleFtpProxy_(self, sender):
  104. self.toggleProxy(self.proxies['ftp']['menuitem'], 'ftpproxy')
  105. def toggleHttpProxy_(self, sender):
  106. self.toggleProxy(self.proxies['http']['menuitem'], 'webproxy')
  107. def toggleHttpsProxy_(self, sender):
  108. self.toggleProxy(self.proxies['https']['menuitem'], 'securewebproxy')
  109. def toggleRtspProxy_(self, sender):
  110. self.toggleProxy(self.proxies['rtsp']['menuitem'], 'streamingproxy')
  111. def toggleSocksProxy_(self, sender):
  112. self.toggleProxy(self.proxies['socks']['menuitem'], 'socksfirewallproxy')
  113. def toggleProxy(self, item, target):
  114. """ callback for clicks on menu item """
  115. servicename = SCNetworkServiceGetName(self.service)
  116. if not servicename:
  117. NSLog("interface '%s' not found in services?" % self.interface)
  118. return
  119. newstate = item.state() == NSOffState and 'on' or 'off'
  120. commands.getoutput("/usr/sbin/networksetup -set%sstate '%s' %s" % (
  121. target,
  122. servicename,
  123. newstate
  124. ))
  125. self.updateProxyStatus()
  126. if __name__ == '__main__':
  127. sharedapp = NSApplication.sharedApplication()
  128. toggler = ToggleProxy.alloc().init()
  129. sharedapp.setDelegate_(toggler)
  130. sharedapp.run()