|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #import <Cocoa/Cocoa.h> |
|
8 #import <SystemConfiguration/SystemConfiguration.h> |
|
9 |
|
10 #include "nsISystemProxySettings.h" |
|
11 #include "mozilla/ModuleUtils.h" |
|
12 #include "nsIServiceManager.h" |
|
13 #include "nsPrintfCString.h" |
|
14 #include "nsNetUtil.h" |
|
15 #include "nsISupportsPrimitives.h" |
|
16 #include "nsIURI.h" |
|
17 #include "nsObjCExceptions.h" |
|
18 #include "mozilla/Attributes.h" |
|
19 |
|
20 class nsOSXSystemProxySettings MOZ_FINAL : public nsISystemProxySettings { |
|
21 public: |
|
22 NS_DECL_THREADSAFE_ISUPPORTS |
|
23 NS_DECL_NSISYSTEMPROXYSETTINGS |
|
24 |
|
25 nsOSXSystemProxySettings(); |
|
26 nsresult Init(); |
|
27 |
|
28 // called by OSX when the proxy settings have changed |
|
29 void ProxyHasChanged(); |
|
30 |
|
31 // is there a PAC url specified in the system configuration |
|
32 bool IsAutoconfigEnabled() const; |
|
33 // retrieve the pac url |
|
34 nsresult GetAutoconfigURL(nsAutoCString& aResult) const; |
|
35 |
|
36 // Find the SystemConfiguration proxy & port for a given URI |
|
37 nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy); |
|
38 |
|
39 // is host:port on the proxy exception list? |
|
40 bool IsInExceptionList(const nsACString& aHost) const; |
|
41 |
|
42 private: |
|
43 ~nsOSXSystemProxySettings(); |
|
44 |
|
45 SCDynamicStoreContext mContext; |
|
46 SCDynamicStoreRef mSystemDynamicStore; |
|
47 NSDictionary* mProxyDict; |
|
48 |
|
49 |
|
50 // Mapping of URI schemes to SystemConfiguration keys |
|
51 struct SchemeMapping { |
|
52 const char* mScheme; |
|
53 CFStringRef mEnabled; |
|
54 CFStringRef mHost; |
|
55 CFStringRef mPort; |
|
56 bool mIsSocksProxy; |
|
57 }; |
|
58 static const SchemeMapping gSchemeMappingList[]; |
|
59 }; |
|
60 |
|
61 NS_IMPL_ISUPPORTS(nsOSXSystemProxySettings, nsISystemProxySettings) |
|
62 |
|
63 NS_IMETHODIMP |
|
64 nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) |
|
65 { |
|
66 *aMainThreadOnly = true; |
|
67 return NS_OK; |
|
68 } |
|
69 |
|
70 // Mapping of URI schemes to SystemConfiguration keys |
|
71 const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = { |
|
72 {"http", kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, false}, |
|
73 {"https", kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, false}, |
|
74 {"ftp", kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort, false}, |
|
75 {"socks", kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort, true}, |
|
76 {NULL, NULL, NULL, NULL, false}, |
|
77 }; |
|
78 |
|
79 static void |
|
80 ProxyHasChangedWrapper(SCDynamicStoreRef aStore, CFArrayRef aChangedKeys, void* aInfo) |
|
81 { |
|
82 static_cast<nsOSXSystemProxySettings*>(aInfo)->ProxyHasChanged(); |
|
83 } |
|
84 |
|
85 |
|
86 nsOSXSystemProxySettings::nsOSXSystemProxySettings() |
|
87 : mSystemDynamicStore(NULL), mProxyDict(NULL) |
|
88 { |
|
89 mContext = (SCDynamicStoreContext){0, this, NULL, NULL, NULL}; |
|
90 } |
|
91 |
|
92 nsresult |
|
93 nsOSXSystemProxySettings::Init() |
|
94 { |
|
95 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
96 |
|
97 // Register for notification of proxy setting changes |
|
98 // See: http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/chapter_4_section_5.html |
|
99 mSystemDynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Mozilla"), ProxyHasChangedWrapper, &mContext); |
|
100 if (!mSystemDynamicStore) |
|
101 return NS_ERROR_FAILURE; |
|
102 |
|
103 // Set up the store to monitor any changes to the proxies |
|
104 CFStringRef proxiesKey = SCDynamicStoreKeyCreateProxies(NULL); |
|
105 if (!proxiesKey) |
|
106 return NS_ERROR_FAILURE; |
|
107 |
|
108 CFArrayRef keyArray = CFArrayCreate(NULL, (const void**)(&proxiesKey), 1, &kCFTypeArrayCallBacks); |
|
109 CFRelease(proxiesKey); |
|
110 if (!keyArray) |
|
111 return NS_ERROR_FAILURE; |
|
112 |
|
113 SCDynamicStoreSetNotificationKeys(mSystemDynamicStore, keyArray, NULL); |
|
114 CFRelease(keyArray); |
|
115 |
|
116 // Add the dynamic store to the run loop |
|
117 CFRunLoopSourceRef storeRLSource = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); |
|
118 if (!storeRLSource) |
|
119 return NS_ERROR_FAILURE; |
|
120 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource, kCFRunLoopCommonModes); |
|
121 CFRelease(storeRLSource); |
|
122 |
|
123 // Load the initial copy of proxy info |
|
124 mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); |
|
125 if (!mProxyDict) |
|
126 return NS_ERROR_FAILURE; |
|
127 |
|
128 return NS_OK; |
|
129 |
|
130 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
131 } |
|
132 |
|
133 nsOSXSystemProxySettings::~nsOSXSystemProxySettings() |
|
134 { |
|
135 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
136 |
|
137 [mProxyDict release]; |
|
138 |
|
139 if (mSystemDynamicStore) { |
|
140 // Invalidate the dynamic store's run loop source |
|
141 // to get the store out of the run loop |
|
142 CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); |
|
143 if (rls) { |
|
144 CFRunLoopSourceInvalidate(rls); |
|
145 CFRelease(rls); |
|
146 } |
|
147 CFRelease(mSystemDynamicStore); |
|
148 } |
|
149 |
|
150 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
151 } |
|
152 |
|
153 |
|
154 void |
|
155 nsOSXSystemProxySettings::ProxyHasChanged() |
|
156 { |
|
157 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
158 |
|
159 [mProxyDict release]; |
|
160 mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); |
|
161 |
|
162 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
163 } |
|
164 |
|
165 nsresult |
|
166 nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy) |
|
167 { |
|
168 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
169 |
|
170 NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); |
|
171 |
|
172 for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) { |
|
173 // Check for matching scheme (when appropriate) |
|
174 if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) && |
|
175 !keys->mIsSocksProxy) |
|
176 continue; |
|
177 |
|
178 // Check the proxy is enabled |
|
179 NSNumber* enabled = [mProxyDict objectForKey:(NSString*)keys->mEnabled]; |
|
180 NS_ENSURE_TRUE(enabled == NULL || [enabled isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); |
|
181 if ([enabled intValue] == 0) |
|
182 continue; |
|
183 |
|
184 // Get the proxy host |
|
185 NSString* host = [mProxyDict objectForKey:(NSString*)keys->mHost]; |
|
186 if (host == NULL) |
|
187 break; |
|
188 NS_ENSURE_TRUE([host isKindOfClass:[NSString class]], NS_ERROR_FAILURE); |
|
189 aResultHost.Assign([host UTF8String]); |
|
190 |
|
191 // Get the proxy port |
|
192 NSNumber* port = [mProxyDict objectForKey:(NSString*)keys->mPort]; |
|
193 NS_ENSURE_TRUE([port isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); |
|
194 aResultPort = [port intValue]; |
|
195 |
|
196 aResultSocksProxy = keys->mIsSocksProxy; |
|
197 |
|
198 return NS_OK; |
|
199 } |
|
200 |
|
201 return NS_ERROR_FAILURE; |
|
202 |
|
203 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
204 } |
|
205 |
|
206 bool |
|
207 nsOSXSystemProxySettings::IsAutoconfigEnabled() const |
|
208 { |
|
209 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; |
|
210 |
|
211 NSNumber* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable]; |
|
212 NS_ENSURE_TRUE(value == NULL || [value isKindOfClass:[NSNumber class]], false); |
|
213 return ([value intValue] != 0); |
|
214 |
|
215 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); |
|
216 } |
|
217 |
|
218 nsresult |
|
219 nsOSXSystemProxySettings::GetAutoconfigURL(nsAutoCString& aResult) const |
|
220 { |
|
221 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
222 |
|
223 NSString* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString]; |
|
224 if (value != NULL) { |
|
225 NS_ENSURE_TRUE([value isKindOfClass:[NSString class]], NS_ERROR_FAILURE); |
|
226 aResult.Assign([value UTF8String]); |
|
227 return NS_OK; |
|
228 } |
|
229 |
|
230 return NS_ERROR_FAILURE; |
|
231 |
|
232 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
233 } |
|
234 |
|
235 static bool |
|
236 IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) |
|
237 { |
|
238 nsAutoCString host(aHost); |
|
239 nsAutoCString override(aOverride); |
|
240 |
|
241 int32_t overrideLength = override.Length(); |
|
242 int32_t tokenStart = 0; |
|
243 int32_t offset = 0; |
|
244 bool star = false; |
|
245 |
|
246 while (tokenStart < overrideLength) { |
|
247 int32_t tokenEnd = override.FindChar('*', tokenStart); |
|
248 if (tokenEnd == tokenStart) { |
|
249 // Star is the first character in the token. |
|
250 star = true; |
|
251 tokenStart++; |
|
252 // If the character following the '*' is a '.' character then skip |
|
253 // it so that "*.foo.com" allows "foo.com". |
|
254 if (override.FindChar('.', tokenStart) == tokenStart) |
|
255 tokenStart++; |
|
256 } else { |
|
257 if (tokenEnd == -1) |
|
258 tokenEnd = overrideLength; // no '*' char, match rest of string |
|
259 nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); |
|
260 offset = host.Find(token, offset); |
|
261 if (offset == -1 || (!star && offset)) |
|
262 return false; |
|
263 star = false; |
|
264 tokenStart = tokenEnd; |
|
265 offset += token.Length(); |
|
266 } |
|
267 } |
|
268 |
|
269 return (star || (offset == static_cast<int32_t>(host.Length()))); |
|
270 } |
|
271 |
|
272 bool |
|
273 nsOSXSystemProxySettings::IsInExceptionList(const nsACString& aHost) const |
|
274 { |
|
275 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; |
|
276 |
|
277 NS_ENSURE_TRUE(mProxyDict != NULL, false); |
|
278 |
|
279 NSArray* exceptionList = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesExceptionsList]; |
|
280 NS_ENSURE_TRUE(exceptionList == NULL || [exceptionList isKindOfClass:[NSArray class]], false); |
|
281 |
|
282 NSEnumerator* exceptionEnumerator = [exceptionList objectEnumerator]; |
|
283 NSString* currentValue = NULL; |
|
284 while ((currentValue = [exceptionEnumerator nextObject])) { |
|
285 NS_ENSURE_TRUE([currentValue isKindOfClass:[NSString class]], false); |
|
286 nsAutoCString overrideStr([currentValue UTF8String]); |
|
287 if (IsHostProxyEntry(aHost, overrideStr)) |
|
288 return true; |
|
289 } |
|
290 |
|
291 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); |
|
292 } |
|
293 |
|
294 nsresult |
|
295 nsOSXSystemProxySettings::GetPACURI(nsACString& aResult) |
|
296 { |
|
297 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
298 |
|
299 NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); |
|
300 |
|
301 nsAutoCString pacUrl; |
|
302 if (IsAutoconfigEnabled() && NS_SUCCEEDED(GetAutoconfigURL(pacUrl))) { |
|
303 aResult.Assign(pacUrl); |
|
304 return NS_OK; |
|
305 } |
|
306 |
|
307 return NS_ERROR_FAILURE; |
|
308 |
|
309 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
310 } |
|
311 |
|
312 nsresult |
|
313 nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec, |
|
314 const nsACString & aScheme, |
|
315 const nsACString & aHost, |
|
316 const int32_t aPort, |
|
317 nsACString & aResult) |
|
318 { |
|
319 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
320 |
|
321 int32_t proxyPort; |
|
322 nsAutoCString proxyHost; |
|
323 bool proxySocks; |
|
324 nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks); |
|
325 |
|
326 if (NS_FAILED(rv) || IsInExceptionList(aHost)) { |
|
327 aResult.AssignLiteral("DIRECT"); |
|
328 } else if (proxySocks) { |
|
329 aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort)); |
|
330 } else { |
|
331 aResult.Assign(NS_LITERAL_CSTRING("PROXY ") + proxyHost + nsPrintfCString(":%d", proxyPort)); |
|
332 } |
|
333 |
|
334 return NS_OK; |
|
335 |
|
336 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
337 } |
|
338 |
|
339 #define NS_OSXSYSTEMPROXYSERVICE_CID /* 9afcd4b8-2e0f-41f4-8f1f-3bf0d3cf67de */\ |
|
340 { 0x9afcd4b8, 0x2e0f, 0x41f4, \ |
|
341 { 0x8f, 0x1f, 0x3b, 0xf0, 0xd3, 0xcf, 0x67, 0xde } } |
|
342 |
|
343 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSXSystemProxySettings, Init); |
|
344 NS_DEFINE_NAMED_CID(NS_OSXSYSTEMPROXYSERVICE_CID); |
|
345 |
|
346 static const mozilla::Module::CIDEntry kOSXSysProxyCIDs[] = { |
|
347 { &kNS_OSXSYSTEMPROXYSERVICE_CID, false, NULL, nsOSXSystemProxySettingsConstructor }, |
|
348 { NULL } |
|
349 }; |
|
350 |
|
351 static const mozilla::Module::ContractIDEntry kOSXSysProxyContracts[] = { |
|
352 { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_OSXSYSTEMPROXYSERVICE_CID }, |
|
353 { NULL } |
|
354 }; |
|
355 |
|
356 static const mozilla::Module kOSXSysProxyModule = { |
|
357 mozilla::Module::kVersion, |
|
358 kOSXSysProxyCIDs, |
|
359 kOSXSysProxyContracts |
|
360 }; |
|
361 |
|
362 NSMODULE_DEFN(nsOSXProxyModule) = &kOSXSysProxyModule; |