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"
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";
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.
60 " 'supported_content_type': ["
62 " 'content_type': 'application/pdf'"
65 " 'content_type': 'image/pwg-raster'"
68 " 'content_type': 'image/jpeg'"
74 " 'is_default': true,"
75 " 'type': 'STANDARD_COLOR'"
78 " 'type': 'STANDARD_MONOCHROME'"
84 " 'height_microns': 297000,"
86 " 'width_microns': 210000"
88 " 'custom_display_name': 'Letter',"
89 " 'height_microns': 279400,"
90 " 'is_default': true,"
91 " 'name': 'NA_LETTER',"
92 " 'width_microns': 215900"
95 " 'page_orientation': {"
97 " 'is_default': true,"
100 " 'type': 'LANDSCAPE'"
103 " 'reverse_order': {"
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
);
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);
138 result
+= " at " + net::IPAddressToString(ip
);
142 scoped_refptr
<base::SingleThreadTaskRunner
> GetTaskRunner() {
143 return base::MessageLoop::current()->message_loop_proxy();
148 using cloud_print_response_parser::Job
;
151 : connection_state_(OFFLINE
),
153 on_idle_posted_(false),
154 pending_local_settings_check_(false),
155 pending_print_jobs_check_(false),
156 pending_deletion_(false) {
159 Printer::~Printer() {
163 bool Printer::Start() {
169 if (state_
.local_settings
.local_discovery
&& !StartLocalDiscoveryServers())
172 print_job_handler_
.reset(new PrintJobHandler
);
173 xtoken_
= XPrivetToken();
174 starttime_
= base::Time::Now();
180 bool Printer::IsRunning() const {
181 return print_job_handler_
;
184 void Printer::Stop() {
187 dns_server_
.Shutdown();
188 http_server_
.Shutdown();
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
,
202 void Printer::OnAuthError() {
203 LOG(ERROR
) << "Auth error occurred";
204 state_
.access_token_update
= base::Time();
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
)
227 if (state_
.registration_state
!= PrinterState::UNREGISTERED
)
228 return PrivetHttpServer::REG_ERROR_INVALID_ACTION
;
230 UpdateRegistrationExpiration();
232 state_
= PrinterState();
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.";
240 LOG(WARNING
) << kUserConfirmationTitle
;
241 base::Time valid_until
= base::Time::Now() +
242 base::TimeDelta::FromSeconds(kUserConfirmationTimeout
);
243 base::MessageLoop::current()->PostTask(
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
,
257 std::string
* claim_url
) {
258 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
259 if (status
!= PrivetHttpServer::REG_ERROR_OK
)
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
)
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
) {
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");
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());
399 LocalPrintJob::CreateResult
Printer::CreateJob(const std::string
& ticket
,
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
,
411 std::string
* error_description
,
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
,
421 std::string
* error_description
,
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
);
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_
) {
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__
;
485 void Printer::OnServerError(const std::string
& description
) {
486 VLOG(3) << "Function: " << __FUNCTION__
;
487 LOG(ERROR
) << "Server error: " << description
;
491 void Printer::OnPrintJobsAvailable(const std::vector
<Job
>& jobs
) {
492 VLOG(3) << "Function: " << __FUNCTION__
;
494 VLOG(0) << "Available printjobs: " << jobs
.size();
496 pending_print_jobs_check_
= false;
501 VLOG(0) << "Downloading printjob.";
502 requester_
->RequestPrintJob(jobs
[0]);
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__
;
518 void Printer::OnLocalSettingsReceived(LocalSettings::State state
,
519 const LocalSettings
& settings
) {
520 pending_local_settings_check_
= false;
522 case LocalSettings::CURRENT
:
523 VLOG(0) << "No new local settings";
526 case LocalSettings::PENDING
:
527 VLOG(0) << "New local settings were received";
528 ApplyLocalSettings(settings
);
530 case LocalSettings::PRINTER_DELETED
:
531 LOG(WARNING
) << "Printer was deleted on server";
532 pending_deletion_
= true;
541 void Printer::OnLocalSettingsUpdated() {
545 void Printer::OnXmppConnected() {
546 pending_local_settings_check_
= true;
547 pending_print_jobs_check_
= true;
552 void Printer::OnXmppAuthError() {
556 void Printer::OnXmppNetworkError() {
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
);
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
);
589 // TODO(maksymb): Ping google.com to check connection state.
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
)
610 if (pending_deletion_
) {
615 if (state_
.access_token_update
< base::Time::Now()) {
616 requester_
->UpdateAccesstoken(state_
.refresh_token
);
620 // TODO(maksymb): Check if privet-accesstoken was requested.
622 if (pending_local_settings_check_
) {
627 if (pending_print_jobs_check_
) {
632 base::MessageLoop::current()->PostDelayedTask(
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
;
654 if (state_
.local_settings
.local_discovery
) {
655 bool success
= StartLocalDiscoveryServers();
657 LOG(ERROR
) << "Local discovery servers cannot be started";
658 // TODO(maksymb): If start failed try to start them again after some timeout
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();
678 void Printer::RememberAccessToken(const std::string
& access_token
,
679 int expires_in_seconds
) {
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
;
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.";
728 if (c
== 'y' || c
== 'Y') {
729 state_
.confirmation_state
= PrinterState::CONFIRMATION_CONFIRMED
;
730 VLOG(0) << "Registration confirmed by user.";
732 state_
.confirmation_state
= PrinterState::CONFIRMATION_DISCARDED
;
733 VLOG(0) << "Registration discarded by user.";
738 base::MessageLoop::current()->PostDelayedTask(
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_
));
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";
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";
786 if (printer_state::LoadFromFile(file_path
, &state_
)) {
787 VLOG(0) << "Printer state loaded from file";
790 VLOG(0) << "Reading/parsing printer state from file failed";
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(
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())
826 if (!StartDnsServer()) {
827 http_server_
.Shutdown();
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.";
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
;
863 service_name_prefix
+= net::IPAddressToString(ipv4
);
864 service_name_prefix
=
865 command_line_reader::ReadServiceNamePrefix(service_name_prefix
);
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
,
882 return dns_server_
.Start(params
,
883 command_line_reader::ReadTtl(kTtlDefault
),
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
) {
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
:
903 return PrivetHttpServer::REG_ERROR_OK
;
904 case PrinterState::CONFIRMATION_TIMEOUT
:
905 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT
;
908 return PrivetHttpServer::REG_ERROR_OK
;
912 std::string
Printer::ConnectionStateToString(ConnectionState state
) const {
921 return "not-configured";
929 void Printer::FallOffline(bool instant_reconnect
) {
930 bool changed
= ChangeState(OFFLINE
);
931 DCHECK(changed
) << "Falling offline from offline is now allowed";
934 SetRegistrationError("Cannot access server during registration process");
936 if (instant_reconnect
) {
939 base::MessageLoop::current()->PostDelayedTask(
941 base::Bind(&Printer::TryConnect
, AsWeakPtr()),
942 base::TimeDelta::FromSeconds(kReconnectTimeout
));
946 bool Printer::ChangeState(ConnectionState new_state
) {
947 if (connection_state_
== new_state
)
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
) {
960 xmpp_listener_
.reset();