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/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/local_settings.h"
26 #include "cloud_print/gcp20/prototype/service_parameters.h"
27 #include "cloud_print/gcp20/prototype/special_io.h"
28 #include "cloud_print/version.h"
29 #include "net/base/net_util.h"
30 #include "net/base/url_util.h"
32 const char kPrinterStatePathDefault
[] = "printer_state.json";
36 const uint16 kHttpPortDefault
= 10101;
37 const uint32 kTtlDefault
= 60*60; // in seconds
39 const char kServiceType
[] = "_privet._tcp.local";
40 const char kSecondaryServiceType
[] = "_printer._sub._privet._tcp.local";
41 const char kServiceNamePrefixDefault
[] = "gcp20_device_";
42 const char kServiceDomainNameFormatDefault
[] = "my-privet-device%d.local";
44 const char kPrinterName
[] = "Google GCP2.0 Prototype";
45 const char kPrinterDescription
[] = "Printer emulator";
47 const char kUserConfirmationTitle
[] = "Confirm registration: type 'y' if you "
48 "agree and any other to discard\n";
49 const int kUserConfirmationTimeout
= 30; // in seconds
50 const int kRegistrationTimeout
= 60; // in seconds
51 const int kReconnectTimeout
= 5; // in seconds
53 const double kTimeToNextAccessTokenUpdate
= 0.8; // relatively to living time.
59 " 'supported_content_type': ["
61 " 'content_type': 'application/pdf'"
64 " 'content_type': 'image/pwg-raster'"
67 " 'content_type': 'image/jpeg'"
73 " 'is_default': true,"
74 " 'type': 'STANDARD_COLOR'"
77 " 'type': 'STANDARD_MONOCHROME'"
83 " 'height_microns': 297000,"
85 " 'width_microns': 210000"
87 " 'custom_display_name': 'Letter',"
88 " 'height_microns': 279400,"
89 " 'is_default': true,"
90 " 'name': 'NA_LETTER',"
91 " 'width_microns': 215900"
94 " 'page_orientation': {"
96 " 'is_default': true,"
99 " 'type': 'LANDSCAPE'"
102 " 'reverse_order': {"
108 // Returns local IP address number of first interface found (except loopback).
109 // Return value is empty if no interface found. Possible interfaces names are
110 // "eth0", "wlan0" etc. If interface name is empty, function will return IP
111 // address of first interface found.
112 net::IPAddressNumber
GetLocalIp(const std::string
& interface_name
,
113 bool return_ipv6_number
) {
114 net::NetworkInterfaceList interfaces
;
115 bool success
= net::GetNetworkList(
116 &interfaces
, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES
);
119 size_t expected_address_size
= return_ipv6_number
? net::kIPv6AddressSize
120 : net::kIPv4AddressSize
;
122 for (net::NetworkInterfaceList::iterator iter
= interfaces
.begin();
123 iter
!= interfaces
.end(); ++iter
) {
124 if (iter
->address
.size() == expected_address_size
&&
125 (interface_name
.empty() || interface_name
== iter
->name
)) {
126 return iter
->address
;
130 return net::IPAddressNumber();
133 std::string
GetDescription() {
134 std::string result
= kPrinterDescription
;
135 net::IPAddressNumber ip
= GetLocalIp("", false);
137 result
+= " at " + net::IPAddressToString(ip
);
141 scoped_refptr
<base::SingleThreadTaskRunner
> GetTaskRunner() {
142 return base::MessageLoop::current()->message_loop_proxy();
147 using cloud_print_response_parser::Job
;
150 : connection_state_(OFFLINE
),
152 on_idle_posted_(false),
153 pending_local_settings_check_(false),
154 pending_print_jobs_check_(false),
155 pending_deletion_(false) {
158 Printer::~Printer() {
162 bool Printer::Start() {
168 if (state_
.local_settings
.local_discovery
&& !StartLocalDiscoveryServers())
171 print_job_handler_
.reset(new PrintJobHandler
);
172 xtoken_
= XPrivetToken();
173 starttime_
= base::Time::Now();
179 bool Printer::IsRunning() const {
180 return print_job_handler_
;
183 void Printer::Stop() {
186 dns_server_
.Shutdown();
187 http_server_
.Shutdown();
189 print_job_handler_
.reset();
190 xmpp_listener_
.reset();
193 std::string
Printer::GetRawCdd() {
194 std::string json_str
;
195 base::JSONWriter::WriteWithOptions(&GetCapabilities(),
196 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
201 void Printer::OnAuthError() {
202 LOG(ERROR
) << "Auth error occurred";
203 state_
.access_token_update
= base::Time();
207 std::string
Printer::GetAccessToken() {
208 return state_
.access_token
;
211 PrivetHttpServer::RegistrationErrorStatus
Printer::RegistrationStart(
212 const std::string
& user
) {
213 CheckRegistrationExpiration();
215 PrinterState::ConfirmationState conf_state
= state_
.confirmation_state
;
216 if (state_
.registration_state
== PrinterState::REGISTRATION_ERROR
||
217 conf_state
== PrinterState::CONFIRMATION_TIMEOUT
||
218 conf_state
== PrinterState::CONFIRMATION_DISCARDED
) {
219 state_
= PrinterState();
222 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
223 if (status
!= PrivetHttpServer::REG_ERROR_OK
)
226 if (state_
.registration_state
!= PrinterState::UNREGISTERED
)
227 return PrivetHttpServer::REG_ERROR_INVALID_ACTION
;
229 UpdateRegistrationExpiration();
231 state_
= PrinterState();
233 state_
.registration_state
= PrinterState::REGISTRATION_STARTED
;
235 if (CommandLine::ForCurrentProcess()->HasSwitch("disable-confirmation")) {
236 state_
.confirmation_state
= PrinterState::CONFIRMATION_CONFIRMED
;
237 VLOG(0) << "Registration confirmed by default.";
239 LOG(WARNING
) << kUserConfirmationTitle
;
240 base::Time valid_until
= base::Time::Now() +
241 base::TimeDelta::FromSeconds(kUserConfirmationTimeout
);
242 base::MessageLoop::current()->PostTask(
244 base::Bind(&Printer::WaitUserConfirmation
, AsWeakPtr(), valid_until
));
247 requester_
->StartRegistration(GenerateProxyId(), kPrinterName
, user
,
248 state_
.local_settings
, GetRawCdd());
250 return PrivetHttpServer::REG_ERROR_OK
;
253 PrivetHttpServer::RegistrationErrorStatus
Printer::RegistrationGetClaimToken(
254 const std::string
& user
,
256 std::string
* claim_url
) {
257 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
258 if (status
!= PrivetHttpServer::REG_ERROR_OK
)
261 // Check if |action=start| was called, but |action=complete| wasn't.
262 if (state_
.registration_state
!= PrinterState::REGISTRATION_STARTED
&&
263 state_
.registration_state
!=
264 PrinterState::REGISTRATION_CLAIM_TOKEN_READY
) {
265 return PrivetHttpServer::REG_ERROR_INVALID_ACTION
;
268 // If |action=getClaimToken| is valid in this state (was checked above) then
269 // check confirmation status.
270 if (state_
.confirmation_state
!= PrinterState::CONFIRMATION_CONFIRMED
)
271 return ConfirmationToRegistrationError(state_
.confirmation_state
);
273 UpdateRegistrationExpiration();
275 // If reply wasn't received yet, reply with |pending_user_action| error.
276 if (state_
.registration_state
== PrinterState::REGISTRATION_STARTED
)
277 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION
;
279 DCHECK_EQ(state_
.confirmation_state
, PrinterState::CONFIRMATION_CONFIRMED
);
280 DCHECK_EQ(state_
.registration_state
,
281 PrinterState::REGISTRATION_CLAIM_TOKEN_READY
);
283 *token
= state_
.registration_token
;
284 *claim_url
= state_
.complete_invite_url
;
285 return PrivetHttpServer::REG_ERROR_OK
;
288 PrivetHttpServer::RegistrationErrorStatus
Printer::RegistrationComplete(
289 const std::string
& user
,
290 std::string
* device_id
) {
291 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
292 if (status
!= PrivetHttpServer::REG_ERROR_OK
)
295 if (state_
.registration_state
!= PrinterState::REGISTRATION_CLAIM_TOKEN_READY
)
296 return PrivetHttpServer::REG_ERROR_INVALID_ACTION
;
298 UpdateRegistrationExpiration();
300 if (state_
.confirmation_state
!= PrinterState::CONFIRMATION_CONFIRMED
)
301 return ConfirmationToRegistrationError(state_
.confirmation_state
);
303 state_
.registration_state
= PrinterState::REGISTRATION_COMPLETING
;
304 requester_
->CompleteRegistration();
305 *device_id
= state_
.device_id
;
307 return PrivetHttpServer::REG_ERROR_OK
;
310 PrivetHttpServer::RegistrationErrorStatus
Printer::RegistrationCancel(
311 const std::string
& user
) {
312 PrivetHttpServer::RegistrationErrorStatus status
= CheckCommonRegErrors(user
);
313 if (status
!= PrivetHttpServer::REG_ERROR_OK
&&
314 status
!= PrivetHttpServer::REG_ERROR_SERVER_ERROR
) {
318 if (state_
.registration_state
== PrinterState::UNREGISTERED
)
319 return PrivetHttpServer::REG_ERROR_INVALID_ACTION
;
321 InvalidateRegistrationExpiration();
323 state_
= PrinterState();
325 requester_
.reset(new CloudPrintRequester(GetTaskRunner(), this));
327 return PrivetHttpServer::REG_ERROR_OK
;
330 void Printer::GetRegistrationServerError(std::string
* description
) {
331 DCHECK_EQ(state_
.registration_state
, PrinterState::REGISTRATION_ERROR
)
332 << "Method shouldn't be called when not needed.";
334 *description
= state_
.error_description
;
337 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo
* info
) {
338 CheckRegistrationExpiration();
340 // TODO(maksymb): Replace "text" with constants.
341 *info
= PrivetHttpServer::DeviceInfo();
342 info
->version
= "1.0";
343 info
->name
= kPrinterName
;
344 info
->description
= GetDescription();
345 info
->url
= kCloudPrintUrl
;
346 info
->id
= state_
.device_id
;
347 info
->device_state
= "idle";
348 info
->connection_state
= ConnectionStateToString(connection_state_
);
349 info
->manufacturer
= COMPANY_FULLNAME_STRING
;
350 info
->model
= "Prototype r" + std::string(LASTCHANGE_STRING
);
351 info
->serial_number
= "20CB5FF2-B28C-4EFA-8DCD-516CFF0455A2";
352 info
->firmware
= CHROME_VERSION_STRING
;
353 info
->uptime
= static_cast<int>((base::Time::Now() - starttime_
).InSeconds());
355 info
->x_privet_token
= xtoken_
.GenerateXToken();
357 // TODO(maksymb): Create enum for available APIs and replace
358 // this API text names with constants from enum. API text names should be only
359 // known in PrivetHttpServer.
360 if (!IsRegistered()) {
361 info
->api
.push_back("/privet/register");
363 info
->api
.push_back("/privet/capabilities");
364 if (IsLocalPrintingAllowed()) {
365 info
->api
.push_back("/privet/printer/createjob");
366 info
->api
.push_back("/privet/printer/submitdoc");
367 info
->api
.push_back("/privet/printer/jobstate");
371 info
->type
.push_back("printer");
374 bool Printer::IsRegistered() const {
375 return state_
.registration_state
== PrinterState::REGISTERED
;
378 bool Printer::IsLocalPrintingAllowed() const {
379 return state_
.local_settings
.local_printing_enabled
;
382 bool Printer::CheckXPrivetTokenHeader(const std::string
& token
) const {
383 return xtoken_
.CheckValidXToken(token
);
386 const base::DictionaryValue
& Printer::GetCapabilities() {
387 if (!state_
.cdd
.get()) {
388 std::string cdd_string
;
389 base::ReplaceChars(kCdd
, "'", "\"", &cdd_string
);
390 scoped_ptr
<base::Value
> json_val(base::JSONReader::Read(cdd_string
));
391 base::DictionaryValue
* json
= NULL
;
392 CHECK(json_val
->GetAsDictionary(&json
));
393 state_
.cdd
.reset(json
->DeepCopy());
398 LocalPrintJob::CreateResult
Printer::CreateJob(const std::string
& ticket
,
402 std::string
* error_description
) {
403 return print_job_handler_
->CreatePrintJob(ticket
, job_id
, expires_in
,
404 error_timeout
, error_description
);
407 LocalPrintJob::SaveResult
Printer::SubmitDoc(const LocalPrintJob
& job
,
410 std::string
* error_description
,
412 return print_job_handler_
->SaveLocalPrintJob(job
, job_id
, expires_in
,
413 error_description
, timeout
);
416 LocalPrintJob::SaveResult
Printer::SubmitDocWithId(
417 const LocalPrintJob
& job
,
418 const std::string
& job_id
,
420 std::string
* error_description
,
422 return print_job_handler_
->CompleteLocalPrintJob(job
, job_id
, expires_in
,
423 error_description
, timeout
);
426 bool Printer::GetJobState(const std::string
& id
, LocalPrintJob::Info
* info
) {
427 return print_job_handler_
->GetJobState(id
, info
);
430 void Printer::OnRegistrationStartResponseParsed(
431 const std::string
& registration_token
,
432 const std::string
& complete_invite_url
,
433 const std::string
& device_id
) {
434 state_
.registration_state
= PrinterState::REGISTRATION_CLAIM_TOKEN_READY
;
435 state_
.device_id
= device_id
;
436 state_
.registration_token
= registration_token
;
437 state_
.complete_invite_url
= complete_invite_url
;
440 void Printer::OnRegistrationFinished(const std::string
& refresh_token
,
441 const std::string
& access_token
,
442 int access_token_expires_in_seconds
) {
443 InvalidateRegistrationExpiration();
445 state_
.registration_state
= PrinterState::REGISTERED
;
446 state_
.refresh_token
= refresh_token
;
447 RememberAccessToken(access_token
, access_token_expires_in_seconds
);
451 void Printer::OnAccesstokenReceviced(const std::string
& access_token
,
452 int expires_in_seconds
) {
453 VLOG(3) << "Function: " << __FUNCTION__
;
454 RememberAccessToken(access_token
, expires_in_seconds
);
455 switch (connection_state_
) {
469 void Printer::OnXmppJidReceived(const std::string
& xmpp_jid
) {
470 state_
.xmpp_jid
= xmpp_jid
;
473 void Printer::OnRegistrationError(const std::string
& description
) {
474 LOG(ERROR
) << "server_error: " << description
;
476 SetRegistrationError(description
);
479 void Printer::OnNetworkError() {
480 VLOG(3) << "Function: " << __FUNCTION__
;
484 void Printer::OnServerError(const std::string
& description
) {
485 VLOG(3) << "Function: " << __FUNCTION__
;
486 LOG(ERROR
) << "Server error: " << description
;
490 void Printer::OnPrintJobsAvailable(const std::vector
<Job
>& jobs
) {
491 VLOG(3) << "Function: " << __FUNCTION__
;
493 VLOG(0) << "Available printjobs: " << jobs
.size();
495 pending_print_jobs_check_
= false;
500 VLOG(0) << "Downloading printjob.";
501 requester_
->RequestPrintJob(jobs
[0]);
505 void Printer::OnPrintJobDownloaded(const Job
& job
) {
506 VLOG(3) << "Function: " << __FUNCTION__
;
507 print_job_handler_
->SavePrintJob(job
.file
, job
.ticket
, job
.create_time
,
508 job
.job_id
, job
.title
, "pdf");
509 requester_
->SendPrintJobDone(job
.job_id
);
512 void Printer::OnPrintJobDone() {
513 VLOG(3) << "Function: " << __FUNCTION__
;
517 void Printer::OnLocalSettingsReceived(LocalSettings::State state
,
518 const LocalSettings
& settings
) {
519 pending_local_settings_check_
= false;
521 case LocalSettings::CURRENT
:
522 VLOG(0) << "No new local settings";
525 case LocalSettings::PENDING
:
526 VLOG(0) << "New local settings were received";
527 ApplyLocalSettings(settings
);
529 case LocalSettings::PRINTER_DELETED
:
530 LOG(WARNING
) << "Printer was deleted on server";
531 pending_deletion_
= true;
540 void Printer::OnLocalSettingsUpdated() {
544 void Printer::OnXmppConnected() {
545 pending_local_settings_check_
= true;
546 pending_print_jobs_check_
= true;
551 void Printer::OnXmppAuthError() {
555 void Printer::OnXmppNetworkError() {
559 void Printer::OnXmppNewPrintJob(const std::string
& device_id
) {
560 DCHECK_EQ(state_
.device_id
, device_id
) << "Data should contain printer_id";
561 pending_print_jobs_check_
= true;
564 void Printer::OnXmppNewLocalSettings(const std::string
& device_id
) {
565 DCHECK_EQ(state_
.device_id
, device_id
) << "Data should contain printer_id";
566 pending_local_settings_check_
= true;
569 void Printer::OnXmppDeleteNotification(const std::string
& device_id
) {
570 DCHECK_EQ(state_
.device_id
, device_id
) << "Data should contain printer_id";
571 pending_deletion_
= true;
574 void Printer::TryConnect() {
575 VLOG(3) << "Function: " << __FUNCTION__
;
577 ChangeState(CONNECTING
);
579 requester_
.reset(new CloudPrintRequester(GetTaskRunner(), this));
581 if (IsRegistered()) {
582 if (state_
.access_token_update
< base::Time::Now()) {
583 requester_
->UpdateAccesstoken(state_
.refresh_token
);
588 // TODO(maksymb): Ping google.com to check connection state.
593 void Printer::ConnectXmpp() {
594 xmpp_listener_
.reset(
595 new CloudPrintXmppListener(state_
.xmpp_jid
,
596 state_
.local_settings
.xmpp_timeout_value
,
597 GetTaskRunner(), this));
598 xmpp_listener_
->Connect(state_
.access_token
);
601 void Printer::OnIdle() {
602 DCHECK(IsRegistered());
603 DCHECK(on_idle_posted_
) << "Instant call is not allowed";
604 on_idle_posted_
= false;
606 if (connection_state_
!= ONLINE
)
609 if (pending_deletion_
) {
614 if (state_
.access_token_update
< base::Time::Now()) {
615 requester_
->UpdateAccesstoken(state_
.refresh_token
);
619 // TODO(maksymb): Check if privet-accesstoken was requested.
621 if (pending_local_settings_check_
) {
626 if (pending_print_jobs_check_
) {
631 base::MessageLoop::current()->PostDelayedTask(
633 base::Bind(&Printer::PostOnIdle
, AsWeakPtr()),
634 base::TimeDelta::FromMilliseconds(1000));
637 void Printer::FetchPrintJobs() {
638 VLOG(3) << "Function: " << __FUNCTION__
;
639 DCHECK(IsRegistered());
640 requester_
->FetchPrintJobs(state_
.device_id
);
643 void Printer::GetLocalSettings() {
644 VLOG(3) << "Function: " << __FUNCTION__
;
645 DCHECK(IsRegistered());
646 requester_
->RequestLocalSettings(state_
.device_id
);
649 void Printer::ApplyLocalSettings(const LocalSettings
& settings
) {
650 state_
.local_settings
= settings
;
653 if (state_
.local_settings
.local_discovery
) {
654 bool success
= StartLocalDiscoveryServers();
656 LOG(ERROR
) << "Local discovery servers cannot be started";
657 // TODO(maksymb): If start failed try to start them again after some timeout
659 dns_server_
.Shutdown();
660 http_server_
.Shutdown();
662 xmpp_listener_
->set_ping_interval(state_
.local_settings
.xmpp_timeout_value
);
664 requester_
->SendLocalSettings(state_
.device_id
, state_
.local_settings
);
667 void Printer::OnPrinterDeleted() {
668 pending_deletion_
= false;
670 state_
= PrinterState();
677 void Printer::RememberAccessToken(const std::string
& access_token
,
678 int expires_in_seconds
) {
680 using base::TimeDelta
;
681 state_
.access_token
= access_token
;
682 int64 time_to_update
= static_cast<int64
>(expires_in_seconds
*
683 kTimeToNextAccessTokenUpdate
);
684 state_
.access_token_update
=
685 Time::Now() + TimeDelta::FromSeconds(time_to_update
);
686 VLOG(0) << "Current access_token: " << access_token
;
690 void Printer::SetRegistrationError(const std::string
& description
) {
691 DCHECK(!IsRegistered());
692 state_
.registration_state
= PrinterState::REGISTRATION_ERROR
;
693 state_
.error_description
= description
;
696 PrivetHttpServer::RegistrationErrorStatus
Printer::CheckCommonRegErrors(
697 const std::string
& user
) {
698 CheckRegistrationExpiration();
699 DCHECK(!IsRegistered());
700 if (connection_state_
!= ONLINE
)
701 return PrivetHttpServer::REG_ERROR_OFFLINE
;
703 if (state_
.registration_state
!= PrinterState::UNREGISTERED
&&
704 user
!= state_
.user
) {
705 return PrivetHttpServer::REG_ERROR_DEVICE_BUSY
;
708 if (state_
.registration_state
== PrinterState::REGISTRATION_ERROR
)
709 return PrivetHttpServer::REG_ERROR_SERVER_ERROR
;
711 DCHECK_EQ(connection_state_
, ONLINE
);
713 return PrivetHttpServer::REG_ERROR_OK
;
716 void Printer::WaitUserConfirmation(base::Time valid_until
) {
717 // TODO(maksymb): Move to separate class.
719 if (base::Time::Now() > valid_until
) {
720 state_
.confirmation_state
= PrinterState::CONFIRMATION_TIMEOUT
;
721 VLOG(0) << "Confirmation timeout reached.";
727 if (c
== 'y' || c
== 'Y') {
728 state_
.confirmation_state
= PrinterState::CONFIRMATION_CONFIRMED
;
729 VLOG(0) << "Registration confirmed by user.";
731 state_
.confirmation_state
= PrinterState::CONFIRMATION_DISCARDED
;
732 VLOG(0) << "Registration discarded by user.";
737 base::MessageLoop::current()->PostDelayedTask(
739 base::Bind(&Printer::WaitUserConfirmation
, AsWeakPtr(), valid_until
),
740 base::TimeDelta::FromMilliseconds(100));
743 std::string
Printer::GenerateProxyId() const {
744 return "{" + base::GenerateGUID() +"}";
747 std::vector
<std::string
> Printer::CreateTxt() const {
748 std::vector
<std::string
> txt
;
749 txt
.push_back("txtvers=1");
750 txt
.push_back("ty=" + std::string(kPrinterName
));
751 txt
.push_back("note=" + std::string(GetDescription()));
752 txt
.push_back("url=" + std::string(kCloudPrintUrl
));
753 txt
.push_back("type=printer");
754 txt
.push_back("id=" + state_
.device_id
);
755 txt
.push_back("cs=" + ConnectionStateToString(connection_state_
));
760 void Printer::SaveToFile() {
761 GetCapabilities(); // Make sure capabilities created.
762 base::FilePath file_path
;
763 file_path
= file_path
.AppendASCII(
764 command_line_reader::ReadStatePath(kPrinterStatePathDefault
));
766 if (printer_state::SaveToFile(file_path
, state_
)) {
767 VLOG(0) << "Printer state written to file";
769 VLOG(0) << "Cannot write printer state to file";
773 bool Printer::LoadFromFile() {
774 state_
= PrinterState();
776 base::FilePath file_path
;
777 file_path
= file_path
.AppendASCII(
778 command_line_reader::ReadStatePath(kPrinterStatePathDefault
));
780 if (!base::PathExists(file_path
)) {
781 VLOG(0) << "Printer state file not found";
785 if (printer_state::LoadFromFile(file_path
, &state_
)) {
786 VLOG(0) << "Printer state loaded from file";
789 VLOG(0) << "Reading/parsing printer state from file failed";
795 void Printer::PostOnIdle() {
796 VLOG(3) << "Function: " << __FUNCTION__
;
797 DCHECK(!on_idle_posted_
) << "Only one instance can be posted.";
798 on_idle_posted_
= true;
800 base::MessageLoop::current()->PostTask(
802 base::Bind(&Printer::OnIdle
, AsWeakPtr()));
805 void Printer::CheckRegistrationExpiration() {
806 if (!registration_expiration_
.is_null() &&
807 registration_expiration_
< base::Time::Now()) {
808 state_
= PrinterState();
809 InvalidateRegistrationExpiration();
813 void Printer::UpdateRegistrationExpiration() {
814 registration_expiration_
=
815 base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout
);
818 void Printer::InvalidateRegistrationExpiration() {
819 registration_expiration_
= base::Time();
822 bool Printer::StartLocalDiscoveryServers() {
823 if (!StartHttpServer())
825 if (!StartDnsServer()) {
826 http_server_
.Shutdown();
832 bool Printer::StartDnsServer() {
833 DCHECK(state_
.local_settings
.local_discovery
);
835 // TODO(maksymb): Add switch for command line to control interface name.
836 net::IPAddressNumber ip
= GetLocalIp("", false);
838 LOG(ERROR
) << "No local IP found. Cannot start printer.";
841 VLOG(0) << "Local address: " << net::IPAddressToString(ip
);
843 uint16 port
= command_line_reader::ReadHttpPort(kHttpPortDefault
);
845 std::string service_name_prefix
=
846 command_line_reader::ReadServiceNamePrefix(kServiceNamePrefixDefault
+
847 net::IPAddressToString(ip
));
848 std::replace(service_name_prefix
.begin(), service_name_prefix
.end(),
851 std::string service_domain_name
=
852 command_line_reader::ReadDomainName(
853 base::StringPrintf(kServiceDomainNameFormatDefault
,
854 base::RandInt(0, INT_MAX
)));
856 ServiceParameters
params(kServiceType
, kSecondaryServiceType
,
857 service_name_prefix
, service_domain_name
,
858 ip
, GetLocalIp("", true), port
);
860 return dns_server_
.Start(params
,
861 command_line_reader::ReadTtl(kTtlDefault
),
865 bool Printer::StartHttpServer() {
866 DCHECK(state_
.local_settings
.local_discovery
);
867 using command_line_reader::ReadHttpPort
;
868 return http_server_
.Start(ReadHttpPort(kHttpPortDefault
));
871 PrivetHttpServer::RegistrationErrorStatus
872 Printer::ConfirmationToRegistrationError(
873 PrinterState::ConfirmationState state
) {
875 case PrinterState::CONFIRMATION_PENDING
:
876 return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION
;
877 case PrinterState::CONFIRMATION_DISCARDED
:
878 return PrivetHttpServer::REG_ERROR_USER_CANCEL
;
879 case PrinterState::CONFIRMATION_CONFIRMED
:
881 return PrivetHttpServer::REG_ERROR_OK
;
882 case PrinterState::CONFIRMATION_TIMEOUT
:
883 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT
;
886 return PrivetHttpServer::REG_ERROR_OK
;
890 std::string
Printer::ConnectionStateToString(ConnectionState state
) const {
899 return "not-configured";
907 void Printer::FallOffline(bool instant_reconnect
) {
908 bool changed
= ChangeState(OFFLINE
);
909 DCHECK(changed
) << "Falling offline from offline is now allowed";
912 SetRegistrationError("Cannot access server during registration process");
914 if (instant_reconnect
) {
917 base::MessageLoop::current()->PostDelayedTask(
919 base::Bind(&Printer::TryConnect
, AsWeakPtr()),
920 base::TimeDelta::FromSeconds(kReconnectTimeout
));
924 bool Printer::ChangeState(ConnectionState new_state
) {
925 if (connection_state_
== new_state
)
928 connection_state_
= new_state
;
929 VLOG(0) << base::StringPrintf(
930 "Printer is now %s (%s)",
931 ConnectionStateToString(connection_state_
).c_str(),
932 IsRegistered() ? "registered" : "unregistered");
934 dns_server_
.UpdateMetadata(CreateTxt());
936 if (connection_state_
== OFFLINE
) {
938 xmpp_listener_
.reset();