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 <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