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