# (c) Copyright 2010-2014. CodeWeavers, Inc.
import os
import traceback
import urllib2
import sys
import cxlog
import cxproduct
import cxutils
import distversion
#####
#
# Desktop environment detection
#
#####
# Cache the desktop environment because determining it can be slow and it's
# not supposed to change at runtime.
_DESKTOP_ENVIRONMENT = None
def get_desktop_environment():
"""Returns the name of the currently running desktop environment."""
# pylint: disable=W0603
global _DESKTOP_ENVIRONMENT
if _DESKTOP_ENVIRONMENT:
return _DESKTOP_ENVIRONMENT
_DESKTOP_ENVIRONMENT = 'unknown'
if cxutils.CX_ROOT is not None and \
cxproduct.get_config()['CrossOver'].get('ManualProxySettings', '0') == '1':
_DESKTOP_ENVIRONMENT = 'config'
elif distversion.IS_MACOSX:
_DESKTOP_ENVIRONMENT = 'macosx'
elif 'GNOME_DESKTOP_SESSION_ID' in os.environ:
_DESKTOP_ENVIRONMENT = 'gnome'
elif 'KDE_FULL_SESSION' in os.environ:
_DESKTOP_ENVIRONMENT = 'kde'
else:
try:
# pylint: disable=F0401
import dbus
bus = dbus.SystemBus()
dbus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
dbus_interface = dbus.Interface(dbus_obj, 'org.freedesktop.DBus')
except Exception, exception: # pylint: disable=W0703
cxlog.log("could not connect to D-Bus: %s\n" % str(exception))
else:
# Do it the slow way
session_managers = (
('org.gnome.SessionManager', 'gnome'),
('org.kde.ksmserver', 'kde'),
('org.xfce.SessionManager', 'xfce'),
)
for dbus_name, desktop_environment in session_managers:
if dbus_interface.NameHasOwner(dbus_name):
_DESKTOP_ENVIRONMENT = desktop_environment
break
return _DESKTOP_ENVIRONMENT
#####
#
# libproxy support
#
#####
# As of version 0.3, libproxy is unfortunately too buggy to be usable
_USE_LIBPROXY = False
_LIBPROXY_FACTORY = None
def get_libproxy_factory():
"""Detects whether libproxy is available and sets it up."""
# pylint: disable=W0603
global _LIBPROXY_FACTORY
if _USE_LIBPROXY and _LIBPROXY_FACTORY is None:
try:
# pylint: disable=F0401
import libproxy
_LIBPROXY_FACTORY = libproxy.ProxyFactory()
except Exception, exception: # pylint: disable=W0703
cxlog.log("could not load libproxy: %s\n" % str(exception))
return _LIBPROXY_FACTORY
def has_libproxy():
"""Returns True if libproxy support is available."""
if get_libproxy_factory():
return True
return False
def get_proxy_libproxy(req):
"""Returns the default proxy to use for the specified URL."""
factory = get_libproxy_factory()
if factory:
proxy = factory.getProxies(req.get_full_url())[0]
if proxy != 'direct://':
return proxy
return ''
#####
#
# Basic GNOME support
#
#####
def get_proxy_gconf(req):
"""Detects GNOME's proxy settings using GConf."""
# pylint: disable=F0401
try:
import gconf
except ImportError:
return ''
client = gconf.client_get_default()
scheme = req.get_type()
if scheme == 'https':
scheme = 'secure' # gconf calls https "secure"
if scheme == 'http':
if not client.get_bool('/system/http_proxy/use_http_proxy'):
return ''
host = client.get_string('/system/http_proxy/host')
port = client.get_int('/system/http_proxy/port')
else:
if client.get_string('/system/proxy/mode') == 'none':
return ''
host = client.get_string('/system/proxy/%s_host' % scheme)
port = client.get_int('/system/proxy/%s_port' % scheme)
if not host or not port:
return ''
if client.get_bool('/system/http_proxy/use_authentication'):
# Not sure if we should do this unconditionally, but gconf provides
# no other authentication info
auth_user = client.get_string('/system/http_proxy/authentication_user')
auth_pass = client.get_string('/system/http_proxy/authentication_password')
auth_info = '%s:%s@' % (auth_user, auth_pass)
else:
auth_info = ''
return '%s%s:%s' % (auth_info, host, port)
def get_proxy_gsettings(req):
"""Detects GNOME's proxy settings using gsettings."""
which_cmd = ["which", "gsettings"]
retcode, out, _err = cxutils.run(which_cmd, stdout=cxutils.GRAB)
if retcode == 0:
gsettings_path = out.rstrip()
else:
return ''
schema = "org.gnome.system.proxy"
gsettings_cmd = [gsettings_path, "get", schema, "mode"]
retcode, out, _err = cxutils.run(gsettings_cmd, stdout=cxutils.GRAB)
if retcode != 0 or out.rstrip().replace("'", "") == "none":
return ''
scheme = req.get_type()
schema = "org.gnome.system.proxy." + scheme
gsettings_cmd = [gsettings_path, "get", schema, "host"]
retcode, out, _err = cxutils.run(gsettings_cmd, stdout=cxutils.GRAB)
host = ""
if retcode == 0:
host = out.rstrip().replace("'", "")
if host == "":
return ''
gsettings_cmd = [gsettings_path, "get", schema, "port"]
retcode, out, _err = cxutils.run(gsettings_cmd, stdout=cxutils.GRAB)
if retcode == 0:
port = out.rstrip()
else:
return ''
return '%s:%s' % (host, port)
#####
#
# Basic KDE 4 support
#
#####
_HAS_KDE = None
def _init_kde():
"""Initializes the KDE environment so we can query its proxy settings."""
# pylint: disable=W0603
global _HAS_KDE
if _HAS_KDE is None:
try:
# pylint: disable=F0401
import PyQt4.Qt
# pylint: disable=W0612
import PyKDE4.kdecore
# QApplication must be instantiated before we can use KConfig.
# Also it must be created on startup otherwise it will cause a
# crash (maybe because of a conflict with GTK+).
PyQt4.Qt.QApplication([])
_HAS_KDE = True
except Exception, exception: # pylint: disable=W0703
cxlog.log("could not initialize the KDE 4 libraries: %s\n" % str(exception))
_HAS_KDE = False
def get_proxy_kde_int(config, name):
"""Helper for get_proxy_kde(). Retrieves the value of an integer KDE
property.
"""
value = config.readEntry(name)
if value == '':
return 0
return int(value)
def get_proxy_kde(req):
"""Detects KDE's proxy settings using PyQT4 and PyKDE4."""
if _HAS_KDE:
# pylint: disable=F0401
import PyKDE4.kdecore
# The config object must be recreated each time otherwise we won't
# notice changes in the proxy settings
config = PyKDE4.kdecore.KConfig('kioslaverc')
proxy_settings = config.group('Proxy Settings')
proxy_type = get_proxy_kde_int(proxy_settings, 'ProxyType')
if proxy_type == 0:
# no proxy
return ''
if proxy_type != 1:
cxlog.warn("Cannot handle KDE proxy of type %d; using environment instead of KDE settings" % proxy_type)
return ''
if proxy_settings.readEntry('NoProxyFor'):
if proxy_settings.readEntry('ReversedException') == 'false':
# Expect the exception to only impact a few hosts we are not
# going to access anyway, typically hosts on the LAN.
cxlog.warn("Cannot handle proxy exceptions; ignoring them")
else:
cxlog.warn("Cannot handle proxy exceptions; using environment instead of KDE settings")
return ''
if get_proxy_kde_int(proxy_settings, 'AuthMode'):
cxlog.warn("Don't know how to read KDE proxy authorization; using environment instead of KDE settings")
return ''
proxy_url = str(proxy_settings.readEntry(req.get_type() + 'Proxy'))
proxy_url = proxy_url.replace(' ', ':') # string value separates host and port with space
if not proxy_url.startswith('//:'):
return proxy_url
return ''
#####
#
# Support the environment variables as a fallback
#
#####
def get_proxy_env(req):
"""Detects the environment's proxy settings."""
return os.environ.get(req.get_type() + '_proxy', '')
def get_proxy_conf(req):
"""Reads proxy settings from crossover.conf."""
global_config = cxproduct.get_config()
return global_config['CrossOver'].get(req.get_type() + 'Proxy', '')
#####
#
# DynamicProxyHandler
#
#####
class DynamicProxyHandler(urllib2.ProxyHandler):
"""A proxy handler that dynamically detects the proxy settings each time
it is queried so it can react to proxy configuration changes.
"""
# Proxies must be in front
handler_order = 100
_PROXY_DETECTORS = {
'gnome': (get_proxy_gconf, get_proxy_gsettings, get_proxy_env),
'kde': (get_proxy_kde, get_proxy_env),
'xfce': (get_proxy_gconf, get_proxy_env),
# The Mac GUI avoids urllib2, so this doesn't matter.
'macosx': (get_proxy_env, ),
'unknown': (get_proxy_env, ),
'config': (get_proxy_conf, ),
}
def __init__(self):
urllib2.ProxyHandler.__init__(self, {})
if has_libproxy():
# libproxy supports all the desktop environments we support, only
# better. So we don't want to fall back to our code and pick up
# potentially incorrect settings (e.g. environment variables).
self._detectors = (get_proxy_libproxy, )
else:
# The desktop environment is not supposed to change at runtime.
self._detectors = self._PROXY_DETECTORS[get_desktop_environment()]
def get_proxy(self, req):
for get_proxy in self._detectors:
try:
url = get_proxy(req)
if url:
if '://' not in url and \
not (sys.version_info >= (2, 6) or \
hasattr(urllib2, '_parse_proxy')):
# Some old versions of python can't cope with a proxy
# URL without a schema
url = 'http://' + url
#
# NOTE! It would be a possible security threat to
# log the actual proxy settings here b/c, for some
# settings, a username/password combo might be included.
# So, don't do that.
#
cxlog.log("Proxy settings detected. Network communication will be attempted via proxy.\n")
return url
except: # pylint: disable=W0702
cxlog.warn("Could not get the proxy info:\n%s" % traceback.format_exc())
return None
def do_open(self, req):
proxy = self.get_proxy(req)
if proxy:
return self.proxy_open(req, proxy, req.get_type())
return None
def ftp_open(self, req):
return self.do_open(req)
def http_open(self, req):
return self.do_open(req)
def https_open(self, req):
return self.do_open(req)
def socks_open(self, req):
return self.do_open(req)
def install_default_opener():
"""Sets up a default opener that auto-detects the current proxy settings."""
proxy_handler = DynamicProxyHandler()
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
def _init():
"""Performs the initialization tasks that must be performed at startup."""
if not has_libproxy() and get_desktop_environment() == 'kde':
# Initializing KDE can be quite slow so only do it if needed
_init_kde()
if __name__ == '__main__':
# set CX_ROOT so reading crossover.conf will work when run stand-alone
cxutils.CX_ROOT = os.path.join(os.path.dirname(sys.argv[0]), '../..')
_init()
#####
#
# Test code
#
#####
def main():
print 'Desktop environment: ' + get_desktop_environment()
dynproxy = DynamicProxyHandler()
print "Hit Enter to refresh, Ctrl-D to quit"
while sys.stdin.readline():
for scheme in ('ftp', 'http', 'https', 'socks'):
print '%s -> [%s]' % (scheme, dynproxy.get_proxy(urllib2.Request(scheme + '://codeweavers.com')))
if __name__ == '__main__':
main()