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