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.

122 lines
4.5 KiB

  1. #!/usr/bin/env python
  2. from Foundation import *
  3. from AppKit import *
  4. from SystemConfiguration import *
  5. import commands, re
  6. class ToggleProxy(NSObject):
  7. def applicationDidFinishLaunching_(self, notification):
  8. # find image files
  9. self.active_image = NSImage.imageNamed_("active")
  10. self.inactive_image = NSImage.imageNamed_("inactive")
  11. # make status bar item
  12. self.statusitem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength)
  13. self.statusitem.retain()
  14. # self.statusitem.setTarget_(self)
  15. self.statusitem.setHighlightMode_(False)
  16. self.statusitem.setEnabled_(True)
  17. self.statusitem.setImage_(self.inactive_image)
  18. # insert a menu into the status bar item
  19. self.menu = NSMenu.alloc().init()
  20. self.statusitem.setMenu_(self.menu)
  21. # add items to menu
  22. self.httpMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  23. "HTTP proxy",
  24. "toggleHttpProxy:",
  25. "")
  26. self.httpsMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  27. "HTTPS proxy",
  28. "toggleHttpsProxy:",
  29. "")
  30. self.socksMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  31. "SOCKS proxy",
  32. "toggleSocksProxy:",
  33. "")
  34. self.menu.addItem_(NSMenuItem.separatorItem())
  35. self.menu.addItemWithTitle_action_keyEquivalent_(
  36. "Quit",
  37. "quitApp:",
  38. "")
  39. # open connection to the dynamic (configuration) store
  40. self.store = SCDynamicStoreCreate(None, "name.klep.toggleproxy", self.dynamicStoreCallback, None)
  41. # start working
  42. self.loadNetworkServices()
  43. self.watchForProxyChanges()
  44. self.updateProxyStatus()
  45. @property
  46. def interface(self):
  47. # get primary interface
  48. return SCDynamicStoreCopyValue(self.store, 'State:/Network/Global/IPv4')['PrimaryInterface']
  49. def loadNetworkServices(self):
  50. """ load list of network services """
  51. self.services = {}
  52. output = commands.getoutput("/usr/sbin/networksetup listnetworkserviceorder")
  53. for servicename, service, device in re.findall(r'\(\d\)\s*(.*?)(?:\n|\r\n?)\(Hardware Port:\s*(.*?), Device:\s*(.*?)\)', output, re.MULTILINE):
  54. self.services[device] = servicename
  55. def watchForProxyChanges(self):
  56. """ install a watcher for proxy changes """
  57. SCDynamicStoreSetNotificationKeys(self.store, None, [ 'State:/Network/Global/Proxies' ])
  58. source = SCDynamicStoreCreateRunLoopSource(None, self.store, 0)
  59. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  60. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  61. def dynamicStoreCallback(self, store, keys, info):
  62. """ callback for watcher """
  63. self.updateProxyStatus()
  64. def updateProxyStatus(self):
  65. """ update proxy status """
  66. # load proxy dictionary
  67. proxydict = SCDynamicStoreCopyProxies(None)
  68. # get status for primary interface
  69. status = proxydict['__SCOPED__'][self.interface]
  70. # update menu items according to their related proxy state
  71. self.httpMenuItem.setState_( status.get('HTTPEnable', False) and NSOnState or NSOffState )
  72. self.httpsMenuItem.setState_( status.get('HTTPSEnable', False) and NSOnState or NSOffState )
  73. self.socksMenuItem.setState_( status.get('SOCKSEnable', False) and NSOnState or NSOffState )
  74. def quitApp_(self, sender):
  75. NSApp.terminate_(self)
  76. def toggleHttpProxy_(self, sender):
  77. self.toggleProxy(self.httpMenuItem, 'webproxy')
  78. def toggleHttpsProxy_(self, sender):
  79. self.toggleProxy(self.httpsMenuItem, 'securewebproxy')
  80. def toggleSocksProxy_(self, sender):
  81. self.toggleProxy(self.socksMenuItem, 'socksfirewallproxy')
  82. def toggleProxy(self, item, target):
  83. """ callback for clicks on menu item """
  84. servicename = self.services.get(self.interface)
  85. if not servicename:
  86. NSLog("interface '%s' not found in services?" % self.interface)
  87. return
  88. newstate = item.state() == NSOffState and 'on' or 'off'
  89. commands.getoutput("/usr/sbin/networksetup -set%sstate '%s' %s" % (
  90. target,
  91. servicename,
  92. newstate
  93. ))
  94. self.updateProxyStatus()
  95. if __name__ == '__main__':
  96. sharedapp = NSApplication.sharedApplication()
  97. toggler = ToggleProxy.alloc().init()
  98. sharedapp.setDelegate_(toggler)
  99. sharedapp.run()