For for issue #7154: Port the code that uses

the SystemConfiguration framework to detect the
proxy settings on OSX from the trunk to python 3.2
This commit is contained in:
Ronald Oussoren 2010-04-18 20:46:11 +00:00
parent f88db8de76
commit 8415120af3
4 changed files with 335 additions and 27 deletions

View File

@ -2140,44 +2140,82 @@ def proxy_bypass_environment(host):
if sys.platform == 'darwin':
def getproxies_internetconfig():
from _scproxy import _get_proxy_settings, _get_proxies
def proxy_bypass_macosx_sysconf(host):
"""
Return True iff this host shouldn't be accessed using a proxy
This function uses the MacOSX framework SystemConfiguration
to fetch the proxy information.
"""
import re
import socket
from fnmatch import fnmatch
hostonly, port = splitport(host)
def ip2num(ipAddr):
parts = ipAddr.split('.')
parts = map(int, parts)
if len(parts) != 4:
parts = (parts + [0, 0, 0, 0])[:4]
return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]
proxy_settings = _get_proxy_settings()
# Check for simple host names:
if '.' not in host:
if proxy_settings['exclude_simple']:
return True
hostIP = None
for value in proxy_settings.get('exceptions', ()):
# Items in the list are strings like these: *.local, 169.254/16
if not value: continue
m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value)
if m is not None:
if hostIP is None:
try:
hostIP = socket.gethostbyname(hostonly)
hostIP = ip2num(hostIP)
except socket.error:
continue
base = ip2num(m.group(1))
mask = int(m.group(2)[1:])
mask = 32 - mask
if (hostIP >> mask) == (base >> mask):
return True
elif fnmatch(host, value):
return True
return False
def getproxies_macosx_sysconf():
"""Return a dictionary of scheme -> proxy server URL mappings.
By convention the mac uses Internet Config to store
proxies. An HTTP proxy, for instance, is stored under
the HttpProxy key.
This function uses the MacOSX framework SystemConfiguration
to fetch the proxy information.
"""
try:
import ic
except ImportError:
return {}
return _get_proxies()
try:
config = ic.IC()
except ic.error:
return {}
proxies = {}
# HTTP:
if 'UseHTTPProxy' in config and config['UseHTTPProxy']:
try:
value = config['HTTPProxyHost']
except ic.error:
pass
else:
proxies['http'] = 'http://%s' % value
# FTP: XXX To be done.
# Gopher: XXX To be done.
return proxies
def proxy_bypass(host):
if getproxies_environment():
return proxy_bypass_environment(host)
else:
return 0
return proxy_bypass_macosx_sysconf(host)
def getproxies():
return getproxies_environment() or getproxies_internetconfig()
return getproxies_environment() or getproxies_macosx_sysconf()
elif os.name == 'nt':
def getproxies_registry():

View File

