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()->task_runner();
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 (base::CommandLine::ForCurrentProcess()->HasSwitch(
237 "disable-confirmation")) {
238 state_
.confirmation_state
= PrinterState::CONFIRMATION_CONFIRMED
;
239 VLOG(0) << "Registration confirmed by default.";
241 LOG(WARNING
) << kUserConfirmationTitle
;
242 base::Time valid_until
= base::Time::Now() +
243 base::TimeDelta::FromSeconds(kUserConfirmationTimeout
);
244 base::MessageLoop::current()->PostTask(
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
,
258 std::string
* claim_url
) {
259 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
260 if (status
!= PrivetHttpServer::REG_ERROR_OK
)
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
)
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
) {
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");
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());
400 LocalPrintJob::CreateResult
Printer::CreateJob(const std::string
& ticket
,
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
,
412 std::string
* error_description
,
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
,
422 std::string
* error_description
,
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
);
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_
) {
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__
;
486 void Printer::OnServerError(const std::string
& description
) {
487 VLOG(3) << "Function: " << __FUNCTION__
;
488 LOG(ERROR
) << "Server error: " << description
;
492 void Printer::OnPrintJobsAvailable(const std::vector
<Job
>& jobs
) {
493 VLOG(3) << "Function: " << __FUNCTION__
;
495 VLOG(0) << "Available printjobs: " << jobs
.size();
497 pending_print_jobs_check_
= false;
502 VLOG(0) << "Downloading printjob.";
503 requester_
->RequestPrintJob(jobs
[0]);
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__
;
519 void Printer::OnLocalSettingsReceived(LocalSettings::State state
,
520 const LocalSettings
& settings
) {
521 pending_local_settings_check_
= false;
523 case LocalSettings::CURRENT
:
524 VLOG(0) << "No new local settings";
527 case LocalSettings::PENDING
:
528 VLOG(0) << "New local settings were received";
529 ApplyLocalSettings(settings
);
531 case LocalSettings::PRINTER_DELETED
:
532 LOG(WARNING
) << "Printer was deleted on server";
533 pending_deletion_
= true;
542 void Printer::OnLocalSettingsUpdated() {
546 void Printer::OnXmppConnected() {
547 pending_local_settings_check_
= true;
548 pending_print_jobs_check_
= true;
553 void Printer::OnXmppAuthError() {
557 void Printer::OnXmppNetworkError() {
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
);
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
);
590 // TODO(maksymb): Ping google.com to check connection state.
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
)
611 if (pending_deletion_
) {
616 if (state_
.access_token_update
< base::Time::Now()) {
617 requester_
->UpdateAccesstoken(state_
.refresh_token
);
621 // TODO(maksymb): Check if privet-accesstoken was requested.
623 if (pending_local_settings_check_
) {
628 if (pending_print_jobs_check_
) {
633 base::MessageLoop::current()->PostDelayedTask(
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
;
655 if (state_
.local_settings
.local_discovery
) {
656 bool success
= StartLocalDiscoveryServers();
658 LOG(ERROR
) << "Local discovery servers cannot be started";
659 // TODO(maksymb): If start failed try to start them again after some timeout
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();
679 void Printer::RememberAccessToken(const std::string
& access_token
,
680 int expires_in_seconds
) {
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
;
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.";
729 if (c
== 'y' || c
== 'Y') {
730 state_
.confirmation_state
= PrinterState::CONFIRMATION_CONFIRMED
;
731 VLOG(0) << "Registration confirmed by user.";
733 state_
.confirmation_state
= PrinterState::CONFIRMATION_DISCARDED
;
734 VLOG(0) << "Registration discarded by user.";
739 base::MessageLoop::current()->PostDelayedTask(
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_
));
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";
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";
787 if (printer_state::LoadFromFile(file_path
, &state_
)) {
788 VLOG(0) << "Printer state loaded from file";
791 VLOG(0) << "Reading/parsing printer state from file failed";
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(
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())
827 if (!StartDnsServer()) {
828 http_server_
.Shutdown();
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.";
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
;
864 service_name_prefix
+= net::IPAddressToString(ipv4
);
865 service_name_prefix
=
866 command_line_reader::ReadServiceNamePrefix(service_name_prefix
);
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
,
883 return dns_server_
.Start(params
,
884 command_line_reader::ReadTtl(kTtlDefault
),
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
) {
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
:
904 return PrivetHttpServer::REG_ERROR_OK
;
905 case PrinterState::CONFIRMATION_TIMEOUT
:
906 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT
;
909 return PrivetHttpServer::REG_ERROR_OK
;
913 std::string
Printer::ConnectionStateToString(ConnectionState state
) const {
922 return "not-configured";
930 void Printer::FallOffline(bool instant_reconnect
) {
931 bool changed
= ChangeState(OFFLINE
);
932 DCHECK(changed
) << "Falling offline from offline is now allowed";
935 SetRegistrationError("Cannot access server during registration process");
937 if (instant_reconnect
) {
940 base::MessageLoop::current()->PostDelayedTask(
942 base::Bind(&Printer::TryConnect
, AsWeakPtr()),
943 base::TimeDelta::FromSeconds(kReconnectTimeout
));
947 bool Printer::ChangeState(ConnectionState new_state
) {
948 if (connection_state_
== new_state
)
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
) {
961 xmpp_listener_
.reset();