Source code for pypac.os_settings

"""
Tools for getting the configured PAC file URL out of the OS settings.
"""

import platform
from sys import version_info

if version_info[0] == 2:
    from urllib import unquote  # type: ignore

    from urlparse import urlparse  # type: ignore
else:
    from urllib.parse import urlparse, unquote  # noqa


#: True if running on Windows.
ON_WINDOWS = platform.system() == "Windows"

#: True if running on macOS/OSX.
ON_DARWIN = platform.system() == "Darwin"

if ON_WINDOWS:
    try:
        import winreg
    except ImportError:  # PY2.
        import _winreg as winreg  # type: ignore

if ON_DARWIN:
    import SystemConfiguration  # type: ignore


_POLICIES_INTERNET_SETTINGS_PATH = "Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
_NORMAL_INTERNET_SETTINGS_PATH = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"


def _is_per_user_proxy_setting():
    """
    Get the PAC ``ProxySettingsPerUser`` value from the Windows Registry.

    See https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-ie-clientnetworkprotocolimplementation-hklmproxyenable.

    :return: True if settings are per-user (default), False if per-machine.
    :rtype: bool
    """
    try:
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _POLICIES_INTERNET_SETTINGS_PATH) as key:
            value, _ = winreg.QueryValueEx(key, "ProxySettingsPerUser")
            return value != 0
    except WindowsError:
        return True


[docs] def autoconfig_url_from_registry(): """ Get the PAC ``AutoConfigURL`` value from the Windows Registry. This setting is visible as the "use automatic configuration script" field in Internet Options > Connection > LAN Settings. The search order is: 1. HKCU Policies (per-user Group Policy) 2. HKLM Policies (machine Group Policy) 3. HKCU Normal (user preference) 4. HKLM Normal (machine default) If ``ProxySettingsPerUser`` is 0 (per-machine mode), HKCU entries are skipped. :return: The value from the registry, or None if the value isn't configured or available. Note that it may be a local filesystem path instead of a URL. :rtype: str|None :raises NotWindowsError: If called on a non-Windows platform. """ if not ON_WINDOWS: raise NotWindowsError() per_user = _is_per_user_proxy_setting() for hive, path in [ (winreg.HKEY_CURRENT_USER, _POLICIES_INTERNET_SETTINGS_PATH), (winreg.HKEY_LOCAL_MACHINE, _POLICIES_INTERNET_SETTINGS_PATH), (winreg.HKEY_CURRENT_USER, _NORMAL_INTERNET_SETTINGS_PATH), (winreg.HKEY_LOCAL_MACHINE, _NORMAL_INTERNET_SETTINGS_PATH), ]: if not per_user and hive == winreg.HKEY_CURRENT_USER: continue try: with winreg.OpenKey(hive, path) as key: value = winreg.QueryValueEx(key, "AutoConfigURL")[0] if not value: continue return value except WindowsError: pass return None
[docs] def autoconfig_url_from_preferences(): """ Get the PAC ``AutoConfigURL`` value from the macOS System Preferences. This setting is visible as the "URL" field in System Preferences > Network > Advanced... > Proxies > Automatic Proxy Configuration. :return: The value from the registry, or None if the value isn't configured or available. Note that it may be local filesystem path instead of a URL. :rtype: str|None :raises NotDarwinError: If called on a non-macOS/OSX platform. """ if not ON_DARWIN: raise NotDarwinError() try: config = SystemConfiguration.SCDynamicStoreCopyProxies(None) except AttributeError: return # Key or value not found. if all( ( "ProxyAutoConfigEnable" in config, "ProxyAutoConfigURLString" in config, not config.get("ProxyAutoDiscoveryEnable", 0), ) ): # Only return a value if it is enabled, not empty, and auto discovery is disabled. return str(config["ProxyAutoConfigURLString"])
[docs] def file_url_to_local_path(file_url): """ Parse a ``AutoConfigURL`` value with ``file://`` scheme into a usable local filesystem path. :param file_url: Must start with ``file://``. :return: A local filesystem path. It might not exist. """ parts = urlparse(file_url) path = unquote(parts.path) if path.startswith("/") and not path.startswith("//"): if ON_DARWIN: return path if len(parts.netloc) == 2 and parts.netloc[1] == ":": # Drive letter and colon. return parts.netloc + path return "C:" + path # Assume C drive if it's just a path starting with /. if len(path) > 2 and path[1] == ":": return path
[docs] class NotWindowsError(Exception): def __init__(self): super(NotWindowsError, self).__init__("Platform is not Windows.")
[docs] class NotDarwinError(Exception): def __init__(self): super(NotDarwinError, self).__init__("Platform is not macOS/OSX.")