Roll src/third_party/WebKit e573d3b:bb268e6 (svn 184923:184924)
[chromium-blink-merge.git] / cloud_print / gcp20 / prototype / printer.cc
blob24866a5ea2dda3995453337f2996a83188f133d0
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.
5 #include "cloud_print/gcp20/prototype/printer.h"
7 #include <limits.h>
8 #include <stdio.h>
9 #include <algorithm>
10 #include <string>
11 #include <vector>
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"
33 const char kPrinterStatePathDefault[] = "printer_state.json";
35 namespace {
37 const uint16 kHttpPortDefault = 10101;
38 const uint32 kTtlDefault = 60*60; // in seconds
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";
45 const char kPrinterName[] = "Google GCP2.0 Prototype";
46 const char kPrinterDescription[] = "Printer emulator";
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
54 const double kTimeToNextAccessTokenUpdate = 0.8; // relatively to living time.
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 "}";
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);
120 size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize
121 : net::kIPv4AddressSize;
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;
131 return net::IPAddressNumber();
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;
142 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
143 return base::MessageLoop::current()->message_loop_proxy();
146 } // namespace
148 using cloud_print_response_parser::Job;
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) {
159 Printer::~Printer() {
160 Stop();
163 bool Printer::Start() {
164 if (IsRunning())
165 return true;
167 LoadFromFile();
169 if (state_.local_settings.local_discovery && !StartLocalDiscoveryServers())
170 return false;
172 print_job_handler_.reset(new PrintJobHandler);
173 xtoken_ = XPrivetToken();
174 starttime_ = base::Time::Now();
176 TryConnect();
177 return true;
180 bool Printer::IsRunning() const {
181 return print_job_handler_;
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();
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;
202 void Printer::OnAuthError() {
203 LOG(ERROR) << "Auth error occurred";
204 state_.access_token_update = base::Time();
205 FallOffline(true);
208 std::string Printer::GetAccessToken() {
209 return state_.access_token;
212 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
213 const std::string& user) {
214 CheckRegistrationExpiration();
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();
223 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
224 if (status != PrivetHttpServer::REG_ERROR_OK)
225 return status;
227 if (state_.registration_state != PrinterState::UNREGISTERED)
228 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
230 UpdateRegistrationExpiration();
232 state_ = PrinterState();
233 state_.user = user;
234 state_.registration_state = PrinterState::REGISTRATION_STARTED;
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));
248 requester_->StartRegistration(GenerateProxyId(), kPrinterName, user,
249 state_.local_settings, GetRawCdd());
251 return PrivetHttpServer::REG_ERROR_OK;
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;
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;
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);
274 UpdateRegistrationExpiration();
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;
280 DCHECK_EQ(state_.confirmation_state, PrinterState::CONFIRMATION_CONFIRMED);
281 DCHECK_EQ(state_.registration_state,
282 PrinterState::REGISTRATION_CLAIM_TOKEN_READY);
284 *token = state_.registration_token;
285 *claim_url = state_.complete_invite_url;
286 return PrivetHttpServer::REG_ERROR_OK;
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;
296 if (state_.registration_state != PrinterState::REGISTRATION_CLAIM_TOKEN_READY)
297 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
299 UpdateRegistrationExpiration();
301 if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED)
302 return ConfirmationToRegistrationError(state_.confirmation_state);
304 state_.registration_state = PrinterState::REGISTRATION_COMPLETING;
305 requester_->CompleteRegistration();
306 *device_id = state_.device_id;
308 return PrivetHttpServer::REG_ERROR_OK;
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;
319 if (state_.registration_state == PrinterState::UNREGISTERED)
320 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
322 InvalidateRegistrationExpiration();
324 state_ = PrinterState();
326 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
328 return PrivetHttpServer::REG_ERROR_OK;
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.";
335 *description = state_.error_description;
338 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
339 CheckRegistrationExpiration();
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());
356 info->x_privet_token = xtoken_.GenerateXToken();
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");
372 info->type.push_back("printer");
375 bool Printer::IsRegistered() const {
376 return state_.registration_state == PrinterState::REGISTERED;
379 bool Printer::IsLocalPrintingAllowed() const {
380 return state_.local_settings.local_printing_enabled;
383 bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
384 return xtoken_.CheckValidXToken(token);
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());
396 return *state_.cdd;
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);
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);
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);
427 bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) {
428 return print_job_handler_->GetJobState(id, info);
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;
441 void Printer::OnRegistrationFinished(const std::string& refresh_token,
442 const std::string& access_token,
443 int access_token_expires_in_seconds) {
444 InvalidateRegistrationExpiration();
446 state_.registration_state = PrinterState::REGISTERED;
447 state_.refresh_token = refresh_token;
448 RememberAccessToken(access_token, access_token_expires_in_seconds);
449 TryConnect();
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;
461 case CONNECTING:
462 TryConnect();
463 break;
465 default:
466 NOTREACHED();
470 void Printer::OnXmppJidReceived(const std::string& xmpp_jid) {
471 state_.xmpp_jid = xmpp_jid;
474 void Printer::OnRegistrationError(const std::string& description) {
475 LOG(ERROR) << "server_error: " << description;
477 SetRegistrationError(description);
480 void Printer::OnNetworkError() {
481 VLOG(3) << "Function: " << __FUNCTION__;
482 FallOffline(false);
485 void Printer::OnServerError(const std::string& description) {
486 VLOG(3) << "Function: " << __FUNCTION__;
487 LOG(ERROR) << "Server error: " << description;
488 FallOffline(false);
491 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) {
492 VLOG(3) << "Function: " << __FUNCTION__;
494 VLOG(0) << "Available printjobs: " << jobs.size();
495 if (jobs.empty()) {
496 pending_print_jobs_check_ = false;
497 PostOnIdle();
498 return;
501 VLOG(0) << "Downloading printjob.";
502 requester_->RequestPrintJob(jobs[0]);
503 return;
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);
513 void Printer::OnPrintJobDone() {
514 VLOG(3) << "Function: " << __FUNCTION__;
515 PostOnIdle();
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;
536 default:
537 NOTREACHED();
541 void Printer::OnLocalSettingsUpdated() {
542 PostOnIdle();
545 void Printer::OnXmppConnected() {
546 pending_local_settings_check_ = true;
547 pending_print_jobs_check_ = true;
548 ChangeState(ONLINE);
549 PostOnIdle();
552 void Printer::OnXmppAuthError() {
553 OnAuthError();
556 void Printer::OnXmppNetworkError() {
557 FallOffline(false);
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;
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;
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;
575 void Printer::TryConnect() {
576 VLOG(3) << "Function: " << __FUNCTION__;
578 ChangeState(CONNECTING);
579 if (!requester_)
580 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
582 if (IsRegistered()) {
583 if (state_.access_token_update < base::Time::Now()) {
584 requester_->UpdateAccesstoken(state_.refresh_token);
585 } else {
586 ConnectXmpp();
588 } else {
589 // TODO(maksymb): Ping google.com to check connection state.
590 ChangeState(ONLINE);
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);
602 void Printer::OnIdle() {
603 DCHECK(IsRegistered());
604 DCHECK(on_idle_posted_) << "Instant call is not allowed";
605 on_idle_posted_ = false;
607 if (connection_state_ != ONLINE)
608 return;
610 if (pending_deletion_) {
611 OnPrinterDeleted();
612 return;
615 if (state_.access_token_update < base::Time::Now()) {
616 requester_->UpdateAccesstoken(state_.refresh_token);
617 return;
620 // TODO(maksymb): Check if privet-accesstoken was requested.
622 if (pending_local_settings_check_) {
623 GetLocalSettings();
624 return;
627 if (pending_print_jobs_check_) {
628 FetchPrintJobs();
629 return;
632 base::MessageLoop::current()->PostDelayedTask(
633 FROM_HERE,
634 base::Bind(&Printer::PostOnIdle, AsWeakPtr()),
635 base::TimeDelta::FromMilliseconds(1000));
638 void Printer::FetchPrintJobs() {
639 VLOG(3) << "Function: " << __FUNCTION__;
640 DCHECK(IsRegistered());
641 requester_->FetchPrintJobs(state_.device_id);
644 void Printer::GetLocalSettings() {
645 VLOG(3) << "Function: " << __FUNCTION__;
646 DCHECK(IsRegistered());
647 requester_->RequestLocalSettings(state_.device_id);
650 void Printer::ApplyLocalSettings(const LocalSettings& settings) {
651 state_.local_settings = settings;
652 SaveToFile();
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();
663 xmpp_listener_->set_ping_interval(state_.local_settings.xmpp_timeout_value);
665 requester_->SendLocalSettings(state_.device_id, state_.local_settings);
668 void Printer::OnPrinterDeleted() {
669 pending_deletion_ = false;
671 state_ = PrinterState();
673 SaveToFile();
674 Stop();
675 Start();
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();
691 void Printer::SetRegistrationError(const std::string& description) {
692 DCHECK(!IsRegistered());
693 state_.registration_state = PrinterState::REGISTRATION_ERROR;
694 state_.error_description = description;
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;
704 if (state_.registration_state != PrinterState::UNREGISTERED &&
705 user != state_.user) {
706 return PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
709 if (state_.registration_state == PrinterState::REGISTRATION_ERROR)
710 return PrivetHttpServer::REG_ERROR_SERVER_ERROR;
712 DCHECK_EQ(connection_state_, ONLINE);
714 return PrivetHttpServer::REG_ERROR_OK;
717 void Printer::WaitUserConfirmation(base::Time valid_until) {
718 // TODO(maksymb): Move to separate class.
720 if (base::Time::Now() > valid_until) {
721 state_.confirmation_state = PrinterState::CONFIRMATION_TIMEOUT;
722 VLOG(0) << "Confirmation timeout reached.";
723 return;
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.";
735 return;
738 base::MessageLoop::current()->PostDelayedTask(
739 FROM_HERE,
740 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until),
741 base::TimeDelta::FromMilliseconds(100));
744 std::string Printer::GenerateProxyId() const {
745 return "{" + base::GenerateGUID() +"}";
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_));
758 return txt;
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));
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";
774 bool Printer::LoadFromFile() {
775 state_ = PrinterState();
777 base::FilePath file_path;
778 file_path = file_path.AppendASCII(
779 command_line_reader::ReadStatePath(kPrinterStatePathDefault));
781 if (!base::PathExists(file_path)) {
782 VLOG(0) << "Printer state file not found";
783 return false;
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";
793 return true;
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;
801 base::MessageLoop::current()->PostTask(
802 FROM_HERE,
803 base::Bind(&Printer::OnIdle, AsWeakPtr()));
806 void Printer::CheckRegistrationExpiration() {
807 if (!registration_expiration_.is_null() &&
808 registration_expiration_ < base::Time::Now()) {
809 state_ = PrinterState();
810 InvalidateRegistrationExpiration();
814 void Printer::UpdateRegistrationExpiration() {
815 registration_expiration_ =
816 base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout);
819 void Printer::InvalidateRegistrationExpiration() {
820 registration_expiration_ = base::Time();
823 bool Printer::StartLocalDiscoveryServers() {
824 if (!StartHttpServer())
825 return false;
826 if (!StartDnsServer()) {
827 http_server_.Shutdown();
828 return false;
830 return true;
833 bool Printer::StartDnsServer() {
834 DCHECK(state_.local_settings.local_discovery);
836 net::IPAddressNumber ipv4;
837 net::IPAddressNumber ipv6;
839 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
840 switches::kDisableIpv4)) {
841 ipv4 = GetLocalIp("", false);
843 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
844 switches::kDisableIpv6)) {
845 ipv6 = GetLocalIp("", true);
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;
854 uint16 port = command_line_reader::ReadHttpPort(kHttpPortDefault);
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);
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(), '.', '_');
869 std::string service_domain_name =
870 command_line_reader::ReadDomainName(
871 base::StringPrintf(kServiceDomainNameFormatDefault,
872 base::RandInt(0, INT_MAX)));
874 ServiceParameters params(kServiceType,
875 kSecondaryServiceType,
876 service_name_prefix,
877 service_domain_name,
878 ipv4,
879 ipv6,
880 port);
882 return dns_server_.Start(params,
883 command_line_reader::ReadTtl(kTtlDefault),
884 CreateTxt());
887 bool Printer::StartHttpServer() {
888 DCHECK(state_.local_settings.local_discovery);
889 using command_line_reader::ReadHttpPort;
890 return http_server_.Start(ReadHttpPort(kHttpPortDefault));
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;
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";
923 default:
924 NOTREACHED();
925 return "";
929 void Printer::FallOffline(bool instant_reconnect) {
930 bool changed = ChangeState(OFFLINE);
931 DCHECK(changed) << "Falling offline from offline is now allowed";
933 if (!IsRegistered())
934 SetRegistrationError("Cannot access server during registration process");
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));
946 bool Printer::ChangeState(ConnectionState new_state) {
947 if (connection_state_ == new_state)
948 return false;
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");
956 dns_server_.UpdateMetadata(CreateTxt());
958 if (connection_state_ == OFFLINE) {
959 requester_.reset();
960 xmpp_listener_.reset();
963 return true;