Home | History | Annotate | Download | only in prototype
      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