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.

99 lines
3.7 KiB

  1. #!/usr/bin/env python
  2. from Foundation import *
  3. from AppKit import *
  4. from SystemConfiguration import *
  5. import commands, re, time
  6. class ToggleProxy(NSObject):
  7. def applicationDidFinishLaunching_(self, notification):
  8. # define some title-related stuff
  9. self.active_color = NSColor.colorWithSRGBRed_green_blue_alpha_(0, 0.5, 0, 1)
  10. self.inactive_color = NSColor.colorWithSRGBRed_green_blue_alpha_(0.6, 0, 0, 1)
  11. self.title_font = NSFont.fontWithName_size_('HelveticaNeue-Bold', 12.0)
  12. # find image files
  13. self.active_image = NSImage.imageNamed_("active")
  14. self.inactive_image = NSImage.imageNamed_("inactive")
  15. # make status bar item
  16. self.statusitem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength)
  17. self.statusitem.retain()
  18. self.statusitem.setAction_("toggleProxy:")
  19. self.statusitem.setTarget_(self)
  20. self.statusitem.setHighlightMode_(False)
  21. self.statusitem.setEnabled_(True)
  22. # start working
  23. self.loadNetworkServices()
  24. self.watchForProxyChanges()
  25. self.updateProxyStatus()
  26. def loadNetworkServices(self):
  27. """ load list of network services, the easy way """
  28. self.services = {}
  29. output = commands.getoutput("/usr/sbin/networksetup listnetworkserviceorder")
  30. for service, device in re.findall(r'Hardware Port:\s*(.*?), Device:\s*(.*?)\)', output):
  31. self.services[device] = service
  32. def watchForProxyChanges(self):
  33. """ install a watcher for proxy changes """
  34. store = SCDynamicStoreCreate(None, "name.klep.toggleproxy", self.proxyStateChanged, None)
  35. SCDynamicStoreSetNotificationKeys(store, None, [ 'State:/Network/Global/Proxies' ])
  36. source = SCDynamicStoreCreateRunLoopSource(None, store, 0)
  37. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  38. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  39. def proxyStateChanged(self, store, keys, info):
  40. """ callback for watcher """
  41. self.updateProxyStatus()
  42. def updateProxyStatus(self):
  43. """ update proxy status """
  44. proxydict = SCDynamicStoreCopyProxies(None)
  45. interface = proxydict['__SCOPED__'].keys()[0]
  46. status = proxydict['__SCOPED__'][interface]
  47. self.active = status.get('HTTPEnable', False) and True or False
  48. self.device = interface
  49. # set image
  50. self.statusitem.setImage_( self.active and self.active_image or self.inactive_image )
  51. # set tooltip
  52. if self.active:
  53. tooltip = "Proxy active on %s:%s" % (
  54. proxydict.get('HTTPProxy', '??'),
  55. proxydict.get('HTTPPort', '??')
  56. )
  57. else:
  58. tooltip = "Proxy is not active"
  59. self.statusitem.setToolTip_(tooltip)
  60. def toggleProxy_(self, sender):
  61. """ callback for clicks on menu item """
  62. event = NSApp.currentEvent()
  63. # Ctrl pressed? if so, quit
  64. if event.modifierFlags() & NSControlKeyMask:
  65. NSApp.terminate_(self)
  66. return
  67. servicename = self.services.get(self.device)
  68. if not servicename:
  69. NSLog("device '%s' not found in services?" % self.device)
  70. return
  71. newstate = self.active and "off" or "on"
  72. commands.getoutput("networksetup setwebproxystate %s %s" % (
  73. servicename,
  74. newstate
  75. ))
  76. self.updateProxyStatus()
  77. if __name__ == '__main__':
  78. sharedapp = NSApplication.sharedApplication()
  79. toggler = ToggleProxy.alloc().init()
  80. sharedapp.setDelegate_(toggler)
  81. sharedapp.run()