Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / proxy / proxy_config_service_mac.cc
blob1483dbc68efaa3fbe4d3a28eb23002d8afa708ac
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/proxy/proxy_config_service_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <SystemConfiguration/SystemConfiguration.h>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "net/base/net_errors.h"
17 #include "net/proxy/proxy_config.h"
18 #include "net/proxy/proxy_info.h"
19 #include "net/proxy/proxy_server.h"
21 namespace net {
23 namespace {
25 // Utility function to pull out a boolean value from a dictionary and return it,
26 // returning a default value if the key is not present.
27 bool GetBoolFromDictionary(CFDictionaryRef dict,
28 CFStringRef key,
29 bool default_value) {
30 CFNumberRef number = base::mac::GetValueFromDictionary<CFNumberRef>(dict,
31 key);
32 if (!number)
33 return default_value;
35 int int_value;
36 if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
37 return int_value;
38 else
39 return default_value;
42 void GetCurrentProxyConfig(ProxyConfig* config) {
43 base::ScopedCFTypeRef<CFDictionaryRef> config_dict(
44 SCDynamicStoreCopyProxies(NULL));
45 DCHECK(config_dict);
47 // auto-detect
49 // There appears to be no UI for this configuration option, and we're not sure
50 // if Apple's proxy code even takes it into account. But the constant is in
51 // the header file so we'll use it.
52 config->set_auto_detect(
53 GetBoolFromDictionary(config_dict.get(),
54 kSCPropNetProxiesProxyAutoDiscoveryEnable,
55 false));
57 // PAC file
59 if (GetBoolFromDictionary(config_dict.get(),
60 kSCPropNetProxiesProxyAutoConfigEnable,
61 false)) {
62 CFStringRef pac_url_ref = base::mac::GetValueFromDictionary<CFStringRef>(
63 config_dict.get(), kSCPropNetProxiesProxyAutoConfigURLString);
64 if (pac_url_ref)
65 config->set_pac_url(GURL(base::SysCFStringRefToUTF8(pac_url_ref)));
68 // proxies (for now ftp, http, https, and SOCKS)
70 if (GetBoolFromDictionary(config_dict.get(),
71 kSCPropNetProxiesFTPEnable,
72 false)) {
73 ProxyServer proxy_server =
74 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
75 config_dict.get(),
76 kSCPropNetProxiesFTPProxy,
77 kSCPropNetProxiesFTPPort);
78 if (proxy_server.is_valid()) {
79 config->proxy_rules().type =
80 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
81 config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_server);
84 if (GetBoolFromDictionary(config_dict.get(),
85 kSCPropNetProxiesHTTPEnable,
86 false)) {
87 ProxyServer proxy_server =
88 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
89 config_dict.get(),
90 kSCPropNetProxiesHTTPProxy,
91 kSCPropNetProxiesHTTPPort);
92 if (proxy_server.is_valid()) {
93 config->proxy_rules().type =
94 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
95 config->proxy_rules().proxies_for_http.SetSingleProxyServer(proxy_server);
98 if (GetBoolFromDictionary(config_dict.get(),
99 kSCPropNetProxiesHTTPSEnable,
100 false)) {
101 ProxyServer proxy_server =
102 ProxyServer::FromDictionary(ProxyServer::SCHEME_HTTP,
103 config_dict.get(),
104 kSCPropNetProxiesHTTPSProxy,
105 kSCPropNetProxiesHTTPSPort);
106 if (proxy_server.is_valid()) {
107 config->proxy_rules().type =
108 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
109 config->proxy_rules().proxies_for_https.
110 SetSingleProxyServer(proxy_server);
113 if (GetBoolFromDictionary(config_dict.get(),
114 kSCPropNetProxiesSOCKSEnable,
115 false)) {
116 ProxyServer proxy_server =
117 ProxyServer::FromDictionary(ProxyServer::SCHEME_SOCKS5,
118 config_dict.get(),
119 kSCPropNetProxiesSOCKSProxy,
120 kSCPropNetProxiesSOCKSPort);
121 if (proxy_server.is_valid()) {
122 config->proxy_rules().type =
123 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
124 config->proxy_rules().fallback_proxies.SetSingleProxyServer(proxy_server);
128 // proxy bypass list
130 CFArrayRef bypass_array_ref = base::mac::GetValueFromDictionary<CFArrayRef>(
131 config_dict.get(), kSCPropNetProxiesExceptionsList);
132 if (bypass_array_ref) {
133 CFIndex bypass_array_count = CFArrayGetCount(bypass_array_ref);
134 for (CFIndex i = 0; i < bypass_array_count; ++i) {
135 CFStringRef bypass_item_ref = base::mac::CFCast<CFStringRef>(
136 CFArrayGetValueAtIndex(bypass_array_ref, i));
137 if (!bypass_item_ref) {
138 LOG(WARNING) << "Expected value for item " << i
139 << " in the kSCPropNetProxiesExceptionsList"
140 " to be a CFStringRef but it was not";
142 } else {
143 config->proxy_rules().bypass_rules.AddRuleFromString(
144 base::SysCFStringRefToUTF8(bypass_item_ref));
149 // proxy bypass boolean
151 if (GetBoolFromDictionary(config_dict.get(),
152 kSCPropNetProxiesExcludeSimpleHostnames,
153 false)) {
154 config->proxy_rules().bypass_rules.AddRuleToBypassLocal();
157 // Source
158 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM);
161 } // namespace
163 // Reference-counted helper for posting a task to
164 // ProxyConfigServiceMac::OnProxyConfigChanged between the notifier and IO
165 // thread. This helper object may outlive the ProxyConfigServiceMac.
166 class ProxyConfigServiceMac::Helper
167 : public base::RefCountedThreadSafe<ProxyConfigServiceMac::Helper> {
168 public:
169 explicit Helper(ProxyConfigServiceMac* parent) : parent_(parent) {
170 DCHECK(parent);
173 // Called when the parent is destroyed.
174 void Orphan() {
175 parent_ = NULL;
178 void OnProxyConfigChanged(const ProxyConfig& new_config) {
179 if (parent_)
180 parent_->OnProxyConfigChanged(new_config);
183 private:
184 friend class base::RefCountedThreadSafe<Helper>;
185 ~Helper() {}
187 ProxyConfigServiceMac* parent_;
190 void ProxyConfigServiceMac::Forwarder::SetDynamicStoreNotificationKeys(
191 SCDynamicStoreRef store) {
192 proxy_config_service_->SetDynamicStoreNotificationKeys(store);
195 void ProxyConfigServiceMac::Forwarder::OnNetworkConfigChange(
196 CFArrayRef changed_keys) {
197 proxy_config_service_->OnNetworkConfigChange(changed_keys);
200 ProxyConfigServiceMac::ProxyConfigServiceMac(
201 const scoped_refptr<base::SingleThreadTaskRunner>& io_thread_task_runner)
202 : forwarder_(this),
203 has_fetched_config_(false),
204 helper_(new Helper(this)),
205 io_thread_task_runner_(io_thread_task_runner) {
206 DCHECK(io_thread_task_runner_.get());
207 config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_));
210 ProxyConfigServiceMac::~ProxyConfigServiceMac() {
211 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
212 // Delete the config_watcher_ to ensure the notifier thread finishes before
213 // this object is destroyed.
214 config_watcher_.reset();
215 helper_->Orphan();
218 void ProxyConfigServiceMac::AddObserver(Observer* observer) {
219 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
220 observers_.AddObserver(observer);
223 void ProxyConfigServiceMac::RemoveObserver(Observer* observer) {
224 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
225 observers_.RemoveObserver(observer);
228 ProxyConfigService::ConfigAvailability
229 ProxyConfigServiceMac::GetLatestProxyConfig(ProxyConfig* config) {
230 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
232 // Lazy-initialize by fetching the proxy setting from this thread.
233 if (!has_fetched_config_) {
234 GetCurrentProxyConfig(&last_config_fetched_);
235 has_fetched_config_ = true;
238 *config = last_config_fetched_;
239 return has_fetched_config_ ? CONFIG_VALID : CONFIG_PENDING;
242 void ProxyConfigServiceMac::SetDynamicStoreNotificationKeys(
243 SCDynamicStoreRef store) {
244 // Called on notifier thread.
246 CFStringRef proxies_key = SCDynamicStoreKeyCreateProxies(NULL);
247 CFArrayRef key_array = CFArrayCreate(
248 NULL, (const void **)(&proxies_key), 1, &kCFTypeArrayCallBacks);
250 bool ret = SCDynamicStoreSetNotificationKeys(store, key_array, NULL);
251 // TODO(willchan): Figure out a proper way to handle this rather than crash.
252 CHECK(ret);
254 CFRelease(key_array);
255 CFRelease(proxies_key);
258 void ProxyConfigServiceMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
259 // Called on notifier thread.
261 // Fetch the new system proxy configuration.
262 ProxyConfig new_config;
263 GetCurrentProxyConfig(&new_config);
265 // Call OnProxyConfigChanged() on the IO thread to notify our observers.
266 io_thread_task_runner_->PostTask(
267 FROM_HERE,
268 base::Bind(&Helper::OnProxyConfigChanged, helper_.get(), new_config));
271 void ProxyConfigServiceMac::OnProxyConfigChanged(
272 const ProxyConfig& new_config) {
273 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
275 // Keep track of the last value we have seen.
276 has_fetched_config_ = true;
277 last_config_fetched_ = new_config;
279 // Notify all the observers.
280 FOR_EACH_OBSERVER(Observer, observers_,
281 OnProxyConfigChanged(new_config, CONFIG_VALID));
284 } // namespace net