michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #import michael@0: #import michael@0: michael@0: #include "nsISystemProxySettings.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIURI.h" michael@0: #include "nsObjCExceptions.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: class nsOSXSystemProxySettings MOZ_FINAL : public nsISystemProxySettings { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSISYSTEMPROXYSETTINGS michael@0: michael@0: nsOSXSystemProxySettings(); michael@0: nsresult Init(); michael@0: michael@0: // called by OSX when the proxy settings have changed michael@0: void ProxyHasChanged(); michael@0: michael@0: // is there a PAC url specified in the system configuration michael@0: bool IsAutoconfigEnabled() const; michael@0: // retrieve the pac url michael@0: nsresult GetAutoconfigURL(nsAutoCString& aResult) const; michael@0: michael@0: // Find the SystemConfiguration proxy & port for a given URI michael@0: nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy); michael@0: michael@0: // is host:port on the proxy exception list? michael@0: bool IsInExceptionList(const nsACString& aHost) const; michael@0: michael@0: private: michael@0: ~nsOSXSystemProxySettings(); michael@0: michael@0: SCDynamicStoreContext mContext; michael@0: SCDynamicStoreRef mSystemDynamicStore; michael@0: NSDictionary* mProxyDict; michael@0: michael@0: michael@0: // Mapping of URI schemes to SystemConfiguration keys michael@0: struct SchemeMapping { michael@0: const char* mScheme; michael@0: CFStringRef mEnabled; michael@0: CFStringRef mHost; michael@0: CFStringRef mPort; michael@0: bool mIsSocksProxy; michael@0: }; michael@0: static const SchemeMapping gSchemeMappingList[]; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsOSXSystemProxySettings, nsISystemProxySettings) michael@0: michael@0: NS_IMETHODIMP michael@0: nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) michael@0: { michael@0: *aMainThreadOnly = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Mapping of URI schemes to SystemConfiguration keys michael@0: const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = { michael@0: {"http", kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, false}, michael@0: {"https", kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, false}, michael@0: {"ftp", kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort, false}, michael@0: {"socks", kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort, true}, michael@0: {NULL, NULL, NULL, NULL, false}, michael@0: }; michael@0: michael@0: static void michael@0: ProxyHasChangedWrapper(SCDynamicStoreRef aStore, CFArrayRef aChangedKeys, void* aInfo) michael@0: { michael@0: static_cast(aInfo)->ProxyHasChanged(); michael@0: } michael@0: michael@0: michael@0: nsOSXSystemProxySettings::nsOSXSystemProxySettings() michael@0: : mSystemDynamicStore(NULL), mProxyDict(NULL) michael@0: { michael@0: mContext = (SCDynamicStoreContext){0, this, NULL, NULL, NULL}; michael@0: } michael@0: michael@0: nsresult michael@0: nsOSXSystemProxySettings::Init() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: // Register for notification of proxy setting changes michael@0: // See: http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/chapter_4_section_5.html michael@0: mSystemDynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Mozilla"), ProxyHasChangedWrapper, &mContext); michael@0: if (!mSystemDynamicStore) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Set up the store to monitor any changes to the proxies michael@0: CFStringRef proxiesKey = SCDynamicStoreKeyCreateProxies(NULL); michael@0: if (!proxiesKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CFArrayRef keyArray = CFArrayCreate(NULL, (const void**)(&proxiesKey), 1, &kCFTypeArrayCallBacks); michael@0: CFRelease(proxiesKey); michael@0: if (!keyArray) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SCDynamicStoreSetNotificationKeys(mSystemDynamicStore, keyArray, NULL); michael@0: CFRelease(keyArray); michael@0: michael@0: // Add the dynamic store to the run loop michael@0: CFRunLoopSourceRef storeRLSource = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); michael@0: if (!storeRLSource) michael@0: return NS_ERROR_FAILURE; michael@0: CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource, kCFRunLoopCommonModes); michael@0: CFRelease(storeRLSource); michael@0: michael@0: // Load the initial copy of proxy info michael@0: mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); michael@0: if (!mProxyDict) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: nsOSXSystemProxySettings::~nsOSXSystemProxySettings() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mProxyDict release]; michael@0: michael@0: if (mSystemDynamicStore) { michael@0: // Invalidate the dynamic store's run loop source michael@0: // to get the store out of the run loop michael@0: CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); michael@0: if (rls) { michael@0: CFRunLoopSourceInvalidate(rls); michael@0: CFRelease(rls); michael@0: } michael@0: CFRelease(mSystemDynamicStore); michael@0: } michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsOSXSystemProxySettings::ProxyHasChanged() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mProxyDict release]; michael@0: mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: nsresult michael@0: nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); michael@0: michael@0: for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) { michael@0: // Check for matching scheme (when appropriate) michael@0: if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) && michael@0: !keys->mIsSocksProxy) michael@0: continue; michael@0: michael@0: // Check the proxy is enabled michael@0: NSNumber* enabled = [mProxyDict objectForKey:(NSString*)keys->mEnabled]; michael@0: NS_ENSURE_TRUE(enabled == NULL || [enabled isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); michael@0: if ([enabled intValue] == 0) michael@0: continue; michael@0: michael@0: // Get the proxy host michael@0: NSString* host = [mProxyDict objectForKey:(NSString*)keys->mHost]; michael@0: if (host == NULL) michael@0: break; michael@0: NS_ENSURE_TRUE([host isKindOfClass:[NSString class]], NS_ERROR_FAILURE); michael@0: aResultHost.Assign([host UTF8String]); michael@0: michael@0: // Get the proxy port michael@0: NSNumber* port = [mProxyDict objectForKey:(NSString*)keys->mPort]; michael@0: NS_ENSURE_TRUE([port isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); michael@0: aResultPort = [port intValue]; michael@0: michael@0: aResultSocksProxy = keys->mIsSocksProxy; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: bool michael@0: nsOSXSystemProxySettings::IsAutoconfigEnabled() const michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: NSNumber* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable]; michael@0: NS_ENSURE_TRUE(value == NULL || [value isKindOfClass:[NSNumber class]], false); michael@0: return ([value intValue] != 0); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); michael@0: } michael@0: michael@0: nsresult michael@0: nsOSXSystemProxySettings::GetAutoconfigURL(nsAutoCString& aResult) const michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: NSString* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString]; michael@0: if (value != NULL) { michael@0: NS_ENSURE_TRUE([value isKindOfClass:[NSString class]], NS_ERROR_FAILURE); michael@0: aResult.Assign([value UTF8String]); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: static bool michael@0: IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) michael@0: { michael@0: nsAutoCString host(aHost); michael@0: nsAutoCString override(aOverride); michael@0: michael@0: int32_t overrideLength = override.Length(); michael@0: int32_t tokenStart = 0; michael@0: int32_t offset = 0; michael@0: bool star = false; michael@0: michael@0: while (tokenStart < overrideLength) { michael@0: int32_t tokenEnd = override.FindChar('*', tokenStart); michael@0: if (tokenEnd == tokenStart) { michael@0: // Star is the first character in the token. michael@0: star = true; michael@0: tokenStart++; michael@0: // If the character following the '*' is a '.' character then skip michael@0: // it so that "*.foo.com" allows "foo.com". michael@0: if (override.FindChar('.', tokenStart) == tokenStart) michael@0: tokenStart++; michael@0: } else { michael@0: if (tokenEnd == -1) michael@0: tokenEnd = overrideLength; // no '*' char, match rest of string michael@0: nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); michael@0: offset = host.Find(token, offset); michael@0: if (offset == -1 || (!star && offset)) michael@0: return false; michael@0: star = false; michael@0: tokenStart = tokenEnd; michael@0: offset += token.Length(); michael@0: } michael@0: } michael@0: michael@0: return (star || (offset == static_cast(host.Length()))); michael@0: } michael@0: michael@0: bool michael@0: nsOSXSystemProxySettings::IsInExceptionList(const nsACString& aHost) const michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: NS_ENSURE_TRUE(mProxyDict != NULL, false); michael@0: michael@0: NSArray* exceptionList = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesExceptionsList]; michael@0: NS_ENSURE_TRUE(exceptionList == NULL || [exceptionList isKindOfClass:[NSArray class]], false); michael@0: michael@0: NSEnumerator* exceptionEnumerator = [exceptionList objectEnumerator]; michael@0: NSString* currentValue = NULL; michael@0: while ((currentValue = [exceptionEnumerator nextObject])) { michael@0: NS_ENSURE_TRUE([currentValue isKindOfClass:[NSString class]], false); michael@0: nsAutoCString overrideStr([currentValue UTF8String]); michael@0: if (IsHostProxyEntry(aHost, overrideStr)) michael@0: return true; michael@0: } michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); michael@0: } michael@0: michael@0: nsresult michael@0: nsOSXSystemProxySettings::GetPACURI(nsACString& aResult) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoCString pacUrl; michael@0: if (IsAutoconfigEnabled() && NS_SUCCEEDED(GetAutoconfigURL(pacUrl))) { michael@0: aResult.Assign(pacUrl); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: nsresult michael@0: nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec, michael@0: const nsACString & aScheme, michael@0: const nsACString & aHost, michael@0: const int32_t aPort, michael@0: nsACString & aResult) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: int32_t proxyPort; michael@0: nsAutoCString proxyHost; michael@0: bool proxySocks; michael@0: nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks); michael@0: michael@0: if (NS_FAILED(rv) || IsInExceptionList(aHost)) { michael@0: aResult.AssignLiteral("DIRECT"); michael@0: } else if (proxySocks) { michael@0: aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort)); michael@0: } else { michael@0: aResult.Assign(NS_LITERAL_CSTRING("PROXY ") + proxyHost + nsPrintfCString(":%d", proxyPort)); michael@0: } michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: #define NS_OSXSYSTEMPROXYSERVICE_CID /* 9afcd4b8-2e0f-41f4-8f1f-3bf0d3cf67de */\ michael@0: { 0x9afcd4b8, 0x2e0f, 0x41f4, \ michael@0: { 0x8f, 0x1f, 0x3b, 0xf0, 0xd3, 0xcf, 0x67, 0xde } } michael@0: michael@0: NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSXSystemProxySettings, Init); michael@0: NS_DEFINE_NAMED_CID(NS_OSXSYSTEMPROXYSERVICE_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kOSXSysProxyCIDs[] = { michael@0: { &kNS_OSXSYSTEMPROXYSERVICE_CID, false, NULL, nsOSXSystemProxySettingsConstructor }, michael@0: { NULL } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kOSXSysProxyContracts[] = { michael@0: { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_OSXSYSTEMPROXYSERVICE_CID }, michael@0: { NULL } michael@0: }; michael@0: michael@0: static const mozilla::Module kOSXSysProxyModule = { michael@0: mozilla::Module::kVersion, michael@0: kOSXSysProxyCIDs, michael@0: kOSXSysProxyContracts michael@0: }; michael@0: michael@0: NSMODULE_DEFN(nsOSXProxyModule) = &kOSXSysProxyModule;