Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / cloud_print / gcp20 / prototype / printer.cc
blobfdbdf8705e856a30eb1a663a20cd27902ee288ea
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 <algorithm>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <string>
11 #include <vector>
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";
34 namespace {
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.
55 const char kCdd[] =
56 "{"
57 " 'version': '1.0',"
58 " 'printer': {"
59 " 'supported_content_type': ["
60 " {"
61 " 'content_type': 'application/pdf'"
62 " },"
63 " {"
64 " 'content_type': 'image/pwg-raster'"
65 " },"
66 " {"
67 " 'content_type': 'image/jpeg'"
68 " }"
69 " ],"
70 " 'color': {"
71 " 'option': ["
72 " {"
73 " 'is_default': true,"
74 " 'type': 'STANDARD_COLOR'"
75 " },"
76 " {"
77 " 'type': 'STANDARD_MONOCHROME'"
78 " }"
79 " ]"
80 " },"
81 " 'media_size': {"
82 " 'option': [ {"
83 " 'height_microns': 297000,"
84 " 'name': 'ISO_A4',"
85 " 'width_microns': 210000"
86 " }, {"
87 " 'custom_display_name': 'Letter',"
88 " 'height_microns': 279400,"
89 " 'is_default': true,"
90 " 'name': 'NA_LETTER',"
91 " 'width_microns': 215900"
92 " } ]"
93 " },"
94 " 'page_orientation': {"
95 " 'option': [ {"
96 " 'is_default': true,"
97 " 'type': 'PORTRAIT'"
98 " }, {"
99 " 'type': 'LANDSCAPE'"
100 " } ]"
101 " },"
102 " 'reverse_order': {"
103 " 'default': false"
104 " }"
105 " }"
106 "}";
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);
117 DCHECK(success);
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);
136 if (!ip.empty())
137 result += " at " + net::IPAddressToString(ip);
138 return result;
141 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
142 return base::MessageLoop::current()->message_loop_proxy();
145 } // namespace
147 using cloud_print_response_parser::Job;
149 Printer::Printer()
150 : connection_state_(OFFLINE),
151 http_server_(this),
152 on_idle_posted_(false),
153 pending_local_settings_check_(false),
154 pending_print_jobs_check_(false),
155 pending_deletion_(false) {
158 Printer::~Printer() {
159 Stop();
162 bool Printer::Start() {
163 if (IsRunning())
164 return true;
166 LoadFromFile();
168 if (state_.local_settings.local_discovery && !StartLocalDiscoveryServers())
169 return false;
171 print_job_handler_.reset(new PrintJobHandler);
172 xtoken_ = XPrivetToken();
173 starttime_ = base::Time::Now();
175 TryConnect();
176 return true;
179 bool Printer::IsRunning() const {
180 return print_job_handler_;
183 void Printer::Stop() {
184 if (!IsRunning())
185 return;
186 dns_server_.Shutdown();
187 http_server_.Shutdown();
188 requester_.reset();
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,
197 &json_str);
198 return json_str;
201 void Printer::OnAuthError() {
202 LOG(ERROR) << "Auth error occurred";
203 state_.access_token_update = base::Time();
204 FallOffline(true);
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)
224 return status;
226 if (state_.registration_state != PrinterState::UNREGISTERED)
227 return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
229 UpdateRegistrationExpiration();
231 state_ = PrinterState();
232 state_.user = user;
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.";
238 } else {
239 LOG(WARNING) << kUserConfirmationTitle;
240 base::Time valid_until = base::Time::Now() +
241 base::TimeDelta::FromSeconds(kUserConfirmationTimeout);
242 base::MessageLoop::current()->PostTask(
243 FROM_HERE,
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,
255 std::string* token,
256 std::string* claim_url) {
257 PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
258 if (status != PrivetHttpServer::REG_ERROR_OK)
259 return status;
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)
293 return status;
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) {
315 return status;
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");
362 } else {
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());
395 return *state_.cdd;
398 LocalPrintJob::CreateResult Printer::CreateJob(const std::string& ticket,
399 std::string* job_id,
400 int* expires_in,
401 int* error_timeout,
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,
408 std::string* job_id,
409 int* expires_in,
410 std::string* error_description,
411 int* timeout) {
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,
419 int* expires_in,
420 std::string* error_description,
421 int* timeout) {
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);
448 TryConnect();
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_) {
456 case ONLINE:
457 PostOnIdle();
458 break;
460 case CONNECTING:
461 TryConnect();
462 break;
464 default:
465 NOTREACHED();
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__;
481 FallOffline(false);
484 void Printer::OnServerError(const std::string& description) {
485 VLOG(3) << "Function: " << __FUNCTION__;
486 LOG(ERROR) << "Server error: " << description;
487 FallOffline(false);
490 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) {
491 VLOG(3) << "Function: " << __FUNCTION__;
493 VLOG(0) << "Available printjobs: " << jobs.size();
494 if (jobs.empty()) {
495 pending_print_jobs_check_ = false;
496 PostOnIdle();
497 return;
500 VLOG(0) << "Downloading printjob.";
501 requester_->RequestPrintJob(jobs[0]);
502 return;
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__;
514 PostOnIdle();
517 void Printer::OnLocalSettingsReceived(LocalSettings::State state,
518 const LocalSettings& settings) {
519 pending_local_settings_check_ = false;
520 switch (state) {
521 case LocalSettings::CURRENT:
522 VLOG(0) << "No new local settings";
523 PostOnIdle();
524 break;
525 case LocalSettings::PENDING:
526 VLOG(0) << "New local settings were received";
527 ApplyLocalSettings(settings);
528 break;
529 case LocalSettings::PRINTER_DELETED:
530 LOG(WARNING) << "Printer was deleted on server";
531 pending_deletion_ = true;
532 PostOnIdle();
533 break;
535 default:
536 NOTREACHED();
540 void Printer::OnLocalSettingsUpdated() {
541 PostOnIdle();
544 void Printer::OnXmppConnected() {
545 pending_local_settings_check_ = true;
546 pending_print_jobs_check_ = true;
547 ChangeState(ONLINE);
548 PostOnIdle();
551 void Printer::OnXmppAuthError() {
552 OnAuthError();
555 void Printer::OnXmppNetworkError() {
556 FallOffline(false);
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);
578 if (!requester_)
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);
584 } else {
585 ConnectXmpp();
587 } else {
588 // TODO(maksymb): Ping google.com to check connection state.
589 ChangeState(ONLINE);
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)
607 return;
609 if (pending_deletion_) {
610 OnPrinterDeleted();
611 return;
614 if (state_.access_token_update < base::Time::Now()) {
615 requester_->UpdateAccesstoken(state_.refresh_token);
616 return;
619 // TODO(maksymb): Check if privet-accesstoken was requested.
621 if (pending_local_settings_check_) {
622 GetLocalSettings();
623 return;
626 if (pending_print_jobs_check_) {
627 FetchPrintJobs();
628 return;
631 base::MessageLoop::current()->PostDelayedTask(
632 FROM_HERE,
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;
651 SaveToFile();
653 if (state_.local_settings.local_discovery) {
654 bool success = StartLocalDiscoveryServers();
655 if (!success)
656 LOG(ERROR) << "Local discovery servers cannot be started";
657 // TODO(maksymb): If start failed try to start them again after some timeout
658 } else {
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();
672 SaveToFile();
673 Stop();
674 Start();
677 void Printer::RememberAccessToken(const std::string& access_token,
678 int expires_in_seconds) {
679 using base::Time;
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;
687 SaveToFile();
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.";
722 return;
725 if (_kbhit()) {
726 int c = _getche();
727 if (c == 'y' || c == 'Y') {
728 state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED;
729 VLOG(0) << "Registration confirmed by user.";
730 } else {
731 state_.confirmation_state = PrinterState::CONFIRMATION_DISCARDED;
732 VLOG(0) << "Registration discarded by user.";
734 return;
737 base::MessageLoop::current()->PostDelayedTask(
738 FROM_HERE,
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_));
757 return txt;
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";
768 } else {
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";
782 return false;
785 if (printer_state::LoadFromFile(file_path, &state_)) {
786 VLOG(0) << "Printer state loaded from file";
787 SaveToFile();
788 } else {
789 VLOG(0) << "Reading/parsing printer state from file failed";
792 return true;
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(
801 FROM_HERE,
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())
824 return false;
825 if (!StartDnsServer()) {
826 http_server_.Shutdown();
827 return false;
829 return true;
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);
837 if (ip.empty()) {
838 LOG(ERROR) << "No local IP found. Cannot start printer.";
839 return false;
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(),
849 '.', '_');
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),
862 CreateTxt());
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) {
874 switch (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:
880 NOTREACHED();
881 return PrivetHttpServer::REG_ERROR_OK;
882 case PrinterState::CONFIRMATION_TIMEOUT:
883 return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT;
884 default:
885 NOTREACHED();
886 return PrivetHttpServer::REG_ERROR_OK;
890 std::string Printer::ConnectionStateToString(ConnectionState state) const {
891 switch (state) {
892 case OFFLINE:
893 return "offline";
894 case ONLINE:
895 return "online";
896 case CONNECTING:
897 return "connecting";
898 case NOT_CONFIGURED:
899 return "not-configured";
901 default:
902 NOTREACHED();
903 return "";
907 void Printer::FallOffline(bool instant_reconnect) {
908 bool changed = ChangeState(OFFLINE);
909 DCHECK(changed) << "Falling offline from offline is now allowed";
911 if (!IsRegistered())
912 SetRegistrationError("Cannot access server during registration process");
914 if (instant_reconnect) {
915 TryConnect();
916 } else {
917 base::MessageLoop::current()->PostDelayedTask(
918 FROM_HERE,
919 base::Bind(&Printer::TryConnect, AsWeakPtr()),
920 base::TimeDelta::FromSeconds(kReconnectTimeout));
924 bool Printer::ChangeState(ConnectionState new_state) {
925 if (connection_state_ == new_state)
926 return false;
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) {
937 requester_.reset();
938 xmpp_listener_.reset();
941 return true;