15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "components/policy/core/common/cloud/device_management_service.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <utility>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/message_loop/message_loop_proxy.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_response_headers.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace em = enterprise_management;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace policy {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPostContentType[] = "application/protobuf";
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Number of times to retry on ERR_NETWORK_CHANGED errors.
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kMaxNetworkChangedRetries = 3;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// HTTP Error Codes of the DM Server with their concrete meanings in the context
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of the DM Server communication.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kSuccess = 200;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInvalidArgument = 400;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInvalidAuthCookieOrDMToken = 401;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMissingLicenses = 402;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDeviceManagementNotAllowed = 403;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInvalidURL = 404;  // This error is not coming from the GFE.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInvalidSerialNumber = 405;
44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst int kDomainMismatch = 406;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDeviceIdConflict = 409;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDeviceNotFound = 410;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kPendingApproval = 412;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInternalServerError = 500;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kServiceUnavailable = 503;
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kPolicyNotFound = 902;
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kDeprovisioned = 903;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsProxyError(const net::URLRequestStatus status) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (status.error()) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_PROXY_CONNECTION_FAILED:
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_TUNNEL_CONNECTION_FAILED:
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_PROXY_AUTH_UNSUPPORTED:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_PROXY_CERTIFICATE_INVALID:
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_SOCKS_CONNECTION_FAILED:
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return fetcher->GetResponseHeaders()->HasHeaderValue(
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "content-type", "application/x-protobuffer");
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool FailedWithProxy(const net::URLFetcher* fetcher) {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The request didn't use a proxy.
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!fetcher->GetStatus().is_success() &&
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      IsProxyError(fetcher->GetStatus())) {
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(WARNING) << "Proxy failed while contacting dmserver.";
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (fetcher->GetStatus().is_success() &&
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fetcher->GetResponseCode() == kSuccess &&
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fetcher->WasFetchedViaProxy() &&
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !IsProtobufMimeType(fetcher)) {
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The proxy server can be misconfigured but pointing to an existing
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // server that replies to requests. Try to recover if a successful
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // request that went through a proxy returns an unexpected mime type.
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 << "fetched via a proxy.";
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* UserAffiliationToString(UserAffiliation affiliation) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (affiliation) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case USER_AFFILIATION_MANAGED:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueUserAffiliationManaged;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case USER_AFFILIATION_NONE:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueUserAffiliationNone;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED() << "Invalid user affiliation " << affiliation;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dm_protocol::kValueUserAffiliationNone;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueRequestAutoEnrollment;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DeviceManagementRequestJob::TYPE_REGISTRATION:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueRequestRegister;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DeviceManagementRequestJob::TYPE_POLICY_FETCH:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueRequestPolicy;
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH:
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return dm_protocol::kValueRequestApiAuthorization;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DeviceManagementRequestJob::TYPE_UNREGISTRATION:
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return dm_protocol::kValueRequestUnregister;
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE:
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return dm_protocol::kValueRequestUploadCertificate;
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL:
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return dm_protocol::kValueRequestDeviceStateRetrieval;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED() << "Invalid job type " << type;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return "";
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Request job implementation used with DeviceManagementService.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DeviceManagementRequestJobImpl(
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      JobType type,
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      const std::string& agent_parameter,
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      const std::string& platform_parameter,
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DeviceManagementService* service,
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      net::URLRequestContextGetter* request_context);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~DeviceManagementRequestJobImpl();
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Handles the URL request response.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void HandleResponse(const net::URLRequestStatus& status,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int response_code,
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      const net::ResponseCookies& cookies,
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      const std::string& data);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Gets the URL to contact.
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL GetURL(const std::string& server_url);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Configures the fetcher, setting up payload and headers.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ConfigureRequest(net::URLFetcher* fetcher);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Returns true if this job should be retried. |fetcher| has just completed,
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // and can be inspected to determine if the request failed and should be
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // retried.
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool ShouldRetry(const net::URLFetcher* fetcher);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Invoked right before retrying this job.
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void PrepareRetry();
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // DeviceManagementRequestJob:
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void Run() OVERRIDE;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Invokes the callback with the given error code.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ReportError(DeviceManagementStatus code);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pointer to the service this job is associated with.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeviceManagementService* service_;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool bypass_proxy_;
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int retries_count_;
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The request context to use for this job.
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  net::URLRequestContextGetter* request_context_;
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    JobType type,
1904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& agent_parameter,
1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& platform_parameter,
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DeviceManagementService* service,
193a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    net::URLRequestContextGetter* request_context)
1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : DeviceManagementRequestJob(type, agent_parameter, platform_parameter),
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      service_(service),
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bypass_proxy_(false),
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      retries_count_(0),
198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      request_context_(request_context) {}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->RemoveJob(this);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJobImpl::Run() {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->AddJob(this);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJobImpl::HandleResponse(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLRequestStatus& status,
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int response_code,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::ResponseCookies& cookies,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& data) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status.status() != net::URLRequestStatus::SUCCESS) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "DMServer request failed, status: " << status.status()
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << ", error: " << status.error();
216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    em::DeviceManagementResponse dummy_response;
217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    callback_.Run(DM_STATUS_REQUEST_FAILED, status.error(), dummy_response);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code != kSuccess)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "DMServer sent an error response: " << response_code;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (response_code) {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSuccess: {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      em::DeviceManagementResponse response;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!response.ParseFromString(data)) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ReportError(DM_STATUS_RESPONSE_DECODING_ERROR);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
231b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      callback_.Run(DM_STATUS_SUCCESS, net::OK, response);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kInvalidArgument:
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_REQUEST_INVALID);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kInvalidAuthCookieOrDMToken:
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kMissingLicenses:
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_MISSING_LICENSES);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kDeviceManagementNotAllowed:
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kPendingApproval:
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING);
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kInvalidURL:
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kInternalServerError:
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kServiceUnavailable:
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kDeviceNotFound:
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kPolicyNotFound:
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kInvalidSerialNumber:
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
263effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    case kDomainMismatch:
264effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      ReportError(DM_STATUS_SERVICE_DOMAIN_MISMATCH);
265effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      return;
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case kDeprovisioned:
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_DEPROVISIONED);
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kDeviceIdConflict:
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Handle all unknown 5xx HTTP error codes as temporary and any other
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // unknown error as one that needs more time to recover.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response_code >= 500 && response_code <= 599)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ReportError(DM_STATUS_HTTP_STATUS_ERROR);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL DeviceManagementRequestJobImpl::GetURL(
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& server_url) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string result(server_url);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result += '?';
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ParameterMap::const_iterator entry(query_params_.begin());
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       entry != query_params_.end();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++entry) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (entry != query_params_.begin())
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result += '&';
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result += net::EscapeQueryParamValue(entry->first, true);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result += '=';
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result += net::EscapeQueryParamValue(entry->second, true);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GURL(result);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJobImpl::ConfigureRequest(
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLFetcher* fetcher) {
301a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  fetcher->SetRequestContext(request_context_);
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        net::LOAD_DO_NOT_SAVE_COOKIES |
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        net::LOAD_DISABLE_CACHE |
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string payload;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(request_.SerializeToString(&payload));
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher->SetUploadData(kPostContentType, payload);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string extra_headers;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!gaia_token_.empty())
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!dm_token_.empty())
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher->SetExtraRequestHeaders(extra_headers);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool DeviceManagementRequestJobImpl::ShouldRetry(
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const net::URLFetcher* fetcher) {
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FailedWithProxy(fetcher) && !bypass_proxy_) {
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Retry the job if it failed due to a broken proxy, by bypassing the
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // proxy on the next try.
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bypass_proxy_ = true;
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // often interrupted during ChromeOS startup when network change notifications
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are sent. Allowing the fetcher to retry once after that is enough to
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // recover; allow it to retry up to 3 times just in case.
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      retries_count_ < kMaxNetworkChangedRetries) {
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ++retries_count_;
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The request didn't fail, or the limit of retry attempts has been reached;
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // forward the result to the job owner.
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DeviceManagementRequestJobImpl::PrepareRetry() {
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!retry_callback_.is_null())
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    retry_callback_.Run(this);
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  em::DeviceManagementResponse dummy_response;
348b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  callback_.Run(code, net::OK, dummy_response);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gaia_token_ = gaia_token;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) {
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamOAuthToken, oauth_token);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::SetUserAffiliation(
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UserAffiliation user_affiliation) {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamUserAffiliation,
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               UserAffiliationToString(user_affiliation));
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dm_token_ = dm_token;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::SetClientID(const std::string& client_id) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamDeviceID, client_id);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() {
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return &request_;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)DeviceManagementRequestJob::DeviceManagementRequestJob(
3804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    JobType type,
3814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& agent_parameter,
3824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& platform_parameter) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
3864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  AddParameter(dm_protocol::kParamAgent, agent_parameter);
3874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  AddParameter(dm_protocol::kParamPlatform, platform_parameter);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DeviceManagementRequestJob::SetRetryCallback(
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const RetryCallback& retry_callback) {
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  retry_callback_ = retry_callback;
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::Start(const Callback& callback) {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callback_ = callback;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Run();
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementRequestJob::AddParameter(const std::string& name,
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              const std::string& value) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  query_params_.push_back(std::make_pair(name, value));
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A random value that other fetchers won't likely use.
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int DeviceManagementService::kURLFetcherID = 0xde71ce1d;
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementService::~DeviceManagementService() {
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // All running jobs should have been cancelled by now.
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(pending_jobs_.empty());
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(queued_jobs_.empty());
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementRequestJob* DeviceManagementService::CreateJob(
415a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DeviceManagementRequestJob::JobType type,
416a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    net::URLRequestContextGetter* request_context) {
4174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new DeviceManagementRequestJobImpl(
4184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      type,
4194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      configuration_->GetAgentParameter(),
4204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      configuration_->GetPlatformParameter(),
421a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      this,
422a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      request_context);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (initialized_)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
42890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&DeviceManagementService::Initialize,
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()),
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::TimeDelta::FromMilliseconds(delay_milliseconds));
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::Initialize() {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (initialized_)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  initialized_ = true;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!queued_jobs_.empty()) {
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    StartJob(queued_jobs_.front());
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    queued_jobs_.pop_front();
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::Shutdown() {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (JobFetcherMap::iterator job(pending_jobs_.begin());
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       job != pending_jobs_.end();
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++job) {
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete job->first;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    queued_jobs_.push_back(job->second);
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_jobs_.clear();
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DeviceManagementService::DeviceManagementService(
457a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_ptr<Configuration> configuration)
4584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : configuration_(configuration.Pass()),
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      initialized_(false),
460c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
4614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DCHECK(configuration_);
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
465a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string server_url = GetServerUrl();
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net::URLFetcher* fetcher = net::URLFetcher::Create(
4674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      kURLFetcherID, job->GetURL(server_url), net::URLFetcher::POST, this);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  job->ConfigureRequest(fetcher);
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_jobs_[fetcher] = job;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher->Start();
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
473a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)std::string DeviceManagementService::GetServerUrl() {
474a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return configuration_->GetServerUrl();
475a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
476a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::OnURLFetchComplete(
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JobFetcherMap::iterator entry(pending_jobs_.find(source));
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (entry == pending_jobs_.end()) {
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Callback from foreign URL fetcher";
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeviceManagementRequestJobImpl* job = entry->second;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_jobs_.erase(entry);
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (job->ShouldRetry(source)) {
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    VLOG(1) << "Retrying dmserver request.";
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    job->PrepareRetry();
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    StartJob(job);
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string data;
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    source->GetResponseAsString(&data);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        source->GetCookies(), data);
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete source;
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (initialized_)
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    StartJob(job);
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    queued_jobs_.push_back(job);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (JobFetcherMap::iterator entry(pending_jobs_.begin());
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       entry != pending_jobs_.end();
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++entry) {
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (entry->second == job) {
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete entry->first;
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pending_jobs_.erase(entry);
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const JobQueue::iterator elem =
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (elem != queued_jobs_.end())
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    queued_jobs_.erase(elem);
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace policy
526