1// Copyright (c) 2011 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 "chrome/browser/policy/device_management_service.h"
6
7#include "chrome/browser/io_thread.h"
8#include "chrome/browser/net/chrome_net_log.h"
9#include "chrome/browser/policy/device_management_backend_impl.h"
10#include "content/browser/browser_thread.h"
11#include "net/base/cookie_monster.h"
12#include "net/base/host_resolver.h"
13#include "net/base/load_flags.h"
14#include "net/base/ssl_config_service_defaults.h"
15#include "net/http/http_auth_handler_factory.h"
16#include "net/http/http_network_layer.h"
17#include "net/proxy/proxy_service.h"
18#include "net/url_request/url_request_context.h"
19#include "net/url_request/url_request_context_getter.h"
20#include "net/url_request/url_request_status.h"
21#include "webkit/glue/webkit_glue.h"
22
23namespace policy {
24
25namespace {
26
27// Custom request context implementation that allows to override the user agent,
28// amongst others. Wraps a baseline request context from which we reuse the
29// networking components.
30class DeviceManagementRequestContext : public net::URLRequestContext {
31 public:
32  explicit DeviceManagementRequestContext(net::URLRequestContext* base_context);
33  virtual ~DeviceManagementRequestContext();
34
35 private:
36  // Overridden from net::URLRequestContext:
37  virtual const std::string& GetUserAgent(const GURL& url) const;
38};
39
40DeviceManagementRequestContext::DeviceManagementRequestContext(
41    net::URLRequestContext* base_context) {
42  // Share resolver, proxy service and ssl bits with the baseline context. This
43  // is important so we don't make redundant requests (e.g. when resolving proxy
44  // auto configuration).
45  set_net_log(base_context->net_log());
46  set_host_resolver(base_context->host_resolver());
47  set_proxy_service(base_context->proxy_service());
48  set_ssl_config_service(base_context->ssl_config_service());
49
50  // Share the http session.
51  set_http_transaction_factory(
52      new net::HttpNetworkLayer(
53          base_context->http_transaction_factory()->GetSession()));
54
55  // No cookies, please.
56  set_cookie_store(new net::CookieMonster(NULL, NULL));
57
58  // Initialize these to sane values for our purposes.
59  set_accept_language("*");
60  set_accept_charset("*");
61}
62
63DeviceManagementRequestContext::~DeviceManagementRequestContext() {
64  delete http_transaction_factory();
65}
66
67const std::string& DeviceManagementRequestContext::GetUserAgent(
68    const GURL& url) const {
69  return webkit_glue::GetUserAgent(url);
70}
71
72// Request context holder.
73class DeviceManagementRequestContextGetter
74    : public net::URLRequestContextGetter {
75 public:
76  DeviceManagementRequestContextGetter(
77      net::URLRequestContextGetter* base_context_getter)
78      : base_context_getter_(base_context_getter) {}
79
80  // Overridden from net::URLRequestContextGetter:
81  virtual net::URLRequestContext* GetURLRequestContext();
82  virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const;
83
84 private:
85  scoped_refptr<net::URLRequestContext> context_;
86  scoped_refptr<net::URLRequestContextGetter> base_context_getter_;
87};
88
89
90net::URLRequestContext*
91DeviceManagementRequestContextGetter::GetURLRequestContext() {
92  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
93  if (!context_) {
94    context_ = new DeviceManagementRequestContext(
95        base_context_getter_->GetURLRequestContext());
96  }
97
98  return context_.get();
99}
100
101scoped_refptr<base::MessageLoopProxy>
102DeviceManagementRequestContextGetter::GetIOMessageLoopProxy() const {
103  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
104}
105
106}  // namespace
107
108DeviceManagementService::~DeviceManagementService() {
109  // All running jobs should have been canceled by now. If not, there are
110  // backend objects still around, which is an error.
111  DCHECK(pending_jobs_.empty());
112  DCHECK(queued_jobs_.empty());
113}
114
115DeviceManagementBackend* DeviceManagementService::CreateBackend() {
116  return new DeviceManagementBackendImpl(this);
117}
118
119void DeviceManagementService::Initialize(
120    net::URLRequestContextGetter* request_context_getter) {
121  DCHECK(!request_context_getter_);
122  request_context_getter_ =
123      new DeviceManagementRequestContextGetter(request_context_getter);
124  while (!queued_jobs_.empty()) {
125    StartJob(queued_jobs_.front());
126    queued_jobs_.pop_front();
127  }
128}
129
130void DeviceManagementService::Shutdown() {
131  for (JobFetcherMap::iterator job(pending_jobs_.begin());
132       job != pending_jobs_.end();
133       ++job) {
134    delete job->first;
135    queued_jobs_.push_back(job->second);
136  }
137  pending_jobs_.clear();
138}
139
140DeviceManagementService::DeviceManagementService(
141    const std::string& server_url)
142    : server_url_(server_url) {
143}
144
145void DeviceManagementService::AddJob(DeviceManagementJob* job) {
146  if (request_context_getter_.get())
147    StartJob(job);
148  else
149    queued_jobs_.push_back(job);
150}
151
152void DeviceManagementService::RemoveJob(DeviceManagementJob* job) {
153  for (JobFetcherMap::iterator entry(pending_jobs_.begin());
154       entry != pending_jobs_.end();
155       ++entry) {
156    if (entry->second == job) {
157      delete entry->first;
158      pending_jobs_.erase(entry);
159      return;
160    }
161  }
162
163  const JobQueue::iterator elem =
164      std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
165  if (elem != queued_jobs_.end())
166    queued_jobs_.erase(elem);
167}
168
169void DeviceManagementService::StartJob(DeviceManagementJob* job) {
170  URLFetcher* fetcher = URLFetcher::Create(0, job->GetURL(server_url_),
171                                           URLFetcher::POST, this);
172  fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
173                          net::LOAD_DO_NOT_SAVE_COOKIES |
174                          net::LOAD_DISABLE_CACHE);
175  fetcher->set_request_context(request_context_getter_.get());
176  job->ConfigureRequest(fetcher);
177  pending_jobs_[fetcher] = job;
178  fetcher->Start();
179}
180
181void DeviceManagementService::OnURLFetchComplete(
182    const URLFetcher* source,
183    const GURL& url,
184    const net::URLRequestStatus& status,
185    int response_code,
186    const ResponseCookies& cookies,
187    const std::string& data) {
188  JobFetcherMap::iterator entry(pending_jobs_.find(source));
189  if (entry != pending_jobs_.end()) {
190    DeviceManagementJob* job = entry->second;
191    pending_jobs_.erase(entry);
192    job->HandleResponse(status, response_code, cookies, data);
193  } else {
194    NOTREACHED() << "Callback from foreign URL fetcher";
195  }
196  delete source;
197}
198
199}  // namespace policy
200