1 /*******************************************************************************
2 * libproxy - A library for proxy configuration
3 * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 ******************************************************************************/
19
20 #include <sstream>
21
22 #include "../extension_config.hpp"
23 using namespace libproxy;
24
25 #include <SystemConfiguration/SystemConfiguration.h>
26
27 class str : public string {
28 public:
str(CFStringRef s)29 str(CFStringRef s) : string() {
30 if (!s) return;
31 const char* tmp = CFStringGetCStringPtr(s, CFStringGetFastestEncoding(s));
32 *this += tmp ? tmp : "";
33 }
34
str(CFArrayRef a)35 str(CFArrayRef a) : string() {
36 if (!a) return;
37 for (CFIndex i=0 ; i < CFArrayGetCount(a) ; i++) {
38 CFStringRef s = (CFStringRef) CFArrayGetValueAtIndex(a, i);
39 *this += str(s);
40 if (i+1 < CFArrayGetCount(a))
41 *this += ",";
42 }
43 }
44 };
45
46 template <class T>
getobj(CFDictionaryRef settings,string key)47 static T getobj(CFDictionaryRef settings, string key) {
48 if (!settings) return NULL;
49 CFStringRef k = CFStringCreateWithCString(NULL, key.c_str(), kCFStringEncodingMacRoman);
50 if (!k) return NULL;
51 T retval = (T) CFDictionaryGetValue(settings, k);
52 CFRelease(k);
53 return retval;
54 }
55
getint(CFDictionaryRef settings,string key,int64_t & answer)56 static bool getint(CFDictionaryRef settings, string key, int64_t& answer) {
57 CFNumberRef n = getobj<CFNumberRef>(settings, key);
58 if (!n) return false;
59 if (!CFNumberGetValue(n, kCFNumberSInt64Type, &answer))
60 return false;
61 return true;
62 }
63
getbool(CFDictionaryRef settings,string key,bool dflt=false)64 static bool getbool(CFDictionaryRef settings, string key, bool dflt=false) {
65 int64_t i;
66 if (!getint(settings, key, i)) return dflt;
67 return i != 0;
68 }
69
protocol_url(CFDictionaryRef settings,string protocol,string & config)70 static bool protocol_url(CFDictionaryRef settings, string protocol, string& config) {
71 int64_t port;
72 string host;
73
74 // Check ProtocolEnabled
75 if (!getbool(settings, protocol + "Enable"))
76 return false;
77
78 // Get ProtocolPort
79 if (!getint(settings, protocol + "Port", port))
80 return false;
81
82 // Get ProtocolProxy
83 if ((host = str(getobj<CFStringRef>(settings, protocol + "Proxy"))) == "")
84 return false;
85
86 stringstream ss;
87 if (protocol == "HTTP" || protocol == "HTTPS" || protocol == "FTP" || protocol == "Gopher")
88 ss << "http://";
89 else if (protocol == "RTSP")
90 ss << "rtsp://";
91 else if (protocol == "SOCKS")
92 ss << "socks://";
93 else
94 return false;
95 ss << host;
96 ss << ":";
97 ss << port;
98
99 config = ss.str();
100 return true;
101 }
102
toupper(string str)103 static string toupper(string str) {
104 string tmp;
105 for (unsigned int i=0 ; str.c_str()[i] ; i++)
106 tmp += toupper(str.c_str()[i]);
107 return tmp;
108 }
109
capitalize(string str)110 static string capitalize(string str) {
111 char c = toupper(str.c_str()[0]);
112 return string(&c, 1) + str.substr(1);
113 }
114
115 class macosx_config_extension : public config_extension {
116 public:
get_config(const url & the_url)117 vector<url> get_config(const url &the_url) {
118 string tmp;
119 CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL);
120 vector<url> response;
121
122 if (!proxies) throw runtime_error("Unable to fetch proxy configuration");
123
124 // wpad://
125 if (getbool(proxies, "ProxyAutoDiscoveryEnable")) {
126 CFRelease(proxies);
127 response.push_back(url("wpad://"));
128 }
129
130 // pac+http://...
131 else if (getbool(proxies, "ProxyAutoConfigEnable") &&
132 (tmp = str(getobj<CFStringRef>(proxies, "ProxyAutoConfigURLString"))) != "" &&
133 url::is_valid(tmp)) {
134 CFRelease(proxies);
135 response.push_back(url(string("pac+") + tmp));
136 }
137
138 // http:// or socks:// (TODO: gopher:// and rtsp:// ???)
139 else if ((protocol_url(proxies, toupper(the_url.get_scheme()), tmp) && url::is_valid(tmp)) ||
140 (protocol_url(proxies, capitalize(the_url.get_scheme()), tmp) && url::is_valid(tmp)) ||
141 (protocol_url(proxies, toupper("http"), tmp) && url::is_valid(tmp)) ||
142 (protocol_url(proxies, toupper("socks"), tmp) && url::is_valid(tmp))) {
143 CFRelease(proxies);
144 response.push_back(url(tmp));
145 }
146 else {
147 // direct://
148 CFRelease(proxies);
149 response.push_back(url("direct://"));
150 }
151
152 return response;
153 }
154
get_ignore(const url &)155 string get_ignore(const url&) {
156 // Get config dict
157 CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL);
158 if (!proxies) return "";
159
160 // Get ignores
161 string tmp = str(getobj<CFArrayRef>(proxies, "ExceptionsList"));
162 if (getbool(proxies, "ExcludeSimpleHostnames"))
163 tmp += (tmp == "" ? string("") : string(",")) + "<local>";
164
165 CFRelease(proxies);
166 return tmp;
167 }
168 };
169
170 MM_MODULE_INIT_EZ(macosx_config_extension, true, NULL, NULL);
171
172