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.

145 lines
5.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. # load icon files
  9. self.icons = {
  10. '0-0-0' : NSImage.imageNamed_("icon-0-0-0"),
  11. '1-0-0' : NSImage.imageNamed_("icon-1-0-0"),
  12. '0-1-0' : NSImage.imageNamed_("icon-0-1-0"),
  13. '0-0-1' : NSImage.imageNamed_("icon-0-0-1"),
  14. '1-1-0' : NSImage.imageNamed_("icon-1-1-0"),
  15. '1-0-1' : NSImage.imageNamed_("icon-1-0-1"),
  16. '1-1-1' : NSImage.imageNamed_("icon-1-1-1")
  17. }
  18. # make status bar item
  19. self.statusitem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength)
  20. self.statusitem.retain()
  21. self.statusitem.setHighlightMode_(False)
  22. self.statusitem.setEnabled_(True)
  23. self.statusitem.setImage_(self.icons['0-0-0'])
  24. # insert a menu into the status bar item
  25. self.menu = NSMenu.alloc().init()
  26. self.statusitem.setMenu_(self.menu)
  27. # add items to menu
  28. self.httpMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  29. "HTTP proxy",
  30. "toggleHttpProxy:",
  31. "")
  32. self.httpsMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  33. "HTTPS proxy",
  34. "toggleHttpsProxy:",
  35. "")
  36. self.socksMenuItem = self.menu.addItemWithTitle_action_keyEquivalent_(
  37. "SOCKS proxy",
  38. "toggleSocksProxy:",
  39. "")
  40. self.menu.addItem_(NSMenuItem.separatorItem())
  41. self.menu.addItemWithTitle_action_keyEquivalent_(
  42. "Quit",
  43. "quitApp:",
  44. "")
  45. # open connection to the dynamic (configuration) store
  46. self.store = SCDynamicStoreCreate(None, "name.klep.toggleproxy", self.dynamicStoreCallback, None)
  47. # start working
  48. self.loadNetworkServices()
  49. self.watchForProxyChanges()
  50. self.updateProxyStatus()
  51. @property
  52. def interface(self):
  53. # get primary interface
  54. return SCDynamicStoreCopyValue(self.store, 'State:/Network/Global/IPv4')['PrimaryInterface']
  55. def loadNetworkServices(self):
  56. """ Load and store a list of network services indexed by their BSDName """
  57. self.services = {}
  58. # Create a dummy preference reference
  59. prefs = SCPreferencesCreate(kCFAllocatorDefault, 'PRG', None)
  60. # Fetch the list of services
  61. for service in SCNetworkServiceCopyAll(prefs):
  62. # This is what we're after, the user-defined service name e.g., "Built-in Ethernet"
  63. name = SCNetworkServiceGetName(service)
  64. # Interface reference
  65. interface = SCNetworkServiceGetInterface(service)
  66. # The BSDName of the interface, E.g., "en1", this will be the index of our list
  67. bsdname = SCNetworkInterfaceGetBSDName(interface)
  68. self.services[bsdname] = name
  69. def watchForProxyChanges(self):
  70. """ install a watcher for proxy changes """
  71. SCDynamicStoreSetNotificationKeys(self.store, None, [ 'State:/Network/Global/Proxies' ])
  72. source = SCDynamicStoreCreateRunLoopSource(None, self.store, 0)
  73. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  74. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  75. def dynamicStoreCallback(self, store, keys, info):
  76. """ callback for watcher """
  77. self.updateProxyStatus()
  78. def updateProxyStatus(self):
  79. """ update proxy status """
  80. # load proxy dictionary
  81. proxydict = SCDynamicStoreCopyProxies(None)
  82. # get status for primary interface
  83. status = proxydict['__SCOPED__'][self.interface]
  84. # update menu items according to their related proxy state
  85. self.httpMenuItem.setState_( status.get('HTTPEnable', False) and NSOnState or NSOffState )
  86. self.httpsMenuItem.setState_( status.get('HTTPSEnable', False) and NSOnState or NSOffState )
  87. self.socksMenuItem.setState_( status.get('SOCKSEnable', False) and NSOnState or NSOffState )
  88. # update icon
  89. self.statusitem.setImage_(
  90. self.icons['%d-%d-%d' % (
  91. status.get('HTTPEnable', False) and 1 or 0,
  92. status.get('HTTPSEnable', False) and 1 or 0,
  93. status.get('SOCKSEnable', False) and 1 or 0
  94. )]
  95. )
  96. def quitApp_(self, sender):
  97. NSApp.terminate_(self)
  98. def toggleHttpProxy_(self, sender):
  99. self.toggleProxy(self.httpMenuItem, 'webproxy')
  100. def toggleHttpsProxy_(self, sender):
  101. self.toggleProxy(self.httpsMenuItem, 'securewebproxy')
  102. def toggleSocksProxy_(self, sender):
  103. self.toggleProxy(self.socksMenuItem, 'socksfirewallproxy')
  104. def toggleProxy(self, item, target):
  105. """ callback for clicks on menu item """
  106. servicename = self.services.get(self.interface)
  107. if not servicename:
  108. NSLog("interface '%s' not found in services?" % self.interface)
  109. return
  110. newstate = item.state() == NSOffState and 'on' or 'off'
  111. commands.getoutput("/usr/sbin/networksetup -set%sstate '%s' %s" % (
  112. target,
  113. servicename,
  114. newstate
  115. ))
  116. self.updateProxyStatus()
  117. if __name__ == '__main__':
  118. sharedapp = NSApplication.sharedApplication()
  119. toggler = ToggleProxy.alloc().init()
  120. sharedapp.setDelegate_(toggler)
  121. sharedapp.run()