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 <algorithm> 8 #include <limits.h> 9 #include <stdio.h> 10 #include <string> 11 #include <vector> 12 13 #include "base/bind.h" 14 #include "base/command_line.h" 15 #include "base/file_util.h" 16 #include "base/format_macros.h" 17 #include "base/guid.h" 18 #include "base/json/json_reader.h" 19 #include "base/json/json_writer.h" 20 #include "base/rand_util.h" 21 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/string_util.h" 23 #include "base/strings/stringprintf.h" 24 #include "cloud_print/gcp20/prototype/command_line_reader.h" 25 #include "cloud_print/gcp20/prototype/local_settings.h" 26 #include "cloud_print/gcp20/prototype/service_parameters.h" 27 #include "cloud_print/gcp20/prototype/special_io.h" 28 #include "cloud_print/version.h" 29 #include "net/base/net_util.h" 30 #include "net/base/url_util.h" 31 32 const char kPrinterStatePathDefault[] = "printer_state.json"; 33 34 namespace { 35 36 const uint16 kHttpPortDefault = 10101; 37 const uint32 kTtlDefault = 60*60; // in seconds 38 39 const char kServiceType[] = "_privet._tcp.local"; 40 const char kSecondaryServiceType[] = "_printer._sub._privet._tcp.local"; 41 const char kServiceNamePrefixDefault[] = "gcp20_device_"; 42 const char kServiceDomainNameFormatDefault[] = "my-privet-device%d.local"; 43 44 const char kPrinterName[] = "Google GCP2.0 Prototype"; 45 const char kPrinterDescription[] = "Printer emulator"; 46 47 const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you " 48 "agree and any other to discard\n"; 49 const int kUserConfirmationTimeout = 30; // in seconds 50 const int kRegistrationTimeout = 60; // in seconds 51 const int kReconnectTimeout = 5; // in seconds 52 53 const double kTimeToNextAccessTokenUpdate = 0.8; // relatively to living time. 54 55 const char kCdd[] = 56 "{" 57 " 'version': '1.0'," 58 " 'printer': {" 59 " 'supported_content_type': [" 60 " {" 61 " 'content_type': 'application/pdf'" 62 " }," 63 " {" 64 " 'content_type': 'image/pwg-raster'" 65 " }," 66 " {" 67 " 'content_type': 'image/jpeg'" 68 " }" 69 " ]," 70 " 'color': {" 71 " 'option': [" 72 " {" 73 " 'is_default': true," 74 " 'type': 'STANDARD_COLOR'" 75 " }," 76 " {" 77 " 'type': 'STANDARD_MONOCHROME'" 78 " }" 79 " ]" 80 " }," 81 " 'media_size': {" 82 " 'option': [ {" 83 " 'height_microns': 297000," 84 " 'name': 'ISO_A4'," 85 " 'width_microns': 210000" 86 " }, {" 87 " 'custom_display_name': 'Letter'," 88 " 'height_microns': 279400," 89 " 'is_default': true," 90 " 'name': 'NA_LETTER'," 91 " 'width_microns': 215900" 92 " } ]" 93 " }," 94 " 'page_orientation': {" 95 " 'option': [ {" 96 " 'is_default': true," 97 " 'type': 'PORTRAIT'" 98 " }, {" 99 " 'type': 'LANDSCAPE'" 100 " } ]" 101 " }," 102 " 'reverse_order': {" 103 " 'default': false" 104 " }" 105 " }" 106 "}"; 107 108 // Returns local IP address number of first interface found (except loopback). 109 // Return value is empty if no interface found. Possible interfaces names are 110 // "eth0", "wlan0" etc. If interface name is empty, function will return IP 111 // address of first interface found. 112 net::IPAddressNumber GetLocalIp(const std::string& interface_name, 113 bool return_ipv6_number) { 114 net::NetworkInterfaceList interfaces; 115 bool success = net::GetNetworkList( 116 &interfaces, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES); 117 DCHECK(success); 118 119 size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize 120 : net::kIPv4AddressSize; 121 122 for (net::NetworkInterfaceList::iterator iter = interfaces.begin(); 123 iter != interfaces.end(); ++iter) { 124 if (iter->address.size() == expected_address_size && 125 (interface_name.empty() || interface_name == iter->name)) { 126 return iter->address; 127 } 128 } 129 130 return net::IPAddressNumber(); 131 } 132 133 std::string GetDescription() { 134 std::string result = kPrinterDescription; 135 net::IPAddressNumber ip = GetLocalIp("", false); 136 if (!ip.empty()) 137 result += " at " + net::IPAddressToString(ip); 138 return result; 139 } 140 141 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() { 142 return base::MessageLoop::current()->message_loop_proxy(); 143 } 144 145 } // namespace 146 147 using cloud_print_response_parser::Job; 148 149 Printer::Printer() 150 : connection_state_(OFFLINE), 151 http_server_(this), 152 on_idle_posted_(false), 153 pending_local_settings_check_(false), 154 pending_print_jobs_check_(false), 155 pending_deletion_(false) { 156 } 157 158 Printer::~Printer() { 159 Stop(); 160 } 161 162 bool Printer::Start() { 163 if (IsRunning()) 164 return true; 165 166 LoadFromFile(); 167 168 if (state_.local_settings.local_discovery && !StartLocalDiscoveryServers()) 169 return false; 170 171 print_job_handler_.reset(new PrintJobHandler); 172 xtoken_ = XPrivetToken(); 173 starttime_ = base::Time::Now(); 174 175 TryConnect(); 176 return true; 177 } 178 179 bool Printer::IsRunning() const { 180 return print_job_handler_; 181 } 182 183 void Printer::Stop() { 184 if (!IsRunning()) 185 return; 186 dns_server_.Shutdown(); 187 http_server_.Shutdown(); 188 requester_.reset(); 189 print_job_handler_.reset(); 190 xmpp_listener_.reset(); 191 } 192 193 std::string Printer::GetRawCdd() { 194 std::string json_str; 195 base::JSONWriter::WriteWithOptions(&GetCapabilities(), 196 base::JSONWriter::OPTIONS_PRETTY_PRINT, 197 &json_str); 198 return json_str; 199 } 200 201 void Printer::OnAuthError() { 202 LOG(ERROR) << "Auth error occurred"; 203 state_.access_token_update = base::Time(); 204 FallOffline(true); 205 } 206 207 std::string Printer::GetAccessToken() { 208 return state_.access_token; 209 } 210 211 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( 212 const std::string& user) { 213 CheckRegistrationExpiration(); 214 215 PrinterState::ConfirmationState conf_state = state_.confirmation_state; 216 if (state_.registration_state == PrinterState::REGISTRATION_ERROR || 217 conf_state == PrinterState::CONFIRMATION_TIMEOUT || 218 conf_state == PrinterState::CONFIRMATION_DISCARDED) { 219 state_ = PrinterState(); 220 } 221 222 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 223 if (status != PrivetHttpServer::REG_ERROR_OK) 224 return status; 225 226 if (state_.registration_state != PrinterState::UNREGISTERED) 227 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 228 229 UpdateRegistrationExpiration(); 230 231 state_ = PrinterState(); 232 state_.user = user; 233 state_.registration_state = PrinterState::REGISTRATION_STARTED; 234 235 if (CommandLine::ForCurrentProcess()->HasSwitch("disable-confirmation")) { 236 state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED; 237 VLOG(0) << "Registration confirmed by default."; 238 } else { 239 LOG(WARNING) << kUserConfirmationTitle; 240 base::Time valid_until = base::Time::Now() + 241 base::TimeDelta::FromSeconds(kUserConfirmationTimeout); 242 base::MessageLoop::current()->PostTask( 243 FROM_HERE, 244 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until)); 245 } 246 247 requester_->StartRegistration(GenerateProxyId(), kPrinterName, user, 248 state_.local_settings, GetRawCdd()); 249 250 return PrivetHttpServer::REG_ERROR_OK; 251 } 252 253 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken( 254 const std::string& user, 255 std::string* token, 256 std::string* claim_url) { 257 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 258 if (status != PrivetHttpServer::REG_ERROR_OK) 259 return status; 260 261 // Check if |action=start| was called, but |action=complete| wasn't. 262 if (state_.registration_state != PrinterState::REGISTRATION_STARTED && 263 state_.registration_state != 264 PrinterState::REGISTRATION_CLAIM_TOKEN_READY) { 265 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 266 } 267 268 // If |action=getClaimToken| is valid in this state (was checked above) then 269 // check confirmation status. 270 if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED) 271 return ConfirmationToRegistrationError(state_.confirmation_state); 272 273 UpdateRegistrationExpiration(); 274 275 // If reply wasn't received yet, reply with |pending_user_action| error. 276 if (state_.registration_state == PrinterState::REGISTRATION_STARTED) 277 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; 278 279 DCHECK_EQ(state_.confirmation_state, PrinterState::CONFIRMATION_CONFIRMED); 280 DCHECK_EQ(state_.registration_state, 281 PrinterState::REGISTRATION_CLAIM_TOKEN_READY); 282 283 *token = state_.registration_token; 284 *claim_url = state_.complete_invite_url; 285 return PrivetHttpServer::REG_ERROR_OK; 286 } 287 288 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete( 289 const std::string& user, 290 std::string* device_id) { 291 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 292 if (status != PrivetHttpServer::REG_ERROR_OK) 293 return status; 294 295 if (state_.registration_state != PrinterState::REGISTRATION_CLAIM_TOKEN_READY) 296 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 297 298 UpdateRegistrationExpiration(); 299 300 if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED) 301 return ConfirmationToRegistrationError(state_.confirmation_state); 302 303 state_.registration_state = PrinterState::REGISTRATION_COMPLETING; 304 requester_->CompleteRegistration(); 305 *device_id = state_.device_id; 306 307 return PrivetHttpServer::REG_ERROR_OK; 308 } 309 310 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel( 311 const std::string& user) { 312 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); 313 if (status != PrivetHttpServer::REG_ERROR_OK && 314 status != PrivetHttpServer::REG_ERROR_SERVER_ERROR) { 315 return status; 316 } 317 318 if (state_.registration_state == PrinterState::UNREGISTERED) 319 return PrivetHttpServer::REG_ERROR_INVALID_ACTION; 320 321 InvalidateRegistrationExpiration(); 322 323 state_ = PrinterState(); 324 325 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); 326 327 return PrivetHttpServer::REG_ERROR_OK; 328 } 329 330 void Printer::GetRegistrationServerError(std::string* description) { 331 DCHECK_EQ(state_.registration_state, PrinterState::REGISTRATION_ERROR) 332 << "Method shouldn't be called when not needed."; 333 334 *description = state_.error_description; 335 } 336 337 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { 338 CheckRegistrationExpiration(); 339 340 // TODO(maksymb): Replace "text" with constants. 341 *info = PrivetHttpServer::DeviceInfo(); 342 info->version = "1.0"; 343 info->name = kPrinterName; 344 info->description = GetDescription(); 345 info->url = kCloudPrintUrl; 346 info->id = state_.device_id; 347 info->device_state = "idle"; 348 info->connection_state = ConnectionStateToString(connection_state_); 349 info->manufacturer = COMPANY_FULLNAME_STRING; 350 info->model = "Prototype r" + std::string(LASTCHANGE_STRING); 351 info->serial_number = "20CB5FF2-B28C-4EFA-8DCD-516CFF0455A2"; 352 info->firmware = CHROME_VERSION_STRING; 353 info->uptime = static_cast<int>((base::Time::Now() - starttime_).InSeconds()); 354 355 info->x_privet_token = xtoken_.GenerateXToken(); 356 357 // TODO(maksymb): Create enum for available APIs and replace 358 // this API text names with constants from enum. API text names should be only 359 // known in PrivetHttpServer. 360 if (!IsRegistered()) { 361 info->api.push_back("/privet/register"); 362 } else { 363 info->api.push_back("/privet/capabilities"); 364 if (IsLocalPrintingAllowed()) { 365 info->api.push_back("/privet/printer/createjob"); 366 info->api.push_back("/privet/printer/submitdoc"); 367 info->api.push_back("/privet/printer/jobstate"); 368 } 369 } 370 371 info->type.push_back("printer"); 372 } 373 374 bool Printer::IsRegistered() const { 375 return state_.registration_state == PrinterState::REGISTERED; 376 } 377 378 bool Printer::IsLocalPrintingAllowed() const { 379 return state_.local_settings.local_printing_enabled; 380 } 381 382 bool Printer::CheckXPrivetTokenHeader(const std::string& token) const { 383 return xtoken_.CheckValidXToken(token); 384 } 385 386 const base::DictionaryValue& Printer::GetCapabilities() { 387 if (!state_.cdd.get()) { 388 std::string cdd_string; 389 base::ReplaceChars(kCdd, "'", "\"", &cdd_string); 390 scoped_ptr<base::Value> json_val(base::JSONReader::Read(cdd_string)); 391 base::DictionaryValue* json = NULL; 392 CHECK(json_val->GetAsDictionary(&json)); 393 state_.cdd.reset(json->DeepCopy()); 394 } 395 return *state_.cdd; 396 } 397 398 LocalPrintJob::CreateResult Printer::CreateJob(const std::string& ticket, 399 std::string* job_id, 400 int* expires_in, 401 int* error_timeout, 402 std::string* error_description) { 403 return print_job_handler_->CreatePrintJob(ticket, job_id, expires_in, 404 error_timeout, error_description); 405 } 406 407 LocalPrintJob::SaveResult Printer::SubmitDoc(const LocalPrintJob& job, 408 std::string* job_id, 409 int* expires_in, 410 std::string* error_description, 411 int* timeout) { 412 return print_job_handler_->SaveLocalPrintJob(job, job_id, expires_in, 413 error_description, timeout); 414 } 415 416 LocalPrintJob::SaveResult Printer::SubmitDocWithId( 417 const LocalPrintJob& job, 418 const std::string& job_id, 419 int* expires_in, 420 std::string* error_description, 421 int* timeout) { 422 return print_job_handler_->CompleteLocalPrintJob(job, job_id, expires_in, 423 error_description, timeout); 424 } 425 426 bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) { 427 return print_job_handler_->GetJobState(id, info); 428 } 429 430 void Printer::OnRegistrationStartResponseParsed( 431 const std::string& registration_token, 432 const std::string& complete_invite_url, 433 const std::string& device_id) { 434 state_.registration_state = PrinterState::REGISTRATION_CLAIM_TOKEN_READY; 435 state_.device_id = device_id; 436 state_.registration_token = registration_token; 437 state_.complete_invite_url = complete_invite_url; 438 } 439 440 void Printer::OnRegistrationFinished(const std::string& refresh_token, 441 const std::string& access_token, 442 int access_token_expires_in_seconds) { 443 InvalidateRegistrationExpiration(); 444 445 state_.registration_state = PrinterState::REGISTERED; 446 state_.refresh_token = refresh_token; 447 RememberAccessToken(access_token, access_token_expires_in_seconds); 448 TryConnect(); 449 } 450 451 void Printer::OnAccesstokenReceviced(const std::string& access_token, 452 int expires_in_seconds) { 453 VLOG(3) << "Function: " << __FUNCTION__; 454 RememberAccessToken(access_token, expires_in_seconds); 455 switch (connection_state_) { 456 case ONLINE: 457 PostOnIdle(); 458 break; 459 460 case CONNECTING: 461 TryConnect(); 462 break; 463 464 default: 465 NOTREACHED(); 466 } 467 } 468 469 void Printer::OnXmppJidReceived(const std::string& xmpp_jid) { 470 state_.xmpp_jid = xmpp_jid; 471 } 472 473 void Printer::OnRegistrationError(const std::string& description) { 474 LOG(ERROR) << "server_error: " << description; 475 476 SetRegistrationError(description); 477 } 478 479 void Printer::OnNetworkError() { 480 VLOG(3) << "Function: " << __FUNCTION__; 481 FallOffline(false); 482 } 483 484 void Printer::OnServerError(const std::string& description) { 485 VLOG(3) << "Function: " << __FUNCTION__; 486 LOG(ERROR) << "Server error: " << description; 487 FallOffline(false); 488 } 489 490 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) { 491 VLOG(3) << "Function: " << __FUNCTION__; 492 493 VLOG(0) << "Available printjobs: " << jobs.size(); 494 if (jobs.empty()) { 495 pending_print_jobs_check_ = false; 496 PostOnIdle(); 497 return; 498 } 499 500 VLOG(0) << "Downloading printjob."; 501 requester_->RequestPrintJob(jobs[0]); 502 return; 503 } 504 505 void Printer::OnPrintJobDownloaded(const Job& job) { 506 VLOG(3) << "Function: " << __FUNCTION__; 507 print_job_handler_->SavePrintJob(job.file, job.ticket, job.create_time, 508 job.job_id, job.title, "pdf"); 509 requester_->SendPrintJobDone(job.job_id); 510 } 511 512 void Printer::OnPrintJobDone() { 513 VLOG(3) << "Function: " << __FUNCTION__; 514 PostOnIdle(); 515 } 516 517 void Printer::OnLocalSettingsReceived(LocalSettings::State state, 518 const LocalSettings& settings) { 519 pending_local_settings_check_ = false; 520 switch (state) { 521 case LocalSettings::CURRENT: 522 VLOG(0) << "No new local settings"; 523 PostOnIdle(); 524 break; 525 case LocalSettings::PENDING: 526 VLOG(0) << "New local settings were received"; 527 ApplyLocalSettings(settings); 528 break; 529 case LocalSettings::PRINTER_DELETED: 530 LOG(WARNING) << "Printer was deleted on server"; 531 pending_deletion_ = true; 532 PostOnIdle(); 533 break; 534 535 default: 536 NOTREACHED(); 537 } 538 } 539 540 void Printer::OnLocalSettingsUpdated() { 541 PostOnIdle(); 542 } 543 544 void Printer::OnXmppConnected() { 545 pending_local_settings_check_ = true; 546 pending_print_jobs_check_ = true; 547 ChangeState(ONLINE); 548 PostOnIdle(); 549 } 550 551 void Printer::OnXmppAuthError() { 552 OnAuthError(); 553 } 554 555 void Printer::OnXmppNetworkError() { 556 FallOffline(false); 557 } 558 559 void Printer::OnXmppNewPrintJob(const std::string& device_id) { 560 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; 561 pending_print_jobs_check_ = true; 562 } 563 564 void Printer::OnXmppNewLocalSettings(const std::string& device_id) { 565 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; 566 pending_local_settings_check_ = true; 567 } 568 569 void Printer::OnXmppDeleteNotification(const std::string& device_id) { 570 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; 571 pending_deletion_ = true; 572 } 573 574 void Printer::TryConnect() { 575 VLOG(3) << "Function: " << __FUNCTION__; 576 577 ChangeState(CONNECTING); 578 if (!requester_) 579 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); 580 581 if (IsRegistered()) { 582 if (state_.access_token_update < base::Time::Now()) { 583 requester_->UpdateAccesstoken(state_.refresh_token); 584 } else { 585 ConnectXmpp(); 586 } 587 } else { 588 // TODO(maksymb): Ping google.com to check connection state. 589 ChangeState(ONLINE); 590 } 591 } 592 593 void Printer::ConnectXmpp() { 594 xmpp_listener_.reset( 595 new CloudPrintXmppListener(state_.xmpp_jid, 596 state_.local_settings.xmpp_timeout_value, 597 GetTaskRunner(), this)); 598 xmpp_listener_->Connect(state_.access_token); 599 } 600 601 void Printer::OnIdle() { 602 DCHECK(IsRegistered()); 603 DCHECK(on_idle_posted_) << "Instant call is not allowed"; 604 on_idle_posted_ = false; 605 606 if (connection_state_ != ONLINE) 607 return; 608 609 if (pending_deletion_) { 610 OnPrinterDeleted(); 611 return; 612 } 613 614 if (state_.access_token_update < base::Time::Now()) { 615 requester_->UpdateAccesstoken(state_.refresh_token); 616 return; 617 } 618 619 // TODO(maksymb): Check if privet-accesstoken was requested. 620 621 if (pending_local_settings_check_) { 622 GetLocalSettings(); 623 return; 624 } 625 626 if (pending_print_jobs_check_) { 627 FetchPrintJobs(); 628 return; 629 } 630 631 base::MessageLoop::current()->PostDelayedTask( 632 FROM_HERE, 633 base::Bind(&Printer::PostOnIdle, AsWeakPtr()), 634 base::TimeDelta::FromMilliseconds(1000)); 635 } 636 637 void Printer::FetchPrintJobs() { 638 VLOG(3) << "Function: " << __FUNCTION__; 639 DCHECK(IsRegistered()); 640 requester_->FetchPrintJobs(state_.device_id); 641 } 642 643 void Printer::GetLocalSettings() { 644 VLOG(3) << "Function: " << __FUNCTION__; 645 DCHECK(IsRegistered()); 646 requester_->RequestLocalSettings(state_.device_id); 647 } 648 649 void Printer::ApplyLocalSettings(const LocalSettings& settings) { 650 state_.local_settings = settings; 651 SaveToFile(); 652 653 if (state_.local_settings.local_discovery) { 654 bool success = StartLocalDiscoveryServers(); 655 if (!success) 656 LOG(ERROR) << "Local discovery servers cannot be started"; 657 // TODO(maksymb): If start failed try to start them again after some timeout 658 } else { 659 dns_server_.Shutdown(); 660 http_server_.Shutdown(); 661 } 662 xmpp_listener_->set_ping_interval(state_.local_settings.xmpp_timeout_value); 663 664 requester_->SendLocalSettings(state_.device_id, state_.local_settings); 665 } 666 667 void Printer::OnPrinterDeleted() { 668 pending_deletion_ = false; 669 670 state_ = PrinterState(); 671 672 SaveToFile(); 673 Stop(); 674 Start(); 675 } 676 677 void Printer::RememberAccessToken(const std::string& access_token, 678 int expires_in_seconds) { 679 using base::Time; 680 using base::TimeDelta; 681 state_.access_token = access_token; 682 int64 time_to_update = static_cast<int64>(expires_in_seconds * 683 kTimeToNextAccessTokenUpdate); 684 state_.access_token_update = 685 Time::Now() + TimeDelta::FromSeconds(time_to_update); 686 VLOG(0) << "Current access_token: " << access_token; 687 SaveToFile(); 688 } 689 690 void Printer::SetRegistrationError(const std::string& description) { 691 DCHECK(!IsRegistered()); 692 state_.registration_state = PrinterState::REGISTRATION_ERROR; 693 state_.error_description = description; 694 } 695 696 PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors( 697 const std::string& user) { 698 CheckRegistrationExpiration(); 699 DCHECK(!IsRegistered()); 700 if (connection_state_ != ONLINE) 701 return PrivetHttpServer::REG_ERROR_OFFLINE; 702 703 if (state_.registration_state != PrinterState::UNREGISTERED && 704 user != state_.user) { 705 return PrivetHttpServer::REG_ERROR_DEVICE_BUSY; 706 } 707 708 if (state_.registration_state == PrinterState::REGISTRATION_ERROR) 709 return PrivetHttpServer::REG_ERROR_SERVER_ERROR; 710 711 DCHECK_EQ(connection_state_, ONLINE); 712 713 return PrivetHttpServer::REG_ERROR_OK; 714 } 715 716 void Printer::WaitUserConfirmation(base::Time valid_until) { 717 // TODO(maksymb): Move to separate class. 718 719 if (base::Time::Now() > valid_until) { 720 state_.confirmation_state = PrinterState::CONFIRMATION_TIMEOUT; 721 VLOG(0) << "Confirmation timeout reached."; 722 return; 723 } 724 725 if (_kbhit()) { 726 int c = _getche(); 727 if (c == 'y' || c == 'Y') { 728 state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED; 729 VLOG(0) << "Registration confirmed by user."; 730 } else { 731 state_.confirmation_state = PrinterState::CONFIRMATION_DISCARDED; 732 VLOG(0) << "Registration discarded by user."; 733 } 734 return; 735 } 736 737 base::MessageLoop::current()->PostDelayedTask( 738 FROM_HERE, 739 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until), 740 base::TimeDelta::FromMilliseconds(100)); 741 } 742 743 std::string Printer::GenerateProxyId() const { 744 return "{" + base::GenerateGUID() +"}"; 745 } 746 747 std::vector<std::string> Printer::CreateTxt() const { 748 std::vector<std::string> txt; 749 txt.push_back("txtvers=1"); 750 txt.push_back("ty=" + std::string(kPrinterName)); 751 txt.push_back("note=" + std::string(GetDescription())); 752 txt.push_back("url=" + std::string(kCloudPrintUrl)); 753 txt.push_back("type=printer"); 754 txt.push_back("id=" + state_.device_id); 755 txt.push_back("cs=" + ConnectionStateToString(connection_state_)); 756 757 return txt; 758 } 759 760 void Printer::SaveToFile() { 761 GetCapabilities(); // Make sure capabilities created. 762 base::FilePath file_path; 763 file_path = file_path.AppendASCII( 764 command_line_reader::ReadStatePath(kPrinterStatePathDefault)); 765 766 if (printer_state::SaveToFile(file_path, state_)) { 767 VLOG(0) << "Printer state written to file"; 768 } else { 769 VLOG(0) << "Cannot write printer state to file"; 770 } 771 } 772 773 bool Printer::LoadFromFile() { 774 state_ = PrinterState(); 775 776 base::FilePath file_path; 777 file_path = file_path.AppendASCII( 778 command_line_reader::ReadStatePath(kPrinterStatePathDefault)); 779 780 if (!base::PathExists(file_path)) { 781 VLOG(0) << "Printer state file not found"; 782 return false; 783 } 784 785 if (printer_state::LoadFromFile(file_path, &state_)) { 786 VLOG(0) << "Printer state loaded from file"; 787 SaveToFile(); 788 } else { 789 VLOG(0) << "Reading/parsing printer state from file failed"; 790 } 791 792 return true; 793 } 794 795 void Printer::PostOnIdle() { 796 VLOG(3) << "Function: " << __FUNCTION__; 797 DCHECK(!on_idle_posted_) << "Only one instance can be posted."; 798 on_idle_posted_ = true; 799 800 base::MessageLoop::current()->PostTask( 801 FROM_HERE, 802 base::Bind(&Printer::OnIdle, AsWeakPtr())); 803 } 804 805 void Printer::CheckRegistrationExpiration() { 806 if (!registration_expiration_.is_null() && 807 registration_expiration_ < base::Time::Now()) { 808 state_ = PrinterState(); 809 InvalidateRegistrationExpiration(); 810 } 811 } 812 813 void Printer::UpdateRegistrationExpiration() { 814 registration_expiration_ = 815 base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout); 816 } 817 818 void Printer::InvalidateRegistrationExpiration() { 819 registration_expiration_ = base::Time(); 820 } 821 822 bool Printer::StartLocalDiscoveryServers() { 823 if (!StartHttpServer()) 824 return false; 825 if (!StartDnsServer()) { 826 http_server_.Shutdown(); 827 return false; 828 } 829 return true; 830 } 831 832 bool Printer::StartDnsServer() { 833 DCHECK(state_.local_settings.local_discovery); 834 835 // TODO(maksymb): Add switch for command line to control interface name. 836 net::IPAddressNumber ip = GetLocalIp("", false); 837 if (ip.empty()) { 838 LOG(ERROR) << "No local IP found. Cannot start printer."; 839 return false; 840 } 841 VLOG(0) << "Local address: " << net::IPAddressToString(ip); 842 843 uint16 port = command_line_reader::ReadHttpPort(kHttpPortDefault); 844 845 std::string service_name_prefix = 846 command_line_reader::ReadServiceNamePrefix(kServiceNamePrefixDefault + 847 net::IPAddressToString(ip)); 848 std::replace(service_name_prefix .begin(), service_name_prefix .end(), 849 '.', '_'); 850 851 std::string service_domain_name = 852 command_line_reader::ReadDomainName( 853 base::StringPrintf(kServiceDomainNameFormatDefault, 854 base::RandInt(0, INT_MAX))); 855 856 ServiceParameters params(kServiceType, kSecondaryServiceType, 857 service_name_prefix, service_domain_name, 858 ip, GetLocalIp("", true), port); 859 860 return dns_server_.Start(params, 861 command_line_reader::ReadTtl(kTtlDefault), 862 CreateTxt()); 863 } 864 865 bool Printer::StartHttpServer() { 866 DCHECK(state_.local_settings.local_discovery); 867 using command_line_reader::ReadHttpPort; 868 return http_server_.Start(ReadHttpPort(kHttpPortDefault)); 869 } 870 871 PrivetHttpServer::RegistrationErrorStatus 872 Printer::ConfirmationToRegistrationError( 873 PrinterState::ConfirmationState state) { 874 switch (state) { 875 case PrinterState::CONFIRMATION_PENDING: 876 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; 877 case PrinterState::CONFIRMATION_DISCARDED: 878 return PrivetHttpServer::REG_ERROR_USER_CANCEL; 879 case PrinterState::CONFIRMATION_CONFIRMED: 880 NOTREACHED(); 881 return PrivetHttpServer::REG_ERROR_OK; 882 case PrinterState::CONFIRMATION_TIMEOUT: 883 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT; 884 default: 885 NOTREACHED(); 886 return PrivetHttpServer::REG_ERROR_OK; 887 } 888 } 889 890 std::string Printer::ConnectionStateToString(ConnectionState state) const { 891 switch (state) { 892 case OFFLINE: 893 return "offline"; 894 case ONLINE: 895 return "online"; 896 case CONNECTING: 897 return "connecting"; 898 case NOT_CONFIGURED: 899 return "not-configured"; 900 901 default: 902 NOTREACHED(); 903 return ""; 904 } 905 } 906 907 void Printer::FallOffline(bool instant_reconnect) { 908 bool changed = ChangeState(OFFLINE); 909 DCHECK(changed) << "Falling offline from offline is now allowed"; 910 911 if (!IsRegistered()) 912 SetRegistrationError("Cannot access server during registration process"); 913 914 if (instant_reconnect) { 915 TryConnect(); 916 } else { 917 base::MessageLoop::current()->PostDelayedTask( 918 FROM_HERE, 919 base::Bind(&Printer::TryConnect, AsWeakPtr()), 920 base::TimeDelta::FromSeconds(kReconnectTimeout)); 921 } 922 } 923 924 bool Printer::ChangeState(ConnectionState new_state) { 925 if (connection_state_ == new_state) 926 return false; 927 928 connection_state_ = new_state; 929 VLOG(0) << base::StringPrintf( 930 "Printer is now %s (%s)", 931 ConnectionStateToString(connection_state_).c_str(), 932 IsRegistered() ? "registered" : "unregistered"); 933 934 dns_server_.UpdateMetadata(CreateTxt()); 935 936 if (connection_state_ == OFFLINE) { 937 requester_.reset(); 938 xmpp_listener_.reset(); 939 } 940 941 return true; 942 } 943