Home | History | Annotate | Download | only in cloud
      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 "chrome/browser/policy/cloud/device_management_service.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/sys_info.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/net/basic_http_user_agent_settings.h"
     17 #include "chrome/browser/net/chrome_net_log.h"
     18 #include "chrome/common/chrome_version_info.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/common/content_client.h"
     21 #include "net/base/escape.h"
     22 #include "net/base/load_flags.h"
     23 #include "net/base/net_errors.h"
     24 #include "net/cookies/cookie_monster.h"
     25 #include "net/dns/host_resolver.h"
     26 #include "net/http/http_network_layer.h"
     27 #include "net/http/http_response_headers.h"
     28 #include "net/proxy/proxy_service.h"
     29 #include "net/ssl/ssl_config_service_defaults.h"
     30 #include "net/url_request/url_fetcher.h"
     31 #include "net/url_request/url_request_context.h"
     32 #include "net/url_request/url_request_context_getter.h"
     33 #include "net/url_request/url_request_status.h"
     34 #include "url/gurl.h"
     35 
     36 #if defined(OS_CHROMEOS)
     37 #include "chrome/browser/chromeos/system/statistics_provider.h"
     38 #endif
     39 
     40 using content::BrowserThread;
     41 
     42 namespace em = enterprise_management;
     43 
     44 namespace policy {
     45 
     46 namespace {
     47 
     48 const char kValueAgent[] = "%s %s(%s)";
     49 const char kValuePlatform[] = "%s|%s|%s";
     50 
     51 const char kPostContentType[] = "application/protobuf";
     52 
     53 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
     54 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
     55 
     56 // Number of times to retry on ERR_NETWORK_CHANGED errors.
     57 const int kMaxNetworkChangedRetries = 3;
     58 
     59 // HTTP Error Codes of the DM Server with their concrete meanings in the context
     60 // of the DM Server communication.
     61 const int kSuccess = 200;
     62 const int kInvalidArgument = 400;
     63 const int kInvalidAuthCookieOrDMToken = 401;
     64 const int kMissingLicenses = 402;
     65 const int kDeviceManagementNotAllowed = 403;
     66 const int kInvalidURL = 404;  // This error is not coming from the GFE.
     67 const int kInvalidSerialNumber = 405;
     68 const int kDeviceIdConflict = 409;
     69 const int kDeviceNotFound = 410;
     70 const int kPendingApproval = 412;
     71 const int kInternalServerError = 500;
     72 const int kServiceUnavailable = 503;
     73 const int kPolicyNotFound = 902;  // This error is not sent as HTTP status code.
     74 
     75 #if defined(OS_CHROMEOS)
     76 // Machine info keys.
     77 const char kMachineInfoHWClass[] = "hardware_class";
     78 const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
     79 #endif
     80 
     81 bool IsProxyError(const net::URLRequestStatus status) {
     82   switch (status.error()) {
     83     case net::ERR_PROXY_CONNECTION_FAILED:
     84     case net::ERR_TUNNEL_CONNECTION_FAILED:
     85     case net::ERR_PROXY_AUTH_UNSUPPORTED:
     86     case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
     87     case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
     88     case net::ERR_PROXY_CERTIFICATE_INVALID:
     89     case net::ERR_SOCKS_CONNECTION_FAILED:
     90     case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
     91       return true;
     92   }
     93   return false;
     94 }
     95 
     96 bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
     97   return fetcher->GetResponseHeaders()->HasHeaderValue(
     98       "content-type", "application/x-protobuffer");
     99 }
    100 
    101 bool FailedWithProxy(const net::URLFetcher* fetcher) {
    102   if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
    103     // The request didn't use a proxy.
    104     return false;
    105   }
    106 
    107   if (!fetcher->GetStatus().is_success() &&
    108       IsProxyError(fetcher->GetStatus())) {
    109     LOG(WARNING) << "Proxy failed while contacting dmserver.";
    110     return true;
    111   }
    112 
    113   if (fetcher->GetStatus().is_success() &&
    114       fetcher->GetResponseCode() == kSuccess &&
    115       fetcher->WasFetchedViaProxy() &&
    116       !IsProtobufMimeType(fetcher)) {
    117     // The proxy server can be misconfigured but pointing to an existing
    118     // server that replies to requests. Try to recover if a successful
    119     // request that went through a proxy returns an unexpected mime type.
    120     LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
    121                  << "fetched via a proxy.";
    122     return true;
    123   }
    124 
    125   return false;
    126 }
    127 
    128 const char* UserAffiliationToString(UserAffiliation affiliation) {
    129   switch (affiliation) {
    130     case USER_AFFILIATION_MANAGED:
    131       return dm_protocol::kValueUserAffiliationManaged;
    132     case USER_AFFILIATION_NONE:
    133       return dm_protocol::kValueUserAffiliationNone;
    134   }
    135   NOTREACHED() << "Invalid user affiliation " << affiliation;
    136   return dm_protocol::kValueUserAffiliationNone;
    137 }
    138 
    139 const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
    140   switch (type) {
    141     case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT:
    142       return dm_protocol::kValueRequestAutoEnrollment;
    143     case DeviceManagementRequestJob::TYPE_REGISTRATION:
    144       return dm_protocol::kValueRequestRegister;
    145     case DeviceManagementRequestJob::TYPE_POLICY_FETCH:
    146       return dm_protocol::kValueRequestPolicy;
    147     case DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH:
    148       return dm_protocol::kValueRequestApiAuthorization;
    149     case DeviceManagementRequestJob::TYPE_UNREGISTRATION:
    150       return dm_protocol::kValueRequestUnregister;
    151     case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE:
    152       return dm_protocol::kValueRequestUploadCertificate;
    153   }
    154   NOTREACHED() << "Invalid job type " << type;
    155   return "";
    156 }
    157 
    158 const std::string& GetAgentString() {
    159   CR_DEFINE_STATIC_LOCAL(std::string, agent, ());
    160   if (!agent.empty())
    161     return agent;
    162 
    163   chrome::VersionInfo version_info;
    164   agent = base::StringPrintf(kValueAgent,
    165                              version_info.Name().c_str(),
    166                              version_info.Version().c_str(),
    167                              version_info.LastChange().c_str());
    168   return agent;
    169 }
    170 
    171 const std::string& GetPlatformString() {
    172   CR_DEFINE_STATIC_LOCAL(std::string, platform, ());
    173   if (!platform.empty())
    174     return platform;
    175 
    176   std::string os_name(base::SysInfo::OperatingSystemName());
    177   std::string os_hardware(base::SysInfo::OperatingSystemArchitecture());
    178 
    179 #if defined(OS_CHROMEOS)
    180   chromeos::system::StatisticsProvider* provider =
    181       chromeos::system::StatisticsProvider::GetInstance();
    182 
    183   std::string hwclass;
    184   std::string board;
    185   if (!provider->GetMachineStatistic(kMachineInfoHWClass, &hwclass) ||
    186       !provider->GetMachineStatistic(kMachineInfoBoard, &board)) {
    187     LOG(ERROR) << "Failed to get machine information";
    188   }
    189   os_name += ",CrOS," + board;
    190   os_hardware += "," + hwclass;
    191 #endif
    192 
    193   std::string os_version("-");
    194 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
    195   int32 os_major_version = 0;
    196   int32 os_minor_version = 0;
    197   int32 os_bugfix_version = 0;
    198   base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
    199                                                &os_minor_version,
    200                                                &os_bugfix_version);
    201   os_version = base::StringPrintf("%d.%d.%d",
    202                                   os_major_version,
    203                                   os_minor_version,
    204                                   os_bugfix_version);
    205 #endif
    206 
    207   platform = base::StringPrintf(kValuePlatform,
    208                                 os_name.c_str(),
    209                                 os_hardware.c_str(),
    210                                 os_version.c_str());
    211   return platform;
    212 }
    213 
    214 // Custom request context implementation that allows to override the user agent,
    215 // amongst others. Wraps a baseline request context from which we reuse the
    216 // networking components.
    217 class DeviceManagementRequestContext : public net::URLRequestContext {
    218  public:
    219   explicit DeviceManagementRequestContext(net::URLRequestContext* base_context);
    220   virtual ~DeviceManagementRequestContext();
    221 
    222  private:
    223   BasicHttpUserAgentSettings basic_http_user_agent_settings_;
    224 };
    225 
    226 DeviceManagementRequestContext::DeviceManagementRequestContext(
    227     net::URLRequestContext* base_context)
    228     // Use sane Accept-Language value for our purposes.
    229     : basic_http_user_agent_settings_("*") {
    230   // Share resolver, proxy service and ssl bits with the baseline context. This
    231   // is important so we don't make redundant requests (e.g. when resolving proxy
    232   // auto configuration).
    233   set_net_log(base_context->net_log());
    234   set_host_resolver(base_context->host_resolver());
    235   set_proxy_service(base_context->proxy_service());
    236   set_ssl_config_service(base_context->ssl_config_service());
    237 
    238   // Share the http session.
    239   set_http_transaction_factory(
    240       new net::HttpNetworkLayer(
    241           base_context->http_transaction_factory()->GetSession()));
    242 
    243   // No cookies, please.
    244   set_cookie_store(new net::CookieMonster(NULL, NULL));
    245 
    246   set_http_user_agent_settings(&basic_http_user_agent_settings_);
    247 }
    248 
    249 DeviceManagementRequestContext::~DeviceManagementRequestContext() {
    250   delete http_transaction_factory();
    251 }
    252 
    253 // Request context holder.
    254 class DeviceManagementRequestContextGetter
    255     : public net::URLRequestContextGetter {
    256  public:
    257   explicit DeviceManagementRequestContextGetter(
    258       net::URLRequestContextGetter* base_context_getter)
    259       : base_context_getter_(base_context_getter) {}
    260 
    261   // Overridden from net::URLRequestContextGetter:
    262   virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
    263   virtual scoped_refptr<base::SingleThreadTaskRunner>
    264       GetNetworkTaskRunner() const OVERRIDE;
    265 
    266  protected:
    267   virtual ~DeviceManagementRequestContextGetter() {}
    268 
    269  private:
    270   scoped_ptr<net::URLRequestContext> context_;
    271   scoped_refptr<net::URLRequestContextGetter> base_context_getter_;
    272 };
    273 
    274 
    275 net::URLRequestContext*
    276 DeviceManagementRequestContextGetter::GetURLRequestContext() {
    277   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    278   if (!context_.get()) {
    279     context_.reset(new DeviceManagementRequestContext(
    280         base_context_getter_->GetURLRequestContext()));
    281   }
    282 
    283   return context_.get();
    284 }
    285 
    286 scoped_refptr<base::SingleThreadTaskRunner>
    287 DeviceManagementRequestContextGetter::GetNetworkTaskRunner() const {
    288   return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
    289 }
    290 
    291 }  // namespace
    292 
    293 // Request job implementation used with DeviceManagementService.
    294 class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
    295  public:
    296   DeviceManagementRequestJobImpl(JobType type,
    297                                  DeviceManagementService* service);
    298   virtual ~DeviceManagementRequestJobImpl();
    299 
    300   // Handles the URL request response.
    301   void HandleResponse(const net::URLRequestStatus& status,
    302                       int response_code,
    303                       const net::ResponseCookies& cookies,
    304                       const std::string& data);
    305 
    306   // Gets the URL to contact.
    307   GURL GetURL(const std::string& server_url);
    308 
    309   // Configures the fetcher, setting up payload and headers.
    310   void ConfigureRequest(net::URLFetcher* fetcher);
    311 
    312   // Returns true if this job should be retried. |fetcher| has just completed,
    313   // and can be inspected to determine if the request failed and should be
    314   // retried.
    315   bool ShouldRetry(const net::URLFetcher* fetcher);
    316 
    317   // Invoked right before retrying this job.
    318   void PrepareRetry();
    319 
    320  protected:
    321   // DeviceManagementRequestJob:
    322   virtual void Run() OVERRIDE;
    323 
    324  private:
    325   // Invokes the callback with the given error code.
    326   void ReportError(DeviceManagementStatus code);
    327 
    328   // Pointer to the service this job is associated with.
    329   DeviceManagementService* service_;
    330 
    331   // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
    332   bool bypass_proxy_;
    333 
    334   // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
    335   int retries_count_;
    336 
    337   DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
    338 };
    339 
    340 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
    341     JobType type,
    342     DeviceManagementService* service)
    343     : DeviceManagementRequestJob(type),
    344       service_(service),
    345       bypass_proxy_(false),
    346       retries_count_(0) {}
    347 
    348 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
    349   service_->RemoveJob(this);
    350 }
    351 
    352 void DeviceManagementRequestJobImpl::Run() {
    353   service_->AddJob(this);
    354 }
    355 
    356 void DeviceManagementRequestJobImpl::HandleResponse(
    357     const net::URLRequestStatus& status,
    358     int response_code,
    359     const net::ResponseCookies& cookies,
    360     const std::string& data) {
    361   if (status.status() != net::URLRequestStatus::SUCCESS) {
    362     LOG(WARNING) << "DMServer request failed, status: " << status.status()
    363                  << ", error: " << status.error();
    364     em::DeviceManagementResponse dummy_response;
    365     callback_.Run(DM_STATUS_REQUEST_FAILED, status.error(), dummy_response);
    366     return;
    367   }
    368 
    369   if (response_code != kSuccess)
    370     LOG(WARNING) << "DMServer sent an error response: " << response_code;
    371 
    372   switch (response_code) {
    373     case kSuccess: {
    374       em::DeviceManagementResponse response;
    375       if (!response.ParseFromString(data)) {
    376         ReportError(DM_STATUS_RESPONSE_DECODING_ERROR);
    377         return;
    378       }
    379       callback_.Run(DM_STATUS_SUCCESS, net::OK, response);
    380       return;
    381     }
    382     case kInvalidArgument:
    383       ReportError(DM_STATUS_REQUEST_INVALID);
    384       return;
    385     case kInvalidAuthCookieOrDMToken:
    386       ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
    387       return;
    388     case kMissingLicenses:
    389       ReportError(DM_STATUS_SERVICE_MISSING_LICENSES);
    390       return;
    391     case kDeviceManagementNotAllowed:
    392       ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
    393       return;
    394     case kPendingApproval:
    395       ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING);
    396       return;
    397     case kInvalidURL:
    398     case kInternalServerError:
    399     case kServiceUnavailable:
    400       ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
    401       return;
    402     case kDeviceNotFound:
    403       ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
    404       return;
    405     case kPolicyNotFound:
    406       ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND);
    407       return;
    408     case kInvalidSerialNumber:
    409       ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
    410       return;
    411     case kDeviceIdConflict:
    412       ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT);
    413       return;
    414     default:
    415       // Handle all unknown 5xx HTTP error codes as temporary and any other
    416       // unknown error as one that needs more time to recover.
    417       if (response_code >= 500 && response_code <= 599)
    418         ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
    419       else
    420         ReportError(DM_STATUS_HTTP_STATUS_ERROR);
    421       return;
    422   }
    423 }
    424 
    425 GURL DeviceManagementRequestJobImpl::GetURL(
    426     const std::string& server_url) {
    427   std::string result(server_url);
    428   result += '?';
    429   for (ParameterMap::const_iterator entry(query_params_.begin());
    430        entry != query_params_.end();
    431        ++entry) {
    432     if (entry != query_params_.begin())
    433       result += '&';
    434     result += net::EscapeQueryParamValue(entry->first, true);
    435     result += '=';
    436     result += net::EscapeQueryParamValue(entry->second, true);
    437   }
    438   return GURL(result);
    439 }
    440 
    441 void DeviceManagementRequestJobImpl::ConfigureRequest(
    442     net::URLFetcher* fetcher) {
    443   fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
    444                         net::LOAD_DO_NOT_SAVE_COOKIES |
    445                         net::LOAD_DISABLE_CACHE |
    446                         (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
    447   std::string payload;
    448   CHECK(request_.SerializeToString(&payload));
    449   fetcher->SetUploadData(kPostContentType, payload);
    450   std::string extra_headers;
    451   if (!gaia_token_.empty())
    452     extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
    453   if (!dm_token_.empty())
    454     extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
    455   fetcher->SetExtraRequestHeaders(extra_headers);
    456 }
    457 
    458 bool DeviceManagementRequestJobImpl::ShouldRetry(
    459     const net::URLFetcher* fetcher) {
    460   if (FailedWithProxy(fetcher) && !bypass_proxy_) {
    461     // Retry the job if it failed due to a broken proxy, by bypassing the
    462     // proxy on the next try.
    463     bypass_proxy_ = true;
    464     return true;
    465   }
    466 
    467   // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
    468   // often interrupted during ChromeOS startup when network change notifications
    469   // are sent. Allowing the fetcher to retry once after that is enough to
    470   // recover; allow it to retry up to 3 times just in case.
    471   if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
    472       retries_count_ < kMaxNetworkChangedRetries) {
    473     ++retries_count_;
    474     return true;
    475   }
    476 
    477   // The request didn't fail, or the limit of retry attempts has been reached;
    478   // forward the result to the job owner.
    479   return false;
    480 }
    481 
    482 void DeviceManagementRequestJobImpl::PrepareRetry() {
    483   if (!retry_callback_.is_null())
    484     retry_callback_.Run(this);
    485 }
    486 
    487 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
    488   em::DeviceManagementResponse dummy_response;
    489   callback_.Run(code, net::OK, dummy_response);
    490 }
    491 
    492 DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
    493 
    494 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
    495   gaia_token_ = gaia_token;
    496 }
    497 
    498 void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) {
    499   AddParameter(dm_protocol::kParamOAuthToken, oauth_token);
    500 }
    501 
    502 void DeviceManagementRequestJob::SetUserAffiliation(
    503     UserAffiliation user_affiliation) {
    504   AddParameter(dm_protocol::kParamUserAffiliation,
    505                UserAffiliationToString(user_affiliation));
    506 }
    507 
    508 void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) {
    509   dm_token_ = dm_token;
    510 }
    511 
    512 void DeviceManagementRequestJob::SetClientID(const std::string& client_id) {
    513   AddParameter(dm_protocol::kParamDeviceID, client_id);
    514 }
    515 
    516 em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() {
    517   return &request_;
    518 }
    519 
    520 DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) {
    521   AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
    522   AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
    523   AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
    524   AddParameter(dm_protocol::kParamAgent, GetAgentString());
    525   AddParameter(dm_protocol::kParamPlatform, GetPlatformString());
    526 }
    527 
    528 void DeviceManagementRequestJob::SetRetryCallback(
    529     const RetryCallback& retry_callback) {
    530   retry_callback_ = retry_callback;
    531 }
    532 
    533 void DeviceManagementRequestJob::Start(const Callback& callback) {
    534   callback_ = callback;
    535   Run();
    536 }
    537 
    538 void DeviceManagementRequestJob::AddParameter(const std::string& name,
    539                                               const std::string& value) {
    540   query_params_.push_back(std::make_pair(name, value));
    541 }
    542 
    543 // A random value that other fetchers won't likely use.
    544 const int DeviceManagementService::kURLFetcherID = 0xde71ce1d;
    545 
    546 DeviceManagementService::~DeviceManagementService() {
    547   // All running jobs should have been cancelled by now.
    548   DCHECK(pending_jobs_.empty());
    549   DCHECK(queued_jobs_.empty());
    550 }
    551 
    552 DeviceManagementRequestJob* DeviceManagementService::CreateJob(
    553     DeviceManagementRequestJob::JobType type) {
    554   return new DeviceManagementRequestJobImpl(type, this);
    555 }
    556 
    557 void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) {
    558   if (initialized_)
    559     return;
    560   base::MessageLoop::current()->PostDelayedTask(
    561       FROM_HERE,
    562       base::Bind(&DeviceManagementService::Initialize,
    563                  weak_ptr_factory_.GetWeakPtr()),
    564       base::TimeDelta::FromMilliseconds(delay_milliseconds));
    565 }
    566 
    567 void DeviceManagementService::Initialize() {
    568   if (initialized_)
    569     return;
    570   DCHECK(!request_context_getter_.get());
    571   request_context_getter_ = new DeviceManagementRequestContextGetter(
    572       g_browser_process->system_request_context());
    573   initialized_ = true;
    574 
    575   while (!queued_jobs_.empty()) {
    576     StartJob(queued_jobs_.front());
    577     queued_jobs_.pop_front();
    578   }
    579 }
    580 
    581 void DeviceManagementService::Shutdown() {
    582   for (JobFetcherMap::iterator job(pending_jobs_.begin());
    583        job != pending_jobs_.end();
    584        ++job) {
    585     delete job->first;
    586     queued_jobs_.push_back(job->second);
    587   }
    588   pending_jobs_.clear();
    589 }
    590 
    591 DeviceManagementService::DeviceManagementService(
    592     const std::string& server_url)
    593     : server_url_(server_url),
    594       initialized_(false),
    595       weak_ptr_factory_(this) {
    596 }
    597 
    598 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
    599   net::URLFetcher* fetcher = net::URLFetcher::Create(
    600       kURLFetcherID, job->GetURL(server_url_), net::URLFetcher::POST, this);
    601   fetcher->SetRequestContext(request_context_getter_.get());
    602   job->ConfigureRequest(fetcher);
    603   pending_jobs_[fetcher] = job;
    604   fetcher->Start();
    605 }
    606 
    607 void DeviceManagementService::OnURLFetchComplete(
    608     const net::URLFetcher* source) {
    609   JobFetcherMap::iterator entry(pending_jobs_.find(source));
    610   if (entry == pending_jobs_.end()) {
    611     NOTREACHED() << "Callback from foreign URL fetcher";
    612     return;
    613   }
    614 
    615   DeviceManagementRequestJobImpl* job = entry->second;
    616   pending_jobs_.erase(entry);
    617 
    618   if (job->ShouldRetry(source)) {
    619     VLOG(1) << "Retrying dmserver request.";
    620     job->PrepareRetry();
    621     StartJob(job);
    622   } else {
    623     std::string data;
    624     source->GetResponseAsString(&data);
    625     job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
    626                         source->GetCookies(), data);
    627   }
    628   delete source;
    629 }
    630 
    631 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
    632   if (initialized_)
    633     StartJob(job);
    634   else
    635     queued_jobs_.push_back(job);
    636 }
    637 
    638 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
    639   for (JobFetcherMap::iterator entry(pending_jobs_.begin());
    640        entry != pending_jobs_.end();
    641        ++entry) {
    642     if (entry->second == job) {
    643       delete entry->first;
    644       pending_jobs_.erase(entry);
    645       return;
    646     }
    647   }
    648 
    649   const JobQueue::iterator elem =
    650       std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
    651   if (elem != queued_jobs_.end())
    652     queued_jobs_.erase(elem);
    653 }
    654 
    655 }  // namespace policy
    656