summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/urllib/request.py92
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_scproxy.c261
-rw-r--r--setup.py6
4 files changed, 335 insertions, 27 deletions
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
index b9de479..ee819c3 100644
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -2140,44 +2140,82 @@ def proxy_bypass_environment(host):
if sys.platform == 'darwin':
- def getproxies_internetconfig():
- """Return a dictionary of scheme -> proxy server URL mappings.
+ from _scproxy import _get_proxy_settings, _get_proxies
- By convention the mac uses Internet Config to store
- proxies. An HTTP proxy, for instance, is stored under
- the HttpProxy key.
+ 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.
"""
- try:
- import ic
- except ImportError:
- return {}
+ 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.
+
+ This function uses the MacOSX framework SystemConfiguration
+ to fetch the proxy information.
+ """
+ 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():
diff --git a/Misc/NEWS b/Misc/NEWS
index d882898..758a2ff 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
diff --git a/Modules/_scproxy.c b/Modules/_scproxy.c
new file mode 100644
index 0000000..602fbe3
--- /dev/null
+++ b/Modules/_scproxy.c
@@ -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
+
diff --git a/setup.py b/setup.py
index 1d648b5e..846940d 100644
--- a/setup.py
+++ b/setup.py
@@ -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)