Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / cloud_print / gcp20 / prototype / printer.cc
blob9820ace41fa7faf77154018b24dbbfdd6917e68a
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 (base::CommandLine::ForCurrentProcess()->HasSwitch(
237 "disable-confirmation")) {
238 state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED;
239 VLOG(0) << "Registration confirmed by default.";
240 } else {
241 LOG(WARNING) << kUserConfirmationTitle;
242 base::Time valid_until = base::Time::Now() +
243 base::TimeDelta::FromSeconds(kUserConfirmationTimeout);
244 base::MessageLoop::current()->PostTask(
245 FROM_HERE,
246 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until));
249 requester_->StartRegistration(GenerateProxyId(), kPrinterName, user,
250 state_.local_settings, GetRawCdd());
252 return PrivetHttpServer::REG_ERROR_OK;
255 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken(
256 const std::string& user,
257 std::string* token,
258 std::string* claim_url) {
259 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
260 if (status != PrivetHttpServer::REG_ERROR_OK)
261 return status;
263 // Check if |action=start| was called, but |action=complete| wasn't.
264 if (state_.registration_state != PrinterState::REGISTRATION_STARTED &&
265 state_.registration_state !=
266 PrinterState::REGISTRATION_CLAIM_TOKEN_READY) {
267 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
270 // If |action=getClaimToken| is valid in this state (was checked above) then
271 // check confirmation status.
272 if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED)
273 return ConfirmationToRegistrationError(state_.confirmation_state);
275 UpdateRegistrationExpiration();
277 // If reply wasn't received yet, reply with |pending_user_action| error.
278 if (state_.registration_state == PrinterState::REGISTRATION_STARTED)
279 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION;
281 DCHECK_EQ(state_.confirmation_state, PrinterState::CONFIRMATION_CONFIRMED);
282 DCHECK_EQ(state_.registration_state,
283 PrinterState::REGISTRATION_CLAIM_TOKEN_READY);
285 *token = state_.registration_token;
286 *claim_url = state_.complete_invite_url;
287 return PrivetHttpServer::REG_ERROR_OK;
290 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete(
291 const std::string& user,
292 std::string* device_id) {
293 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
294 if (status != PrivetHttpServer::REG_ERROR_OK)
295 return status;
297 if (state_.registration_state != PrinterState::REGISTRATION_CLAIM_TOKEN_READY)
298 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
300 UpdateRegistrationExpiration();
302 if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED)
303 return ConfirmationToRegistrationError(state_.confirmation_state);
305 state_.registration_state = PrinterState::REGISTRATION_COMPLETING;
306 requester_->CompleteRegistration();
307 *device_id = state_.device_id;
309 return PrivetHttpServer::REG_ERROR_OK;
312 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel(
313 const std::string& user) {
314 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
315 if (status != PrivetHttpServer::REG_ERROR_OK &&
316 status != PrivetHttpServer::REG_ERROR_SERVER_ERROR) {
317 return status;
320 if (state_.registration_state == PrinterState::UNREGISTERED)
321 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
323 InvalidateRegistrationExpiration();
325 state_ = PrinterState();
327 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
329 return PrivetHttpServer::REG_ERROR_OK;
332 void Printer::GetRegistrationServerError(std::string* description) {
333 DCHECK_EQ(state_.registration_state, PrinterState::REGISTRATION_ERROR)
334 << "Method shouldn't be called when not needed.";
336 *description = state_.error_description;
339 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
340 CheckRegistrationExpiration();
342 // TODO(maksymb): Replace "text" with constants.
343 *info = PrivetHttpServer::DeviceInfo();
344 info->version = "1.0";
345 info->name = kPrinterName;
346 info->description = GetDescription();
347 info->url = kCloudPrintUrl;
348 info->id = state_.device_id;
349 info->device_state = "idle";
350 info->connection_state = ConnectionStateToString(connection_state_);
351 info->manufacturer = COMPANY_FULLNAME_STRING;
352 info->model = "Prototype r" + std::string(LASTCHANGE_STRING);
353 info->serial_number = "20CB5FF2-B28C-4EFA-8DCD-516CFF0455A2";
354 info->firmware = CHROME_VERSION_STRING;
355 info->uptime = static_cast<int>((base::Time::Now() - starttime_).InSeconds());
357 info->x_privet_token = xtoken_.GenerateXToken();
359 // TODO(maksymb): Create enum for available APIs and replace
360 // this API text names with constants from enum. API text names should be only
361 // known in PrivetHttpServer.
362 if (!IsRegistered()) {
363 info->api.push_back("/privet/register");
364 } else {
365 info->api.push_back("/privet/capabilities");
366 if (IsLocalPrintingAllowed()) {
367 info->api.push_back("/privet/printer/createjob");
368 info->api.push_back("/privet/printer/submitdoc");
369 info->api.push_back("/privet/printer/jobstate");
373 info->type.push_back("printer");
376 bool Printer::IsRegistered() const {
377 return state_.registration_state == PrinterState::REGISTERED;
380 bool Printer::IsLocalPrintingAllowed() const {
381 return state_.local_settings.local_printing_enabled;
384 bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
385 return xtoken_.CheckValidXToken(token);
388 const base::DictionaryValue& Printer::GetCapabilities() {
389 if (!state_.cdd.get()) {
390 std::string cdd_string;
391 base::ReplaceChars(kCdd, "'", "\"", &cdd_string);
392 scoped_ptr<base::Value> json_val(base::JSONReader::Read(cdd_string));
393 base::DictionaryValue* json = NULL;
394 CHECK(json_val->GetAsDictionary(&json));
395 state_.cdd.reset(json->DeepCopy());
397 return *state_.cdd;
400 LocalPrintJob::CreateResult Printer::CreateJob(const std::string& ticket,
401 std::string* job_id,
402 int* expires_in,
403 int* error_timeout,
404 std::string* error_description) {
405 return print_job_handler_->CreatePrintJob(ticket, job_id, expires_in,
406 error_timeout, error_description);
409 LocalPrintJob::SaveResult Printer::SubmitDoc(const LocalPrintJob& job,
410 std::string* job_id,
411 int* expires_in,
412 std::string* error_description,
413 int* timeout) {
414 return print_job_handler_->SaveLocalPrintJob(job, job_id, expires_in,
415 error_description, timeout);
418 LocalPrintJob::SaveResult Printer::SubmitDocWithId(
419 const LocalPrintJob& job,
420 const std::string& job_id,
421 int* expires_in,
422 std::string* error_description,
423 int* timeout) {
424 return print_job_handler_->CompleteLocalPrintJob(job, job_id, expires_in,
425 error_description, timeout);
428 bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) {
429 return print_job_handler_->GetJobState(id, info);
432 void Printer::OnRegistrationStartResponseParsed(
433 const std::string& registration_token,
434 const std::string& complete_invite_url,
435 const std::string& device_id) {
436 state_.registration_state = PrinterState::REGISTRATION_CLAIM_TOKEN_READY;
437 state_.device_id = device_id;
438 state_.registration_token = registration_token;
439 state_.complete_invite_url = complete_invite_url;
442 void Printer::OnRegistrationFinished(const std::string& refresh_token,
443 const std::string& access_token,
444 int access_token_expires_in_seconds) {
445 InvalidateRegistrationExpiration();
447 state_.registration_state = PrinterState::REGISTERED;
448 state_.refresh_token = refresh_token;
449 RememberAccessToken(access_token, access_token_expires_in_seconds);
450 TryConnect();
453 void Printer::OnAccesstokenReceviced(const std::string& access_token,
454 int expires_in_seconds) {
455 VLOG(3) << "Function: " << __FUNCTION__;
456 RememberAccessToken(access_token, expires_in_seconds);
457 switch (connection_state_) {
458 case ONLINE:
459 PostOnIdle();
460 break;
462 case CONNECTING:
463 TryConnect();
464 break;
466 default:
467 NOTREACHED();
471 void Printer::OnXmppJidReceived(const std::string& xmpp_jid) {
472 state_.xmpp_jid = xmpp_jid;
475 void Printer::OnRegistrationError(const std::string& description) {
476 LOG(ERROR) << "server_error: " << description;
478 SetRegistrationError(description);
481 void Printer::OnNetworkError() {
482 VLOG(3) << "Function: " << __FUNCTION__;
483 FallOffline(false);
486 void Printer::OnServerError(const std::string& description) {
487 VLOG(3) << "Function: " << __FUNCTION__;
488 LOG(ERROR) << "Server error: " << description;
489 FallOffline(false);
492 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) {
493 VLOG(3) << "Function: " << __FUNCTION__;
495 VLOG(0) << "Available printjobs: " << jobs.size();
496 if (jobs.empty()) {
497 pending_print_jobs_check_ = false;
498 PostOnIdle();
499 return;
502 VLOG(0) << "Downloading printjob.";
503 requester_->RequestPrintJob(jobs[0]);
504 return;
507 void Printer::OnPrintJobDownloaded(const Job& job) {
508 VLOG(3) << "Function: " << __FUNCTION__;
509 print_job_handler_->SavePrintJob(job.file, job.ticket, job.create_time,
510 job.job_id, job.title, "pdf");
511 requester_->SendPrintJobDone(job.job_id);
514 void Printer::OnPrintJobDone() {
515 VLOG(3) << "Function: " << __FUNCTION__;
516 PostOnIdle();
519 void Printer::OnLocalSettingsReceived(LocalSettings::State state,
520 const LocalSettings& settings) {
521 pending_local_settings_check_ = false;
522 switch (state) {
523 case LocalSettings::CURRENT:
524 VLOG(0) << "No new local settings";
525 PostOnIdle();
526 break;
527 case LocalSettings::PENDING:
528 VLOG(0) << "New local settings were received";
529 ApplyLocalSettings(settings);
530 break;
531 case LocalSettings::PRINTER_DELETED:
532 LOG(WARNING) << "Printer was deleted on server";
533 pending_deletion_ = true;
534 PostOnIdle();
535 break;
537 default:
538 NOTREACHED();
542 void Printer::OnLocalSettingsUpdated() {
543 PostOnIdle();
546 void Printer::OnXmppConnected() {
547 pending_local_settings_check_ = true;
548 pending_print_jobs_check_ = true;
549 ChangeState(ONLINE);
550 PostOnIdle();
553 void Printer::OnXmppAuthError() {
554 OnAuthError();
557 void Printer::OnXmppNetworkError() {
558 FallOffline(false);
561 void Printer::OnXmppNewPrintJob(const std::string& device_id) {
562 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id";
563 pending_print_jobs_check_ = true;
566 void Printer::OnXmppNewLocalSettings(const std::string& device_id) {
567 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id";
568 pending_local_settings_check_ = true;
571 void Printer::OnXmppDeleteNotification(const std::string& device_id) {
572 DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id";
573 pending_deletion_ = true;
576 void Printer::TryConnect() {
577 VLOG(3) << "Function: " << __FUNCTION__;
579 ChangeState(CONNECTING);
580 if (!requester_)
581 requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
583 if (IsRegistered()) {
584 if (state_.access_token_update < base::Time::Now()) {
585 requester_->UpdateAccesstoken(state_.refresh_token);
586 } else {
587 ConnectXmpp();
589 } else {
590 // TODO(maksymb): Ping google.com to check connection state.
591 ChangeState(ONLINE);
595 void Printer::ConnectXmpp() {
596 xmpp_listener_.reset(
597 new CloudPrintXmppListener(state_.xmpp_jid,
598 state_.local_settings.xmpp_timeout_value,
599 GetTaskRunner(), this));
600 xmpp_listener_->Connect(state_.access_token);
603 void Printer::OnIdle() {
604 DCHECK(IsRegistered());
605 DCHECK(on_idle_posted_) << "Instant call is not allowed";
606 on_idle_posted_ = false;
608 if (connection_state_ != ONLINE)
609 return;
611 if (pending_deletion_) {
612 OnPrinterDeleted();
613 return;
616 if (state_.access_token_update < base::Time::Now()) {
617 requester_->UpdateAccesstoken(state_.refresh_token);
618 return;
621 // TODO(maksymb): Check if privet-accesstoken was requested.
623 if (pending_local_settings_check_) {
624 GetLocalSettings();
625 return;
628 if (pending_print_jobs_check_) {
629 FetchPrintJobs();
630 return;
633 base::MessageLoop::current()->PostDelayedTask(
634 FROM_HERE,
635 base::Bind(&Printer::PostOnIdle, AsWeakPtr()),
636 base::TimeDelta::FromMilliseconds(1000));
639 void Printer::FetchPrintJobs() {
640 VLOG(3) << "Function: " << __FUNCTION__;
641 DCHECK(IsRegistered());
642 requester_->FetchPrintJobs(state_.device_id);
645 void Printer::GetLocalSettings() {
646 VLOG(3) << "Function: " << __FUNCTION__;
647 DCHECK(IsRegistered());
648 requester_->RequestLocalSettings(state_.device_id);
651 void Printer::ApplyLocalSettings(const LocalSettings& settings) {
652 state_.local_settings = settings;
653 SaveToFile();
655 if (state_.local_settings.local_discovery) {
656 bool success = StartLocalDiscoveryServers();
657 if (!success)
658 LOG(ERROR) << "Local discovery servers cannot be started";
659 // TODO(maksymb): If start failed try to start them again after some timeout
660 } else {
661 dns_server_.Shutdown();
662 http_server_.Shutdown();
664 xmpp_listener_->set_ping_interval(state_.local_settings.xmpp_timeout_value);
666 requester_->SendLocalSettings(state_.device_id, state_.local_settings);
669 void Printer::OnPrinterDeleted() {
670 pending_deletion_ = false;
672 state_ = PrinterState();
674 SaveToFile();
675 Stop();
676 Start();
679 void Printer::RememberAccessToken(const std::string& access_token,
680 int expires_in_seconds) {
681 using base::Time;
682 using base::TimeDelta;
683 state_.access_token = access_token;
684 int64 time_to_update = static_cast<int64>(expires_in_seconds *
685 kTimeToNextAccessTokenUpdate);
686 state_.access_token_update =
687 Time::Now() + TimeDelta::FromSeconds(time_to_update);
688 VLOG(0) << "Current access_token: " << access_token;
689 SaveToFile();
692 void Printer::SetRegistrationError(const std::string& description) {
693 DCHECK(!IsRegistered());
694 state_.registration_state = PrinterState::REGISTRATION_ERROR;
695 state_.error_description = description;
698 PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors(
699 const std::string& user) {
700 CheckRegistrationExpiration();
701 DCHECK(!IsRegistered());
702 if (connection_state_ != ONLINE)
703 return PrivetHttpServer::REG_ERROR_OFFLINE;
705 if (state_.registration_state != PrinterState::UNREGISTERED &&
706 user != state_.user) {
707 return PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
710 if (state_.registration_state == PrinterState::REGISTRATION_ERROR)
711 return PrivetHttpServer::REG_ERROR_SERVER_ERROR;
713 DCHECK_EQ(connection_state_, ONLINE);
715 return PrivetHttpServer::REG_ERROR_OK;
718 void Printer::WaitUserConfirmation(base::Time valid_until) {
719 // TODO(maksymb): Move to separate class.
721 if (base::Time::Now() > valid_until) {
722 state_.confirmation_state = PrinterState::CONFIRMATION_TIMEOUT;
723 VLOG(0) << "Confirmation timeout reached.";
724 return;
727 if (_kbhit()) {
728 int c = _getche();
729 if (c == 'y' || c == 'Y') {
730 state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED;
731 VLOG(0) << "Registration confirmed by user.";
732 } else {
733 state_.confirmation_state = PrinterState::CONFIRMATION_DISCARDED;
734 VLOG(0) << "Registration discarded by user.";
736 return;
739 base::MessageLoop::current()->PostDelayedTask(
740 FROM_HERE,
741 base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until),
742 base::TimeDelta::FromMilliseconds(100));
745 std::string Printer::GenerateProxyId() const {
746 return "{" + base::GenerateGUID() +"}";
749 std::vector<std::string> Printer::CreateTxt() const {
750 std::vector<std::string> txt;
751 txt.push_back("txtvers=1");
752 txt.push_back("ty=" + std::string(kPrinterName));
753 txt.push_back("note=" + std::string(GetDescription()));
754 txt.push_back("url=" + std::string(kCloudPrintUrl));
755 txt.push_back("type=printer");
756 txt.push_back("id=" + state_.device_id);
757 txt.push_back("cs=" + ConnectionStateToString(connection_state_));
759 return txt;
762 void Printer::SaveToFile() {
763 GetCapabilities(); // Make sure capabilities created.
764 base::FilePath file_path;
765 file_path = file_path.AppendASCII(
766 command_line_reader::ReadStatePath(kPrinterStatePathDefault));
768 if (printer_state::SaveToFile(file_path, state_)) {
769 VLOG(0) << "Printer state written to file";
770 } else {
771 VLOG(0) << "Cannot write printer state to file";
775 bool Printer::LoadFromFile() {
776 state_ = PrinterState();
778 base::FilePath file_path;
779 file_path = file_path.AppendASCII(
780 command_line_reader::ReadStatePath(kPrinterStatePathDefault));
782 if (!base::PathExists(file_path)) {
783 VLOG(0) << "Printer state file not found";
784 return false;
787 if (printer_state::LoadFromFile(file_path, &state_)) {
788 VLOG(0) << "Printer state loaded from file";
789 SaveToFile();
790 } else {
791 VLOG(0) << "Reading/parsing printer state from file failed";
794 return true;
797 void Printer::PostOnIdle() {
798 VLOG(3) << "Function: " << __FUNCTION__;
799 DCHECK(!on_idle_posted_) << "Only one instance can be posted.";
800 on_idle_posted_ = true;
802 base::MessageLoop::current()->PostTask(
803 FROM_HERE,
804 base::Bind(&Printer::OnIdle, AsWeakPtr()));
807 void Printer::CheckRegistrationExpiration() {
808 if (!registration_expiration_.is_null() &&
809 registration_expiration_ < base::Time::Now()) {
810 state_ = PrinterState();
811 InvalidateRegistrationExpiration();
815 void Printer::UpdateRegistrationExpiration() {
816 registration_expiration_ =
817 base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout);
820 void Printer::InvalidateRegistrationExpiration() {
821 registration_expiration_ = base::Time();
824 bool Printer::StartLocalDiscoveryServers() {
825 if (!StartHttpServer())
826 return false;
827 if (!StartDnsServer()) {
828 http_server_.Shutdown();
829 return false;
831 return true;
834 bool Printer::StartDnsServer() {
835 DCHECK(state_.local_settings.local_discovery);
837 net::IPAddressNumber ipv4;
838 net::IPAddressNumber ipv6;
840 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
841 switches::kDisableIpv4)) {
842 ipv4 = GetLocalIp("", false);
844 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
845 switches::kDisableIpv6)) {
846 ipv6 = GetLocalIp("", true);
849 // TODO(maksymb): Add switch for command line to control interface name.
850 if (ipv4.empty() && ipv6.empty()) {
851 LOG(ERROR) << "No local IP found. Cannot start printer.";
852 return false;
855 uint16 port = command_line_reader::ReadHttpPort(kHttpPortDefault);
857 VLOG_IF(0, !ipv4.empty())
858 << "Local IPv4 address: " << net::IPAddressToStringWithPort(ipv4, port);
859 VLOG_IF(0, !ipv6.empty())
860 << "Local IPv6 address: " << net::IPAddressToStringWithPort(ipv6, port);
862 std::string service_name_prefix = kServiceNamePrefixDefault;
863 if (!ipv4.empty())
864 service_name_prefix += net::IPAddressToString(ipv4);
865 service_name_prefix =
866 command_line_reader::ReadServiceNamePrefix(service_name_prefix);
867 std::replace(
868 service_name_prefix.begin(), service_name_prefix.end(), '.', '_');
870 std::string service_domain_name =
871 command_line_reader::ReadDomainName(
872 base::StringPrintf(kServiceDomainNameFormatDefault,
873 base::RandInt(0, INT_MAX)));
875 ServiceParameters params(kServiceType,
876 kSecondaryServiceType,
877 service_name_prefix,
878 service_domain_name,
879 ipv4,
880 ipv6,
881 port);
883 return dns_server_.Start(params,
884 command_line_reader::ReadTtl(kTtlDefault),
885 CreateTxt());
888 bool Printer::StartHttpServer() {
889 DCHECK(state_.local_settings.local_discovery);
890 using command_line_reader::ReadHttpPort;
891 return http_server_.Start(ReadHttpPort(kHttpPortDefault));
894 PrivetHttpServer::RegistrationErrorStatus
895 Printer::ConfirmationToRegistrationError(
896 PrinterState::ConfirmationState state) {
897 switch (state) {
898 case PrinterState::CONFIRMATION_PENDING:
899 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION;
900 case PrinterState::CONFIRMATION_DISCARDED:
901 return PrivetHttpServer::REG_ERROR_USER_CANCEL;
902 case PrinterState::CONFIRMATION_CONFIRMED:
903 NOTREACHED();
904 return PrivetHttpServer::REG_ERROR_OK;
905 case PrinterState::CONFIRMATION_TIMEOUT:
906 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT;
907 default:
908 NOTREACHED();
909 return PrivetHttpServer::REG_ERROR_OK;
913 std::string Printer::ConnectionStateToString(ConnectionState state) const {
914 switch (state) {
915 case OFFLINE:
916 return "offline";
917 case ONLINE:
918 return "online";
919 case CONNECTING:
920 return "connecting";
921 case NOT_CONFIGURED:
922 return "not-configured";
924 default:
925 NOTREACHED();
926 return "";
930 void Printer::FallOffline(bool instant_reconnect) {
931 bool changed = ChangeState(OFFLINE);
932 DCHECK(changed) << "Falling offline from offline is now allowed";
934 if (!IsRegistered())
935 SetRegistrationError("Cannot access server during registration process");
937 if (instant_reconnect) {
938 TryConnect();
939 } else {
940 base::MessageLoop::current()->PostDelayedTask(
941 FROM_HERE,
942 base::Bind(&Printer::TryConnect, AsWeakPtr()),
943 base::TimeDelta::FromSeconds(kReconnectTimeout));
947 bool Printer::ChangeState(ConnectionState new_state) {
948 if (connection_state_ == new_state)
949 return false;
951 connection_state_ = new_state;
952 VLOG(0) << base::StringPrintf(
953 "Printer is now %s (%s)",
954 ConnectionStateToString(connection_state_).c_str(),
955 IsRegistered() ? "registered" : "unregistered");
957 dns_server_.UpdateMetadata(CreateTxt());
959 if (connection_state_ == OFFLINE) {
960 requester_.reset();
961 xmpp_listener_.reset();
964 return true;