1 // Copyright 2013 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 "cloud_print/gcp20/prototype/printer.h" 6 7 #include <stdio.h> 8 #include <string> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/command_line.h" 13 #include "base/file_util.h" 14 #include "base/guid.h" 15 #include "base/json/json_reader.h" 16 #include "base/json/json_writer.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/stringprintf.h" 19 #include "cloud_print/gcp20/prototype/command_line_reader.h" 20 #include "cloud_print/gcp20/prototype/service_parameters.h" 21 #include "cloud_print/gcp20/prototype/special_io.h" 22 #include "net/base/net_util.h" 23 #include "net/base/url_util.h" 24 25 const char kPrinterStatePathDefault[] = "printer_state.json"; 26 27 namespace { 28 29 const uint16 kHttpPortDefault = 10101; 30 const uint32 kTtlDefault = 60*60; // in seconds 31 const int kXmppPingIntervalDefault = 5*60; // in seconds 32 33 const char kServiceType[] = "_privet._tcp.local"; 34 const char kServiceNamePrefixDefault[] = "first_gcp20_device"; 35 const char kServiceDomainNameDefault[] = "my-privet-device.local"; 36 37 const char kPrinterName[] = "Google GCP2.0 Prototype"; 38 const char kPrinterDescription[] = "Printer emulator"; 39 40 const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you " 41 "agree and any other to discard\n"; 42 const int64 kUserConfirmationTimeout = 30; // in seconds 43 44 const uint32 kReconnectTimeout = 5; // in seconds 45 46 const double kTimeToNextAccessTokenUpdate = 0.8; // relatively to living time. 47 48 const char kCdd[] = 49 "{\n" 50 " 'version': '1.0',\n" 51 " 'printer': {\n" 52 " 'vendor_capability': [\n" 53 " {\n" 54 " 'id': 'psk:MediaType',\n" 55 " 'display_name': 'Media Type',\n" 56 " 'type': 'SELECT',\n" 57 " 'select_cap': {\n" 58 " 'option': [\n" 59 " {\n" 60 " 'value': 'psk:Plain',\n" 61 " 'display_name': 'Plain Paper',\n" 62 " 'is_default': true\n" 63 " },\n" 64 " {\n" 65 " 'value': 'ns0000:Glossy',\n" 66 " 'display_name': 'Glossy Photo',\n" 67 " 'is_default': false\n" 68 " }\n" 69 " ]\n" 70 " }\n" 71 " }\n" 72 " ],\n" 73 " 'reverse_order': { 'default': false }\n" 74 " }\n" 75 "}\n"; 76 77 // Returns local IP address number of first interface found (except loopback). 78 // Return value is empty if no interface found. Possible interfaces names are 79 // "eth0", "wlan0" etc. If interface name is empty, function will return IP 80 // address of first interface found. 81 net::IPAddressNumber GetLocalIp(const std::string& interface_name, 82 bool return_ipv6_number) { 83 net::NetworkInterfaceList interfaces; 84 bool success = net::GetNetworkList(&interfaces); 85 DCHECK(success); 86 87 size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize 88 : net::kIPv4AddressSize; 89 90 for (net::NetworkInterfaceList::iterator iter = interfaces.begin(); 91 iter != interfaces.end(); ++iter) { 92 if (iter->address.size() == expected_address_size && 93 (interface_name.empty() || interface_name == iter->name)) { 94 LOG(INFO) << net::IPAddressToString(iter->address); 95 return iter->address; 96 } 97 } 98 99 return net::IPAddressNumber(); 100 } 101 102 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() { 103 return base::MessageLoop::current()->message_loop_proxy(); 104 } 105 106 } // namespace 107 108 using cloud_print_response_parser::Job; 109 110 Printer::RegistrationInfo::RegistrationInfo() 111 : state(DEV_REG_UNREGISTERED), 112 confirmation_state(CONFIRMATION_PENDING) { 113 } 114 115 Printer::RegistrationInfo::~RegistrationInfo() { 116 } 117 118 Printer::Printer() 119 : http_server_(this), 120 connection_state_(OFFLINE), 121 on_idle_posted_(false), 122 pending_local_settings_check_(false), 123 pending_print_jobs_check_(false) { 124 } 125 126 Printer::~Printer() { 127 Stop(); 128 } 129 130 bool Printer::Start() { 131 if (IsRunning()) 132 return true; 133 134 // TODO(maksymb): Add switch for command line to control interface name. 135 net::IPAddressNumber ip = GetLocalIp("", false); 136 if (ip.empty()) { 137 LOG(ERROR) << "No local IP found. Cannot start printer."; 138 return false; 139 } 140 VLOG(1) << "Local address: " << net::IPAddressToString(ip); 141 142 uint16 port = command_line_reader::ReadHttpPort(kHttpPortDefault); 143 144 // Starting HTTP server. 145 if (!http_server_.Start(port)) 146 return false; 147 148 if (!LoadFromFile()) 149 reg_info_ = RegistrationInfo(); 150 151 // Starting DNS-SD server. 152 std::string service_name_prefix = 153 command_line_reader::ReadServiceNamePrefix(kServiceNamePrefixDefault); 154 std::string service_domain_name = 155 command_line_reader::ReadDomainName(kServiceDomainNameDefault); 156 if (!dns_server_.Start( 157 ServiceParameters(kServiceType, service_name_prefix, service_domain_name, 158 ip, port), 159 command_line_reader::ReadTtl(kTtlDefault), 160 CreateTxt())) { 161 http_server_.Shutdown(); 162 return false; 163 } 164 165 print_job_handler_.reset(new PrintJobHandler); 166 xtoken_ = XPrivetToken(); 167 starttime_ = base::Time::Now(); 168 169 TryConnect(); 170 return true; 171 } 172 173 bool Printer::IsRunning() const { 174 return print_job_handler_; 175 } 176 177 void Printer::Stop() { 178 dns_server_.Shutdown(); 179 http_server_.Shutdown(); 180 requester_.reset(); 181 print_job_handler_.reset(); 182 xmpp_listener_.reset(); 183 } 184 185 void Printer::OnAuthError() { 186 LOG(ERROR) << "Auth error occurred"; 187 access_token_update_ = base::Time::Now(); 188 ChangeState(OFFLINE); 189 // TODO(maksymb): Implement *instant* updating of access_token. 190 } 191 192 std::string Printer::GetAccessToken() { 193 return access_token_; 194 } 195 196 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( 197 const std::string& user) { 198 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 199 if (status != PrivetHttpServer::REG_ERROR_OK) 200 return status; 201 202 if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED) 203 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 204 205 reg_info_ = RegistrationInfo(); 206 reg_info_.user = user; 207 reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_STARTED; 208 209 if (CommandLine::ForCurrentProcess()->HasSwitch("disable-confirmation")) { 210 reg_info_.confirmation_state = RegistrationInfo::CONFIRMATION_CONFIRMED; 211 LOG(INFO) << "Registration confirmed by default."; 212 } else { 213 printf("%s", kUserConfirmationTitle); 214 base::Time valid_until = base::Time::Now() + 215 base::TimeDelta::FromSeconds(kUserConfirmationTimeout); 216 base::MessageLoop::current()->PostTask( 217 FROM_HERE, 218 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until)); 219 } 220 221 requester_->StartRegistration(GenerateProxyId(), kPrinterName, user, kCdd); 222 223 return PrivetHttpServer::REG_ERROR_OK; 224 } 225 226 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken( 227 const std::string& user, 228 std::string* token, 229 std::string* claim_url) { 230 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 231 if (status != PrivetHttpServer::REG_ERROR_OK) 232 return status; 233 234 // Check if |action=start| was called, but |action=complete| wasn't. 235 if (reg_info_.state != RegistrationInfo::DEV_REG_REGISTRATION_STARTED && 236 reg_info_.state != 237 RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) 238 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 239 240 // If |action=getClaimToken| is valid in this state (was checked above) then 241 // check confirmation status. 242 if (reg_info_.confirmation_state != RegistrationInfo::CONFIRMATION_CONFIRMED) 243 return ConfirmationToRegistrationError(reg_info_.confirmation_state); 244 245 // If reply wasn't received yet, reply with |pending_user_action| error. 246 if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_STARTED) 247 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; 248 249 DCHECK_EQ(reg_info_.state, 250 RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY); 251 DCHECK_EQ(reg_info_.confirmation_state, 252 RegistrationInfo::CONFIRMATION_CONFIRMED); 253 254 *token = reg_info_.registration_token; 255 *claim_url = reg_info_.complete_invite_url; 256 return PrivetHttpServer::REG_ERROR_OK; 257 } 258 259 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete( 260 const std::string& user, 261 std::string* device_id) { 262 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 263 if (status != PrivetHttpServer::REG_ERROR_OK) 264 return status; 265 266 if (reg_info_.state != 267 RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) { 268 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 269 } 270 271 if (reg_info_.confirmation_state != RegistrationInfo::CONFIRMATION_CONFIRMED) 272 return ConfirmationToRegistrationError(reg_info_.confirmation_state); 273 274 reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_COMPLETING; 275 requester_->CompleteRegistration(); 276 *device_id = reg_info_.device_id; 277 278 return PrivetHttpServer::REG_ERROR_OK; 279 } 280 281 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel( 282 const std::string& user) { 283 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 284 if (status != PrivetHttpServer::REG_ERROR_OK && 285 status != PrivetHttpServer::REG_ERROR_SERVER_ERROR) { 286 return status; 287 } 288 289 if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED) 290 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 291 292 reg_info_ = RegistrationInfo(); 293 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); 294 295 return PrivetHttpServer::REG_ERROR_OK; 296 } 297 298 void Printer::GetRegistrationServerError(std::string* description) { 299 DCHECK_EQ(reg_info_.state, RegistrationInfo::DEV_REG_REGISTRATION_ERROR) << 300 "Method shouldn't be called when not needed."; 301 302 *description = reg_info_.error_description; 303 } 304 305 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { 306 // TODO(maksymb): Replace "text" with constants. 307 308 *info = PrivetHttpServer::DeviceInfo(); 309 info->version = "1.0"; 310 info->name = kPrinterName; 311 info->description = kPrinterDescription; 312 info->url = kCloudPrintUrl; 313 info->id = reg_info_.device_id; 314 info->device_state = "idle"; 315 info->connection_state = ConnectionStateToString(connection_state_); 316 info->manufacturer = "Google"; 317 info->model = "Prototype"; 318 info->serial_number = "2.3.5.7.13.17.19.31.61.89.107.127.521.607.1279.2203"; 319 info->firmware = "3.7.31.127.8191.131071.524287.2147483647"; 320 info->uptime = static_cast<int>((base::Time::Now() - starttime_).InSeconds()); 321 322 info->x_privet_token = xtoken_.GenerateXToken(); 323 324 if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED) 325 info->api.push_back("/privet/register"); 326 327 info->type.push_back("printer"); 328 } 329 330 bool Printer::IsRegistered() const { 331 return reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED; 332 } 333 334 bool Printer::CheckXPrivetTokenHeader(const std::string& token) const { 335 return xtoken_.CheckValidXToken(token); 336 } 337 338 void Printer::OnRegistrationStartResponseParsed( 339 const std::string& registration_token, 340 const std::string& complete_invite_url, 341 const std::string& device_id) { 342 reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY; 343 reg_info_.device_id = device_id; 344 reg_info_.registration_token = registration_token; 345 reg_info_.complete_invite_url = complete_invite_url; 346 } 347 348 void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token, 349 const std::string& access_token, 350 int access_token_expires_in_seconds) { 351 reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; 352 reg_info_.refresh_token = refresh_token; 353 RememberAccessToken(access_token, access_token_expires_in_seconds); 354 TryConnect(); 355 } 356 357 void Printer::OnAccesstokenReceviced(const std::string& access_token, 358 int expires_in_seconds) { 359 VLOG(3) << "Function: " << __FUNCTION__; 360 RememberAccessToken(access_token, expires_in_seconds); 361 switch (connection_state_) { 362 case ONLINE: 363 PostOnIdle(); 364 break; 365 366 case CONNECTING: 367 TryConnect(); 368 break; 369 370 default: 371 NOTREACHED(); 372 } 373 } 374 375 void Printer::OnXmppJidReceived(const std::string& xmpp_jid) { 376 reg_info_.xmpp_jid = xmpp_jid; 377 } 378 379 void Printer::OnRegistrationError(const std::string& description) { 380 LOG(ERROR) << "server_error: " << description; 381 382 // TODO(maksymb): Implement waiting after error and timeout of registration. 383 reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_ERROR; 384 reg_info_.error_description = description; 385 } 386 387 void Printer::OnNetworkError() { 388 VLOG(3) << "Function: " << __FUNCTION__; 389 ChangeState(OFFLINE); 390 } 391 392 void Printer::OnServerError(const std::string& description) { 393 VLOG(3) << "Function: " << __FUNCTION__; 394 LOG(ERROR) << "Server error: " << description; 395 396 ChangeState(OFFLINE); 397 } 398 399 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) { 400 VLOG(3) << "Function: " << __FUNCTION__; 401 402 LOG(INFO) << "Available printjobs: " << jobs.size(); 403 404 if (jobs.empty()) { 405 pending_print_jobs_check_ = false; 406 PostOnIdle(); 407 return; 408 } 409 410 LOG(INFO) << "Downloading printjob."; 411 requester_->RequestPrintJob(jobs[0]); 412 return; 413 } 414 415 void Printer::OnPrintJobDownloaded(const Job& job) { 416 VLOG(3) << "Function: " << __FUNCTION__; 417 print_job_handler_->SavePrintJob( 418 job.file, 419 job.ticket, 420 base::StringPrintf("%s.%s", job.create_time.c_str(), job.job_id.c_str()), 421 job.title); 422 requester_->SendPrintJobDone(job.job_id); 423 } 424 425 void Printer::OnPrintJobDone() { 426 VLOG(3) << "Function: " << __FUNCTION__; 427 PostOnIdle(); 428 } 429 430 void Printer::OnXmppConnected() { 431 pending_local_settings_check_ = true; 432 pending_print_jobs_check_ = true; 433 ChangeState(ONLINE); 434 PostOnIdle(); 435 } 436 437 void Printer::OnXmppAuthError() { 438 OnAuthError(); 439 } 440 441 void Printer::OnXmppNetworkError() { 442 ChangeState(OFFLINE); 443 } 444 445 void Printer::OnXmppNewPrintJob(const std::string& device_id) { 446 DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; 447 pending_print_jobs_check_ = true; 448 } 449 450 void Printer::OnXmppNewLocalSettings(const std::string& device_id) { 451 DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; 452 NOTIMPLEMENTED(); 453 } 454 455 void Printer::OnXmppDeleteNotification(const std::string& device_id) { 456 DCHECK_EQ(reg_info_.device_id, device_id) << "Data should contain printer_id"; 457 NOTIMPLEMENTED(); 458 } 459 460 void Printer::TryConnect() { 461 VLOG(3) << "Function: " << __FUNCTION__; 462 463 ChangeState(CONNECTING); 464 if (!requester_) 465 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); 466 467 if (IsRegistered()) { 468 if (access_token_update_ < base::Time::Now()) { 469 requester_->UpdateAccesstoken(reg_info_.refresh_token); 470 } else { 471 ConnectXmpp(); 472 } 473 } else { 474 // TODO(maksymb): Ping google.com to check connection state. 475 ChangeState(ONLINE); 476 } 477 } 478 479 void Printer::ConnectXmpp() { 480 xmpp_listener_.reset( 481 new CloudPrintXmppListener(reg_info_.xmpp_jid, kXmppPingIntervalDefault, 482 GetTaskRunner(), this)); 483 xmpp_listener_->Connect(access_token_); 484 } 485 486 void Printer::OnIdle() { 487 DCHECK(IsRegistered()); 488 DCHECK(on_idle_posted_) << "Instant call is not allowed"; 489 on_idle_posted_ = false; 490 491 if (connection_state_ != ONLINE) 492 return; 493 494 if (access_token_update_ < base::Time::Now()) { 495 requester_->UpdateAccesstoken(reg_info_.refresh_token); 496 return; 497 } 498 499 // TODO(maksymb): Check if privet-accesstoken was requested. 500 501 // TODO(maksymb): Check if local-printing was requested. 502 503 if (pending_local_settings_check_) { 504 GetLocalSettings(); 505 return; 506 } 507 508 if (pending_print_jobs_check_) { 509 FetchPrintJobs(); 510 return; 511 } 512 513 base::MessageLoop::current()->PostDelayedTask( 514 FROM_HERE, 515 base::Bind(&Printer::PostOnIdle, AsWeakPtr()), 516 base::TimeDelta::FromMilliseconds(1000)); 517 } 518 519 void Printer::GetLocalSettings() { 520 DCHECK(IsRegistered()); 521 522 pending_local_settings_check_ = false; 523 PostOnIdle(); 524 } 525 526 void Printer::FetchPrintJobs() { 527 VLOG(3) << "Function: " << __FUNCTION__; 528 529 DCHECK(IsRegistered()); 530 requester_->FetchPrintJobs(reg_info_.device_id); 531 } 532 533 void Printer::RememberAccessToken(const std::string& access_token, 534 int expires_in_seconds) { 535 using base::Time; 536 using base::TimeDelta; 537 access_token_ = access_token; 538 int64 time_to_update = static_cast<int64>(expires_in_seconds * 539 kTimeToNextAccessTokenUpdate); 540 access_token_update_ = Time::Now() + TimeDelta::FromSeconds(time_to_update); 541 VLOG(1) << "Current access_token: " << access_token; 542 SaveToFile(); 543 } 544 545 PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors( 546 const std::string& user) const { 547 DCHECK(!IsRegistered()); 548 549 if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED && 550 user != reg_info_.user) { 551 return PrivetHttpServer::REG_ERROR_DEVICE_BUSY; 552 } 553 554 if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_ERROR) 555 return PrivetHttpServer::REG_ERROR_SERVER_ERROR; 556 557 return PrivetHttpServer::REG_ERROR_OK; 558 } 559 560 void Printer::WaitUserConfirmation(base::Time valid_until) { 561 if (base::Time::Now() > valid_until) { 562 reg_info_.confirmation_state = RegistrationInfo::CONFIRMATION_TIMEOUT; 563 LOG(INFO) << "Confirmation timeout reached."; 564 return; 565 } 566 567 if (_kbhit()) { 568 int c = _getche(); 569 if (c == 'y' || c == 'Y') { 570 reg_info_.confirmation_state = RegistrationInfo::CONFIRMATION_CONFIRMED; 571 LOG(INFO) << "Registration confirmed by user."; 572 } else { 573 reg_info_.confirmation_state = RegistrationInfo::CONFIRMATION_DISCARDED; 574 LOG(INFO) << "Registration discarded by user."; 575 } 576 return; 577 } 578 579 base::MessageLoop::current()->PostDelayedTask( 580 FROM_HERE, 581 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until), 582 base::TimeDelta::FromMilliseconds(100)); 583 } 584 585 std::string Printer::GenerateProxyId() const { 586 return "{" + base::GenerateGUID() +"}"; 587 } 588 589 std::vector<std::string> Printer::CreateTxt() const { 590 std::vector<std::string> txt; 591 txt.push_back("txtvers=1"); 592 txt.push_back("ty=" + std::string(kPrinterName)); 593 txt.push_back("note=" + std::string(kPrinterDescription)); 594 txt.push_back("url=" + std::string(kCloudPrintUrl)); 595 txt.push_back("type=printer"); 596 txt.push_back("id=" + reg_info_.device_id); 597 txt.push_back("cs=" + ConnectionStateToString(connection_state_)); 598 599 return txt; 600 } 601 602 void Printer::SaveToFile() const { 603 base::FilePath file_path; 604 file_path = file_path.AppendASCII( 605 command_line_reader::ReadStatePath(kPrinterStatePathDefault)); 606 607 base::DictionaryValue json; 608 // TODO(maksymb): Get rid of in-place constants. 609 if (IsRegistered()) { 610 json.SetBoolean("registered", true); 611 json.SetString("user", reg_info_.user); 612 json.SetString("device_id", reg_info_.device_id); 613 json.SetString("refresh_token", reg_info_.refresh_token); 614 json.SetString("xmpp_jid", reg_info_.xmpp_jid); 615 json.SetString("access_token", access_token_); 616 json.SetInteger("access_token_update", 617 static_cast<int>(access_token_update_.ToTimeT())); 618 } else { 619 json.SetBoolean("registered", false); 620 } 621 622 std::string json_str; 623 base::JSONWriter::WriteWithOptions(&json, 624 base::JSONWriter::OPTIONS_PRETTY_PRINT, 625 &json_str); 626 if (!file_util::WriteFile(file_path, json_str.data(), 627 static_cast<int>(json_str.size()))) { 628 LOG(ERROR) << "Cannot write state."; 629 } 630 LOG(INFO) << "State written to file."; 631 } 632 633 bool Printer::LoadFromFile() { 634 base::FilePath file_path; 635 file_path = file_path.AppendASCII( 636 command_line_reader::ReadStatePath(kPrinterStatePathDefault)); 637 if (!base::PathExists(file_path)) { 638 LOG(INFO) << "Registration info is not found. Printer is unregistered."; 639 return false; 640 } 641 642 LOG(INFO) << "Loading registration info from file."; 643 std::string json_str; 644 if (!file_util::ReadFileToString(file_path, &json_str)) { 645 LOG(ERROR) << "Cannot open file."; 646 return false; 647 } 648 649 scoped_ptr<base::Value> json_val(base::JSONReader::Read(json_str)); 650 base::DictionaryValue* json = NULL; 651 if (!json_val || !json_val->GetAsDictionary(&json)) { 652 LOG(ERROR) << "Cannot read JSON dictionary from file."; 653 return false; 654 } 655 656 bool registered = false; 657 if (!json->GetBoolean("registered", ®istered)) { 658 LOG(ERROR) << "Cannot parse |registered| state."; 659 return false; 660 } 661 662 if (!registered) { 663 reg_info_ = RegistrationInfo(); 664 return true; 665 } 666 667 std::string user; 668 if (!json->GetString("user", &user)) { 669 LOG(ERROR) << "Cannot parse |user|."; 670 return false; 671 } 672 673 std::string device_id; 674 if (!json->GetString("device_id", &device_id)) { 675 LOG(ERROR) << "Cannot parse |device_id|."; 676 return false; 677 } 678 679 std::string refresh_token; 680 if (!json->GetString("refresh_token", &refresh_token)) { 681 LOG(ERROR) << "Cannot parse |refresh_token|."; 682 return false; 683 } 684 685 std::string xmpp_jid; 686 if (!json->GetString("xmpp_jid", &xmpp_jid)) { 687 LOG(ERROR) << "Cannot parse |xmpp_jid|."; 688 return false; 689 } 690 691 std::string access_token; 692 if (!json->GetString("access_token", &access_token)) { 693 LOG(ERROR) << "Cannot parse |access_token|."; 694 return false; 695 } 696 697 int access_token_update; 698 if (!json->GetInteger("access_token_update", &access_token_update)) { 699 LOG(ERROR) << "Cannot parse |access_token_update|."; 700 return false; 701 } 702 703 reg_info_ = RegistrationInfo(); 704 reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; 705 reg_info_.user = user; 706 reg_info_.device_id = device_id; 707 reg_info_.refresh_token = refresh_token; 708 reg_info_.xmpp_jid = xmpp_jid; 709 using base::Time; 710 access_token_ = access_token; 711 access_token_update_ = Time::FromTimeT(access_token_update); 712 713 return true; 714 } 715 716 void Printer::PostOnIdle() { 717 VLOG(3) << "Function: " << __FUNCTION__; 718 DCHECK(!on_idle_posted_) << "Only one instance can be posted."; 719 on_idle_posted_ = true; 720 721 base::MessageLoop::current()->PostTask( 722 FROM_HERE, 723 base::Bind(&Printer::OnIdle, AsWeakPtr())); 724 } 725 726 PrivetHttpServer::RegistrationErrorStatus 727 Printer::ConfirmationToRegistrationError( 728 RegistrationInfo::ConfirmationState state) { 729 switch (state) { 730 case RegistrationInfo::CONFIRMATION_PENDING: 731 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; 732 case RegistrationInfo::CONFIRMATION_DISCARDED: 733 return PrivetHttpServer::REG_ERROR_USER_CANCEL; 734 case RegistrationInfo::CONFIRMATION_CONFIRMED: 735 NOTREACHED(); 736 return PrivetHttpServer::REG_ERROR_OK; 737 case RegistrationInfo::CONFIRMATION_TIMEOUT: 738 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT; 739 default: 740 NOTREACHED(); 741 return PrivetHttpServer::REG_ERROR_OK; 742 } 743 } 744 745 std::string Printer::ConnectionStateToString(ConnectionState state) const { 746 switch (state) { 747 case OFFLINE: 748 return "offline"; 749 case ONLINE: 750 return "online"; 751 case CONNECTING: 752 return "connecting"; 753 case NOT_CONFIGURED: 754 return "not-configured"; 755 756 default: 757 NOTREACHED(); 758 return ""; 759 } 760 } 761 762 bool Printer::ChangeState(ConnectionState new_state) { 763 if (connection_state_ == new_state) 764 return false; 765 766 connection_state_ = new_state; 767 LOG(INFO) << base::StringPrintf( 768 "Printer is now %s (%s)", 769 ConnectionStateToString(connection_state_).c_str(), 770 IsRegistered() ? "registered" : "unregistered"); 771 772 dns_server_.UpdateMetadata(CreateTxt()); 773 774 switch (connection_state_) { 775 case CONNECTING: 776 break; 777 778 case ONLINE: 779 break; 780 781 case OFFLINE: 782 requester_.reset(); 783 xmpp_listener_.reset(); 784 base::MessageLoop::current()->PostDelayedTask( 785 FROM_HERE, 786 base::Bind(&Printer::TryConnect, AsWeakPtr()), 787 base::TimeDelta::FromSeconds(kReconnectTimeout)); 788 789 case NOT_CONFIGURED: 790 break; 791 792 default: 793 NOTREACHED(); 794 } 795 796 return true; 797 } 798 799