xref: /third_party/python/Modules/_scproxy.c (revision 7db96d56)
1/*
2 * Helper method for urllib to fetch the proxy configuration settings
3 * using the SystemConfiguration framework.
4 */
5#include <Python.h>
6#include <SystemConfiguration/SystemConfiguration.h>
7
8static int32_t
9cfnum_to_int32(CFNumberRef num)
10{
11    int32_t result;
12
13    CFNumberGetValue(num, kCFNumberSInt32Type, &result);
14    return result;
15}
16
17static PyObject*
18cfstring_to_pystring(CFStringRef ref)
19{
20    const char* s;
21
22    s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
23    if (s) {
24        return PyUnicode_DecodeUTF8(
25                        s, strlen(s), NULL);
26
27    } else {
28        CFIndex len = CFStringGetLength(ref);
29        Boolean ok;
30        PyObject* result;
31        char* buf;
32
33        buf = PyMem_Malloc(len*4);
34        if (buf == NULL) {
35            PyErr_NoMemory();
36            return NULL;
37        }
38
39        ok = CFStringGetCString(ref,
40                        buf, len * 4,
41                        kCFStringEncodingUTF8);
42        if (!ok) {
43            PyMem_Free(buf);
44            return NULL;
45        } else {
46            result = PyUnicode_DecodeUTF8(
47                            buf, strlen(buf), NULL);
48            PyMem_Free(buf);
49        }
50        return result;
51    }
52}
53
54
55static PyObject*
56get_proxy_settings(PyObject* Py_UNUSED(mod), PyObject *Py_UNUSED(ignored))
57{
58    CFDictionaryRef proxyDict = NULL;
59    CFNumberRef aNum = NULL;
60    CFArrayRef anArray = NULL;
61    PyObject* result = NULL;
62    PyObject* v;
63    int r;
64
65    Py_BEGIN_ALLOW_THREADS
66    proxyDict = SCDynamicStoreCopyProxies(NULL);
67    Py_END_ALLOW_THREADS
68
69    if (!proxyDict) {
70        Py_RETURN_NONE;
71    }
72
73    result = PyDict_New();
74    if (result == NULL) goto error;
75
76    aNum = CFDictionaryGetValue(proxyDict,
77        kSCPropNetProxiesExcludeSimpleHostnames);
78    if (aNum == NULL) {
79        v = PyBool_FromLong(0);
80    } else {
81        v = PyBool_FromLong(cfnum_to_int32(aNum));
82    }
83
84    if (v == NULL) goto error;
85
86    r = PyDict_SetItemString(result, "exclude_simple", v);
87    Py_DECREF(v); v = NULL;
88    if (r == -1) goto error;
89
90    anArray = CFDictionaryGetValue(proxyDict,
91                    kSCPropNetProxiesExceptionsList);
92    if (anArray != NULL) {
93        CFIndex len = CFArrayGetCount(anArray);
94        CFIndex i;
95        v = PyTuple_New(len);
96        if (v == NULL) goto error;
97
98        r = PyDict_SetItemString(result, "exceptions", v);
99        Py_DECREF(v);
100        if (r == -1) goto error;
101
102        for (i = 0; i < len; i++) {
103            CFStringRef aString = NULL;
104
105            aString = CFArrayGetValueAtIndex(anArray, i);
106            if (aString == NULL) {
107                PyTuple_SetItem(v, i, Py_None);
108                Py_INCREF(Py_None);
109            } else {
110                PyObject* t = cfstring_to_pystring(aString);
111                if (!t) {
112                    PyTuple_SetItem(v, i, Py_None);
113                    Py_INCREF(Py_None);
114                } else {
115                    PyTuple_SetItem(v, i, t);
116                }
117            }
118        }
119    }
120
121    CFRelease(proxyDict);
122    return result;
123
124error:
125    if (proxyDict)  CFRelease(proxyDict);
126    Py_XDECREF(result);
127    return NULL;
128}
129
130static int
131set_proxy(PyObject* proxies, const char* proto, CFDictionaryRef proxyDict,
132                CFStringRef enabledKey,
133                CFStringRef hostKey, CFStringRef portKey)
134{
135    CFNumberRef aNum;
136
137    aNum = CFDictionaryGetValue(proxyDict, enabledKey);
138    if (aNum && cfnum_to_int32(aNum)) {
139        CFStringRef hostString;
140
141        hostString = CFDictionaryGetValue(proxyDict, hostKey);
142        aNum = CFDictionaryGetValue(proxyDict, portKey);
143
144        if (hostString) {
145            int r;
146            PyObject* h = cfstring_to_pystring(hostString);
147            PyObject* v;
148            if (h) {
149                if (aNum) {
150                    int32_t port = cfnum_to_int32(aNum);
151                    v = PyUnicode_FromFormat("http://%U:%ld",
152                        h, (long)port);
153                } else {
154                    v = PyUnicode_FromFormat("http://%U", h);
155                }
156                Py_DECREF(h);
157                if (!v) return -1;
158                r = PyDict_SetItemString(proxies, proto,
159                    v);
160                Py_DECREF(v);
161                return r;
162            }
163        }
164
165    }
166    return 0;
167}
168
169
170
171static PyObject*
172get_proxies(PyObject* Py_UNUSED(mod), PyObject *Py_UNUSED(ignored))
173{
174    PyObject* result = NULL;
175    int r;
176    CFDictionaryRef proxyDict = NULL;
177
178    Py_BEGIN_ALLOW_THREADS
179    proxyDict = SCDynamicStoreCopyProxies(NULL);
180    Py_END_ALLOW_THREADS
181
182    if (proxyDict == NULL) {
183        return PyDict_New();
184    }
185
186    result = PyDict_New();
187    if (result == NULL) goto error;
188
189    r = set_proxy(result, "http", proxyDict,
190        kSCPropNetProxiesHTTPEnable,
191        kSCPropNetProxiesHTTPProxy,
192        kSCPropNetProxiesHTTPPort);
193    if (r == -1) goto error;
194    r = set_proxy(result, "https", proxyDict,
195        kSCPropNetProxiesHTTPSEnable,
196        kSCPropNetProxiesHTTPSProxy,
197        kSCPropNetProxiesHTTPSPort);
198    if (r == -1) goto error;
199    r = set_proxy(result, "ftp", proxyDict,
200        kSCPropNetProxiesFTPEnable,
201        kSCPropNetProxiesFTPProxy,
202        kSCPropNetProxiesFTPPort);
203    if (r == -1) goto error;
204    r = set_proxy(result, "gopher", proxyDict,
205        kSCPropNetProxiesGopherEnable,
206        kSCPropNetProxiesGopherProxy,
207        kSCPropNetProxiesGopherPort);
208    if (r == -1) goto error;
209
210    CFRelease(proxyDict);
211    return result;
212error:
213    if (proxyDict)  CFRelease(proxyDict);
214    Py_XDECREF(result);
215    return NULL;
216}
217
218static PyMethodDef mod_methods[] = {
219    {
220        "_get_proxy_settings",
221        get_proxy_settings,
222        METH_NOARGS,
223        NULL,
224    },
225    {
226        "_get_proxies",
227        get_proxies,
228        METH_NOARGS,
229        NULL,
230    },
231    { 0, 0, 0, 0 }
232};
233
234static PyModuleDef_Slot _scproxy_slots[] = {
235    {0, NULL}
236};
237
238static struct PyModuleDef _scproxy_module = {
239    PyModuleDef_HEAD_INIT,
240    .m_name = "_scproxy",
241    .m_size = 0,
242    .m_methods = mod_methods,
243    .m_slots = _scproxy_slots,
244};
245
246#ifdef __cplusplus
247extern "C" {
248#endif
249
250PyMODINIT_FUNC
251PyInit__scproxy(void)
252{
253    return PyModuleDef_Init(&_scproxy_module);
254}
255
256#ifdef __cplusplus
257}
258#endif
259