Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / proxy / proxy_config_service_mac.cc
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.
4
5 #include "net/proxy/proxy_config_service_mac.h"
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <SystemConfiguration/SystemConfiguration.h>
9
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"
20
21 namespace net {
22
23 namespace {
24
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;
34
35   int int_value;
36   if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
37     return int_value;
38   else
39     return default_value;
40 }
41
42 void GetCurrentProxyConfig(ProxyConfig* config) {
43   base::ScopedCFTypeRef<CFDictionaryRef> config_dict(
44       SCDynamicStoreCopyProxies(NULL));
45   DCHECK(config_dict);
46
47   // auto-detect
48
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));
56
57   // PAC file
58
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)));
66   }
67
68   // proxies (for now ftp, http, https, and SOCKS)
69
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);
82     }
83   }
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);
96     }
97   }
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);
111     }
112   }
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);
125     }
126   }
127
128   // proxy bypass list
129
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";
141
142       } else {
143         config->proxy_rules().bypass_rules.AddRuleFromString(
144             base::SysCFStringRefToUTF8(bypass_item_ref));
145       }
146     }
147   }
148
149   // proxy bypass boolean
150
151   if (GetBoolFromDictionary(config_dict.get(),
152                             kSCPropNetProxiesExcludeSimpleHostnames,
153                             false)) {
154     config->proxy_rules().bypass_rules.AddRuleToBypassLocal();
155   }
156
157   // Source
158   config->set_source(PROXY_CONFIG_SOURCE_SYSTEM);
159 }
160
161 }  // namespace
162
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);
171   }
172
173   // Called when the parent is destroyed.
174   void Orphan() {
175     parent_ = NULL;
176   }
177
178   void OnProxyConfigChanged(const ProxyConfig& new_config) {
179     if (parent_)
180       parent_->OnProxyConfigChanged(new_config);
181   }
182
183  private:
184   friend class base::RefCountedThreadSafe<Helper>;
185   ~Helper() {}
186
187   ProxyConfigServiceMac* parent_;
188 };
189
190 void ProxyConfigServiceMac::Forwarder::SetDynamicStoreNotificationKeys(
191     SCDynamicStoreRef store) {
192   proxy_config_service_->SetDynamicStoreNotificationKeys(store);
193 }
194
195 void ProxyConfigServiceMac::Forwarder::OnNetworkConfigChange(
196     CFArrayRef changed_keys) {
197   proxy_config_service_->OnNetworkConfigChange(changed_keys);
198 }
199
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_));
208 }
209
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();
216 }
217
218 void ProxyConfigServiceMac::AddObserver(Observer* observer) {
219   DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
220   observers_.AddObserver(observer);
221 }
222
223 void ProxyConfigServiceMac::RemoveObserver(Observer* observer) {
224   DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
225   observers_.RemoveObserver(observer);
226 }
227
228 net::ProxyConfigService::ConfigAvailability
229     ProxyConfigServiceMac::GetLatestProxyConfig(ProxyConfig* config) {
230   DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
231
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;
236   }
237
238   *config = last_config_fetched_;
239   return has_fetched_config_ ? CONFIG_VALID : CONFIG_PENDING;
240 }
241
242 void ProxyConfigServiceMac::SetDynamicStoreNotificationKeys(
243     SCDynamicStoreRef store) {
244   // Called on notifier thread.
245
246   CFStringRef proxies_key = SCDynamicStoreKeyCreateProxies(NULL);
247   CFArrayRef key_array = CFArrayCreate(
248       NULL, (const void **)(&proxies_key), 1, &kCFTypeArrayCallBacks);
249
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);
253
254   CFRelease(key_array);
255   CFRelease(proxies_key);
256 }
257
258 void ProxyConfigServiceMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
259   // Called on notifier thread.
260
261   // Fetch the new system proxy configuration.
262   ProxyConfig new_config;
263   GetCurrentProxyConfig(&new_config);
264
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));
269 }
270
271 void ProxyConfigServiceMac::OnProxyConfigChanged(
272     const ProxyConfig& new_config) {
273   DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
274
275   // Keep track of the last value we have seen.
276   has_fetched_config_ = true;
277   last_config_fetched_ = new_config;
278
279   // Notify all the observers.
280   FOR_EACH_OBSERVER(Observer, observers_,
281                     OnProxyConfigChanged(new_config, CONFIG_VALID));
282 }
283
284 }  // namespace net