Mac OS X menu item for quickly enabling/disabling HTTP proxy
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

200 righe
8.6 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. hasProxies = 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. hasProxies = True
  52. else:
  53. proxy['menuitem'] = None
  54. if hasProxies:
  55. self.menu.addItem_(NSMenuItem.separatorItem())
  56. self.enableallmi = self.menu.addItemWithTitle_action_keyEquivalent_('Enable All', 'enableall', '')
  57. self.disableallmi = self.menu.addItemWithTitle_action_keyEquivalent_('Disable All', 'disableall', '')
  58. self.menu.addItem_(NSMenuItem.separatorItem())
  59. # Need a way to quit
  60. self.menu.addItemWithTitle_action_keyEquivalent_("Quit", "quitApp:", "")
  61. # Start working
  62. # self.loadNetworkServices()
  63. self.watchForProxyChanges()
  64. self.updateProxyStatus()
  65. @property
  66. def interface(self):
  67. # get primary interface
  68. return SCDynamicStoreCopyValue(self.store, 'State:/Network/Global/IPv4')['PrimaryInterface']
  69. @property
  70. def service(self):
  71. """ Returns the service relating to self.interface """
  72. prefs = SCPreferencesCreate(kCFAllocatorDefault, 'PRG', None)
  73. # Fetch the list of services
  74. for serviceRef in SCNetworkServiceCopyAll(prefs):
  75. interface = SCNetworkServiceGetInterface(serviceRef)
  76. if self.interface == SCNetworkInterfaceGetBSDName(interface):
  77. return serviceRef
  78. return None
  79. def watchForProxyChanges(self):
  80. """ install a watcher for proxy changes """
  81. SCDynamicStoreSetNotificationKeys(self.store, None, [ 'State:/Network/Global/Proxies' ])
  82. source = SCDynamicStoreCreateRunLoopSource(None, self.store, 0)
  83. loop = NSRunLoop.currentRunLoop().getCFRunLoop()
  84. CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
  85. def dynamicStoreCallback(self, store, keys, info):
  86. """ callback for watcher """
  87. self.updateProxyStatus()
  88. def updateProxyStatus(self):
  89. """ update proxy status """
  90. # load proxy dictionary
  91. proxydict = SCDynamicStoreCopyProxies(None)
  92. # get status for primary interface
  93. status = proxydict['__SCOPED__'][self.interface]
  94. # Are any proxies active now?
  95. anyProxyEnabled = False
  96. # update menu items according to their related proxy state
  97. for proxy in self.proxies.values():
  98. if proxy['menuitem']:
  99. proxy['menuitem'].setState_(status.get(proxy['pref'], False) and NSOnState or NSOffState)
  100. if status.get(proxy['pref'], False):
  101. anyProxyEnabled = True
  102. self.enableallmi.setState_(NSOffState)
  103. # set image
  104. self.statusitem.setImage_(anyProxyEnabled and self.active_image or self.inactive_image)
  105. def quitApp_(self, sender):
  106. NSApp.terminate_(self)
  107. proxymap = {
  108. 'ftp' : 'ftpproxy',
  109. 'http': 'webproxy',
  110. 'https': 'securewebproxy',
  111. 'rtsp': 'streamingproxy',
  112. 'socks': 'socksfirewallproxy',
  113. }
  114. def toggleFtpProxy_(self, sender):
  115. self.toggleProxy(self.proxies['ftp']['menuitem'], 'ftpproxy')
  116. def toggleHttpProxy_(self, sender):
  117. self.toggleProxy(self.proxies['http']['menuitem'], 'webproxy')
  118. def toggleHttpsProxy_(self, sender):
  119. self.toggleProxy(self.proxies['https']['menuitem'], 'securewebproxy')
  120. def toggleRtspProxy_(self, sender):
  121. self.toggleProxy(self.proxies['rtsp']['menuitem'], 'streamingproxy')
  122. def toggleSocksProxy_(self, sender): self.toggleProxy(self.proxies['socks']['menuitem'], 'socksfirewallproxy')
  123. def toggleProxy(self, item, target):
  124. """ callback for clicks on menu item """
  125. servicename = SCNetworkServiceGetName(self.service)
  126. if not servicename:
  127. NSLog("interface '%s' not found in services?" % self.interface)
  128. return
  129. newstate = item.state() == NSOffState and 'on' or 'off'
  130. cmd = "/usr/sbin/networksetup -set%sstate '%s' %s" % (
  131. target,
  132. servicename,
  133. newstate
  134. )
  135. print 'cmd:', `cmd`
  136. commands.getoutput(cmd)
  137. self.updateProxyStatus()
  138. def setall(self, state):
  139. servicename = SCNetworkServiceGetName(self.service)
  140. if not servicename:
  141. NSLog("interface '%s' not found in services?" % self.interface)
  142. return
  143. cmd = [ '/usr/sbin/networksetup' ]
  144. newstate = state and 'on' or 'off'
  145. for i in self.proxies:
  146. if self.proxies[i]['menuitem']:
  147. cmd.append("-set%sstate '%s' %s" % (self.proxymap[i], servicename, newstate))
  148. cmd = ' '.join(cmd)
  149. print 'cmd:', `cmd`
  150. commands.getoutput(' '.join(cmd))
  151. self.updateProxyStatus()
  152. def enableall(self, sender):
  153. self.setall(True)
  154. def disableall(self, sender):
  155. self.setall(False)
  156. if __name__ == '__main__':
  157. sharedapp = NSApplication.sharedApplication()
  158. toggler = ToggleProxy.alloc().init()
  159. sharedapp.setDelegate_(toggler)
  160. sharedapp.run()