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