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.

107 lines
3.9 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. # open connection to the dynamic (configuration) store
  23. self.store = SCDynamicStoreCreate(None, "name.klep.toggleproxy", self.dynamicStoreCallback, None)
  24. # start working
  25. self.loadNetworkServices()
  26. self.watchForProxyChanges()
  27. self.updateProxyStatus()
  28. @property
  29. def interface(self):
  30. # get primary interface
  31. return SCDynamicStoreCopyValue(self.store, 'State:/Network/Global/IPv4')['PrimaryInterface']
  32. def loadNetworkServices(self):
  33. """ load list of network services """
  34. self.services = {}
  35. for key, dictionary in SCDynamicStoreCopyMultiple(self.store, None, [ 'Setup:/Network/Service/.*/Interface' ]).items():
  36. self.services[dictionary['DeviceName']] = dictionary['UserDefinedName']
  37. def watchForProxyChanges(self):
  38. """ install a watcher for proxy changes """
  39. SCDynamicStoreSetNotificationKeys(self.store, None, [ 'State:/Network/Global/Proxies' ])
  40. source = SCDynamicStoreCreateRunLoopSource(None, self.store, 0)
  41. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  42. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  43. def dynamicStoreCallback(self, store, keys, info):
  44. """ callback for watcher """
  45. self.updateProxyStatus()
  46. def updateProxyStatus(self):
  47. """ update proxy status """
  48. # load proxy dictionary
  49. proxydict = SCDynamicStoreCopyProxies(None)
  50. # get status for primary interface
  51. status = proxydict['__SCOPED__'][self.interface]
  52. self.active = status.get('HTTPEnable', False) and True or False
  53. # set image
  54. self.statusitem.setImage_( self.active and self.active_image or self.inactive_image )
  55. # set tooltip
  56. if self.active:
  57. tooltip = "[%s] proxy active on %s:%s" % (
  58. self.interface,
  59. proxydict.get('HTTPProxy', '??'),
  60. proxydict.get('HTTPPort', '??'),
  61. )
  62. else:
  63. tooltip = "[%s] proxy not active" % self.interface
  64. self.statusitem.setToolTip_(tooltip)
  65. def toggleProxy_(self, sender):
  66. """ callback for clicks on menu item """
  67. event = NSApp.currentEvent()
  68. # Ctrl pressed? if so, quit
  69. if event.modifierFlags() & NSControlKeyMask:
  70. NSApp.terminate_(self)
  71. return
  72. servicename = self.services.get(self.interface)
  73. if not servicename:
  74. NSLog("interface '%s' not found in services?" % self.interface)
  75. return
  76. newstate = self.active and "off" or "on"
  77. commands.getoutput("networksetup setwebproxystate %s %s" % (
  78. servicename,
  79. newstate
  80. ))
  81. self.updateProxyStatus()
  82. if __name__ == '__main__':
  83. sharedapp = NSApplication.sharedApplication()
  84. toggler = ToggleProxy.alloc().init()
  85. sharedapp.setDelegate_(toggler)
  86. sharedapp.run()