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
33const char kPrinterStatePathDefault[] = "printer_state.json";
34
35namespace {
36
37const uint16 kHttpPortDefault = 10101;
38const uint32 kTtlDefault = 60*60;  // in seconds
39
40const char kServiceType[] = "_privet._tcp.local";
41const char kSecondaryServiceType[] = "_printer._sub._privet._tcp.local";
42const char kServiceNamePrefixDefault[] = "gcp20_device_";
43const char kServiceDomainNameFormatDefault[] = "my-privet-device%d.local";
44
45const char kPrinterName[] = "Google GCP2.0 Prototype";
46const char kPrinterDescription[] = "Printer emulator";
47
48const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you "
49                                      "agree and any other to discard\n";
50const int kUserConfirmationTimeout = 30;  // in seconds
51const int kRegistrationTimeout = 60;  // in seconds
52const int kReconnectTimeout = 5;  // in seconds
53
54const double kTimeToNextAccessTokenUpdate = 0.8;  // relatively to living time.
55
56const 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.
113net::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
134std::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
142scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
143  return base::MessageLoop::current()->message_loop_proxy();
144}
145
146}  // namespace
147
148using cloud_print_response_parser::Job;
149
150Printer::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
159Printer::~Printer() {
160  Stop();
161}
162
163bool 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
180bool Printer::IsRunning() const {
181  return print_job_handler_;
182}
183
184void 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
194std::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
202void Printer::OnAuthError() {
203  LOG(ERROR) << "Auth error occurred";
204  state_.access_token_update = base::Time();
205  FallOffline(true);
206}
207
208std::string Printer::GetAccessToken() {
209  return state_.access_token;
210}
211
212PrivetHttpServer::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
254PrivetHttpServer::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
289PrivetHttpServer::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
311PrivetHttpServer::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
331void 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
338void 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
375bool Printer::IsRegistered() const {
376  return state_.registration_state == PrinterState::REGISTERED;
377}
378
379bool Printer::IsLocalPrintingAllowed() const {
380  return state_.local_settings.local_printing_enabled;
381}
382
383bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
384  return xtoken_.CheckValidXToken(token);
385}
386
387const 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
399LocalPrintJob::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
408LocalPrintJob::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
417LocalPrintJob::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
427bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) {
428  return print_job_handler_->GetJobState(id, info);
429}
430
431void 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
441void 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
452void 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
470void Printer::OnXmppJidReceived(const std::string& xmpp_jid) {
471  state_.xmpp_jid = xmpp_jid;
472}
473
474void Printer::OnRegistrationError(const std::string& description) {
475  LOG(ERROR) << "server_error: " << description;
476
477  SetRegistrationError(description);
478}
479
480void Printer::OnNetworkError() {
481  VLOG(3) << "Function: " << __FUNCTION__;
482  FallOffline(false);
483}
484
485void Printer::OnServerError(const std::string& description) {
486  VLOG(3) << "Function: " << __FUNCTION__;
487  LOG(ERROR) << "Server error: " << description;
488  FallOffline(false);
489}
490
491void 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
506void 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
513void Printer::OnPrintJobDone() {
514  VLOG(3) << "Function: " << __FUNCTION__;
515  PostOnIdle();
516}
517
518void 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
541void Printer::OnLocalSettingsUpdated() {
542  PostOnIdle();
543}
544
545void Printer::OnXmppConnected() {
546  pending_local_settings_check_ = true;
547  pending_print_jobs_check_ = true;
548  ChangeState(ONLINE);
549  PostOnIdle();
550}
551
552void Printer::OnXmppAuthError() {
553  OnAuthError();
554}
555
556void Printer::OnXmppNetworkError() {
557  FallOffline(false);
558}
559
560void 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
565void 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
570void 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
575void 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
594void 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
602void 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
638void Printer::FetchPrintJobs() {
639  VLOG(3) << "Function: " << __FUNCTION__;
640  DCHECK(IsRegistered());
641  requester_->FetchPrintJobs(state_.device_id);
642}
643
644void Printer::GetLocalSettings() {
645  VLOG(3) << "Function: " << __FUNCTION__;
646  DCHECK(IsRegistered());
647  requester_->RequestLocalSettings(state_.device_id);
648}
649
650void 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
668void Printer::OnPrinterDeleted() {
669  pending_deletion_ = false;
670
671  state_ = PrinterState();
672
673  SaveToFile();
674  Stop();
675  Start();
676}
677
678void 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
691void Printer::SetRegistrationError(const std::string& description) {
692  DCHECK(!IsRegistered());
693  state_.registration_state = PrinterState::REGISTRATION_ERROR;
694  state_.error_description = description;
695}
696
697PrivetHttpServer::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
717void 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
744std::string Printer::GenerateProxyId() const {
745  return "{" + base::GenerateGUID() +"}";
746}
747
748std::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
761void 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
774bool 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
796void 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
806void Printer::CheckRegistrationExpiration() {
807  if (!registration_expiration_.is_null() &&
808      registration_expiration_ < base::Time::Now()) {
809    state_ = PrinterState();
810    InvalidateRegistrationExpiration();
811  }
812}
813
814void Printer::UpdateRegistrationExpiration() {
815  registration_expiration_ =
816      base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout);
817}
818
819void Printer::InvalidateRegistrationExpiration() {
820  registration_expiration_ = base::Time();
821}
822
823bool Printer::StartLocalDiscoveryServers() {
824  if (!StartHttpServer())
825    return false;
826  if (!StartDnsServer()) {
827    http_server_.Shutdown();
828    return false;
829  }
830  return true;
831}
832
833bool 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
887bool Printer::StartHttpServer() {
888  DCHECK(state_.local_settings.local_discovery);
889  using command_line_reader::ReadHttpPort;
890  return http_server_.Start(ReadHttpPort(kHttpPortDefault));
891}
892
893PrivetHttpServer::RegistrationErrorStatus
894Printer::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
912std::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
929void 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
946bool 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