@ -318,6 +318,9 @@ C-API
Library
-------
- Issue #7154: urllib.request can now detect the proxy settings on OSX 10.6
(as long as the user didn't specify 'automatic proxy configuration').
- Issue #3817: ftplib.FTP.abort() method now considers 225 a valid response
code as stated in RFC-959 at chapter 5.4.

261
Modules/_scproxy.c Normal file
View File

@ -0,0 +1,261 @@
/*
* Helper method for urllib to fetch the proxy configuration settings
* using the SystemConfiguration framework.
*/
#include <Python.h>
#include <SystemConfiguration/SystemConfiguration.h>
static int32_t
cfnum_to_int32(CFNumberRef num)
{
int32_t result;
CFNumberGetValue(num, kCFNumberSInt32Type, &result);
return result;
}
static PyObject*
cfstring_to_pystring(CFStringRef ref)
{
const char* s;
s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
if (s) {
return PyUnicode_DecodeUTF8(
s, strlen(s), NULL);
} else {
CFIndex len = CFStringGetLength(ref);
Boolean ok;
PyObject* result;
char* buf;
buf = PyMem_Malloc(len*4);
if (buf == NULL) {
PyErr_NoMemory();
return NULL;
}
ok = CFStringGetCString(ref,
buf, len * 4,
kCFStringEncodingUTF8);
if (!ok) {
PyMem_Free(buf);
return NULL;
} else {
result = PyUnicode_DecodeUTF8(
buf, strlen(buf), NULL);
PyMem_Free(buf);
}
return result;
}
}
static PyObject*
get_proxy_settings(PyObject* mod __attribute__((__unused__)))
{
CFDictionaryRef proxyDict = NULL;
CFNumberRef aNum = NULL;
CFArrayRef anArray = NULL;
PyObject* result = NULL;
PyObject* v;
int r;
proxyDict = SCDynamicStoreCopyProxies(NULL);
if (!proxyDict) {
Py_INCREF(Py_None);
return Py_None;
}
result = PyDict_New();
if (result == NULL) goto error;
if (&kSCPropNetProxiesExcludeSimpleHostnames != NULL) {
aNum = CFDictionaryGetValue(proxyDict,
kSCPropNetProxiesExcludeSimpleHostnames);
if (aNum == NULL) {
v = PyBool_FromLong(1);
} else {
v = PyBool_FromLong(cfnum_to_int32(aNum));
}
} else {
v = PyBool_FromLong(1);
}
if (v == NULL) goto error;
r = PyDict_SetItemString(result, "exclude_simple", v);
Py_DECREF(v); v = NULL;
if (r == -1) goto error;
anArray = CFDictionaryGetValue(proxyDict,
kSCPropNetProxiesExceptionsList);
if (anArray != NULL) {
CFIndex len = CFArrayGetCount(anArray);
CFIndex i;
v = PyTuple_New(len);
if (v == NULL) goto error;
r = PyDict_SetItemString(result, "exceptions", v);
Py_DECREF(v);
if (r == -1) goto error;
for (i = 0; i < len; i++) {
CFStringRef aString = NULL;
aString = CFArrayGetValueAtIndex(anArray, i);
if (aString == NULL) {
PyTuple_SetItem(v, i, Py_None);
Py_INCREF(Py_None);
} else {
PyObject* t = cfstring_to_pystring(aString);
if (!t) {
PyTuple_SetItem(v, i, Py_None);
Py_INCREF(Py_None);
} else {
PyTuple_SetItem(v, i, t);
}
}
}
}
CFRelease(proxyDict);
return result;
error:
if (proxyDict) CFRelease(proxyDict);
Py_XDECREF(result);
return NULL;
}
static int
set_proxy(PyObject* proxies, char* proto, CFDictionaryRef proxyDict,
CFStringRef enabledKey,
CFStringRef hostKey, CFStringRef portKey)
{
CFNumberRef aNum;
aNum = CFDictionaryGetValue(proxyDict, enabledKey);
if (aNum && cfnum_to_int32(aNum)) {
CFStringRef hostString;
hostString = CFDictionaryGetValue(proxyDict, hostKey);
aNum = CFDictionaryGetValue(proxyDict, portKey);
if (hostString) {
int r;
PyObject* h = cfstring_to_pystring(hostString);
PyObject* v;
if (h) {
if (aNum) {
int32_t port = cfnum_to_int32(aNum);
v = PyUnicode_FromFormat("http://%U:%ld",
h, (long)port);
} else {
v = PyUnicode_FromFormat("http://%U", h);
}
Py_DECREF(h);
if (!v) return -1;
r = PyDict_SetItemString(proxies, proto,
v);
Py_DECREF(v);
return r;
}
}
}
return 0;
}
static PyObject*
get_proxies(PyObject* mod __attribute__((__unused__)))
{
PyObject* result = NULL;
int r;
CFDictionaryRef proxyDict = NULL;
proxyDict = SCDynamicStoreCopyProxies(NULL);
if (proxyDict == NULL) {
return PyDict_New();
}
result = PyDict_New();
if (result == NULL) goto error;
r = set_proxy(result, "http", proxyDict,
kSCPropNetProxiesHTTPEnable,
kSCPropNetProxiesHTTPProxy,
kSCPropNetProxiesHTTPPort);
if (r == -1) goto error;
r = set_proxy(result, "https", proxyDict,
kSCPropNetProxiesHTTPSEnable,
kSCPropNetProxiesHTTPSProxy,
kSCPropNetProxiesHTTPSPort);
if (r == -1) goto error;
r = set_proxy(result, "ftp", proxyDict,
kSCPropNetProxiesFTPEnable,
kSCPropNetProxiesFTPProxy,
kSCPropNetProxiesFTPPort);
if (r == -1) goto error;
r = set_proxy(result, "gopher", proxyDict,
kSCPropNetProxiesGopherEnable,
kSCPropNetProxiesGopherProxy,
kSCPropNetProxiesGopherPort);
if (r == -1) goto error;
CFRelease(proxyDict);
return result;
error:
if (proxyDict) CFRelease(proxyDict);
Py_XDECREF(result);
return NULL;
}
static PyMethodDef mod_methods[] = {
{
"_get_proxy_settings",
(PyCFunction)get_proxy_settings,
METH_NOARGS,
NULL,
},
{
"_get_proxies",
(PyCFunction)get_proxies,
METH_NOARGS,
NULL,
},
{ 0, 0, 0, 0 }
};
static struct PyModuleDef mod_module = {
PyModuleDef_HEAD_INIT,
"_scproxy",
NULL,
-1,
mod_methods,
NULL,
NULL,
NULL,
NULL
};
#ifdef __cplusplus
extern "C" {
#endif
PyObject*
PyInit__scproxy(void)
{
return PyModule_Create(&mod_module);
}
#ifdef __cplusplus
}
#endif

View File

@ -1238,6 +1238,12 @@ class PyBuildExt(build_ext):
Extension('_gestalt', ['_gestalt.c'],
extra_link_args=['-framework', 'Carbon'])
)
exts.append(
Extension('_scproxy', ['_scproxy.c'],
extra_link_args=[
'-framework', 'SystemConfiguration',
'-framework', 'CoreFoundation',
]))
self.extensions.extend(exts)