Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / cloud_print / gcp20 / prototype / printer.cc
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.
4
5 #include "cloud_print/gcp20/prototype/printer.h"
6
7 #include <algorithm>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <string>
11 #include <vector>
12
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"
31
32 const char kPrinterStatePathDefault[] = "printer_state.json";
33
34 namespace {
35
36 const uint16 kHttpPortDefault = 10101;
37 const uint32 kTtlDefault = 60*60;  // in seconds
38
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";
43
44 const char kPrinterName[] = "Google GCP2.0 Prototype";
45 const char kPrinterDescription[] = "Printer emulator";
46
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
52
53 const double kTimeToNextAccessTokenUpdate = 0.8;  // relatively to living time.
54
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 "}";
107
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);
118
119   size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize
120                                                     : net::kIPv4AddressSize;
121
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;
127     }
128   }
129
130   return net::IPAddressNumber();
131 }
132
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;
139 }
140
141 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
142   return base::MessageLoop::current()->message_loop_proxy();
143 }
144
145 }  // namespace
146
147 using cloud_print_response_parser::Job;
148
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) {
156 }
157
158 Printer::~Printer() {
159   Stop();
160 }
161
162 bool Printer::Start() {
163   if (IsRunning())
164     return true;
165
166   LoadFromFile();
167
168   if (state_.local_settings.local_discovery && !StartLocalDiscoveryServers())
169     return false;
170
171   print_job_handler_.reset(new PrintJobHandler);
172   xtoken_ = XPrivetToken();
173   starttime_ = base::Time::Now();
174
175   TryConnect();
176   return true;
177 }
178
179 bool Printer::IsRunning() const {
180   return print_job_handler_;
181 }
182
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();
191 }
192
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;
199 }
200
201 void Printer::OnAuthError() {
202   LOG(ERROR) << "Auth error occurred";
203   state_.access_token_update = base::Time();
204   FallOffline(true);
205 }
206
207 std::string Printer::GetAccessToken() {
208   return state_.access_token;
209 }
210
211 PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
212     const std::string& user) {
213   CheckRegistrationExpiration();
214
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();
220   }
221
222   PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user);
223   if (status != PrivetHttpServer::REG_ERROR_OK)
224     return status;
225
226   if (state_.registration_state != PrinterState::UNREGISTERED)
227     return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
228
229   UpdateRegistrationExpiration();
230
231   state_ = PrinterState();
232   state_.user = user;
233   state_.registration_state = PrinterState::REGISTRATION_STARTED;
234
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));
245   }
246
247   requester_->StartRegistration(GenerateProxyId(), kPrinterName, user,
248                                 state_.local_settings, GetRawCdd());
249
250   return PrivetHttpServer::REG_ERROR_OK;
251 }
252
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;
260
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;
266   }
267
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);
272
273   UpdateRegistrationExpiration();
274
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;
278
279   DCHECK_EQ(state_.confirmation_state, PrinterState::CONFIRMATION_CONFIRMED);
280   DCHECK_EQ(state_.registration_state,
281             PrinterState::REGISTRATION_CLAIM_TOKEN_READY);
282
283   *token = state_.registration_token;
284   *claim_url = state_.complete_invite_url;
285   return PrivetHttpServer::REG_ERROR_OK;
286 }
287
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;
294
295   if (state_.registration_state != PrinterState::REGISTRATION_CLAIM_TOKEN_READY)
296     return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
297
298   UpdateRegistrationExpiration();
299
300   if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED)
301     return ConfirmationToRegistrationError(state_.confirmation_state);
302
303   state_.registration_state = PrinterState::REGISTRATION_COMPLETING;
304   requester_->CompleteRegistration();
305   *device_id = state_.device_id;
306
307   return PrivetHttpServer::REG_ERROR_OK;
308 }
309
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;
316   }
317
318   if (state_.registration_state == PrinterState::UNREGISTERED)
319     return PrivetHttpServer::REG_ERROR_INVALID_ACTION;
320
321   InvalidateRegistrationExpiration();
322
323   state_ = PrinterState();
324
325   requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
326
327   return PrivetHttpServer::REG_ERROR_OK;
328 }
329
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.";
333
334   *description = state_.error_description;
335 }
336
337 void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
338   CheckRegistrationExpiration();
339
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());
354
355   info->x_privet_token = xtoken_.GenerateXToken();
356
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");
368     }
369   }
370
371   info->type.push_back("printer");
372 }
373
374 bool Printer::IsRegistered() const {
375   return state_.registration_state == PrinterState::REGISTERED;
376 }
377
378 bool Printer::IsLocalPrintingAllowed() const {
379   return state_.local_settings.local_printing_enabled;
380 }
381
382 bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
383   return xtoken_.CheckValidXToken(token);
384 }
385
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());
394   }
395   return *state_.cdd;
396 }
397
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);
405 }
406
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);
414 }
415
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);
424 }
425
426 bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) {
427   return print_job_handler_->GetJobState(id, info);
428 }
429
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;
438 }
439
440 void Printer::OnRegistrationFinished(const std::string& refresh_token,
441                                      const std::string& access_token,
442                                      int access_token_expires_in_seconds) {
443   InvalidateRegistrationExpiration();
444
445   state_.registration_state = PrinterState::REGISTERED;
446   state_.refresh_token = refresh_token;
447   RememberAccessToken(access_token, access_token_expires_in_seconds);
448   TryConnect();
449 }
450
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;
459
460     case CONNECTING:
461       TryConnect();
462       break;
463
464     default:
465       NOTREACHED();
466   }
467 }
468
469 void Printer::OnXmppJidReceived(const std::string& xmpp_jid) {
470   state_.xmpp_jid = xmpp_jid;
471 }
472
473 void Printer::OnRegistrationError(const std::string& description) {
474   LOG(ERROR) << "server_error: " << description;
475
476   SetRegistrationError(description);
477 }
478
479 void Printer::OnNetworkError() {
480   VLOG(3) << "Function: " << __FUNCTION__;
481   FallOffline(false);
482 }
483
484 void Printer::OnServerError(const std::string& description) {
485   VLOG(3) << "Function: " << __FUNCTION__;
486   LOG(ERROR) << "Server error: " << description;
487   FallOffline(false);
488 }
489
490 void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) {
491   VLOG(3) << "Function: " << __FUNCTION__;
492
493   VLOG(0) << "Available printjobs: " << jobs.size();
494   if (jobs.empty()) {
495     pending_print_jobs_check_ = false;
496     PostOnIdle();
497     return;
498   }
499
500   VLOG(0) << "Downloading printjob.";
501   requester_->RequestPrintJob(jobs[0]);
502   return;
503 }
504
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);
510 }
511
512 void Printer::OnPrintJobDone() {
513   VLOG(3) << "Function: " << __FUNCTION__;
514   PostOnIdle();
515 }
516
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;
534
535     default:
536       NOTREACHED();
537   }
538 }
539
540 void Printer::OnLocalSettingsUpdated() {
541   PostOnIdle();
542 }
543
544 void Printer::OnXmppConnected() {
545   pending_local_settings_check_ = true;
546   pending_print_jobs_check_ = true;
547   ChangeState(ONLINE);
548   PostOnIdle();
549 }
550
551 void Printer::OnXmppAuthError() {
552   OnAuthError();
553 }
554
555 void Printer::OnXmppNetworkError() {
556   FallOffline(false);
557 }
558
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;
562 }
563
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;
567 }
568
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;
572 }
573
574 void Printer::TryConnect() {
575   VLOG(3) << "Function: " << __FUNCTION__;
576
577   ChangeState(CONNECTING);
578   if (!requester_)
579     requester_.reset(new CloudPrintRequester(GetTaskRunner(), this));
580
581   if (IsRegistered()) {
582     if (state_.access_token_update < base::Time::Now()) {
583       requester_->UpdateAccesstoken(state_.refresh_token);
584     } else {
585       ConnectXmpp();
586     }
587   } else {
588     // TODO(maksymb): Ping google.com to check connection state.
589     ChangeState(ONLINE);
590   }
591 }
592
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);
599 }
600
601 void Printer::OnIdle() {
602   DCHECK(IsRegistered());
603   DCHECK(on_idle_posted_) << "Instant call is not allowed";
604   on_idle_posted_ = false;
605
606   if (connection_state_ != ONLINE)
607     return;
608
609   if (pending_deletion_) {
610     OnPrinterDeleted();
611     return;
612   }
613
614   if (state_.access_token_update < base::Time::Now()) {
615     requester_->UpdateAccesstoken(state_.refresh_token);
616     return;
617   }
618
619   // TODO(maksymb): Check if privet-accesstoken was requested.
620
621   if (pending_local_settings_check_) {
622     GetLocalSettings();
623     return;
624   }
625
626   if (pending_print_jobs_check_) {
627     FetchPrintJobs();
628     return;
629   }
630
631   base::MessageLoop::current()->PostDelayedTask(
632         FROM_HERE,
633         base::Bind(&Printer::PostOnIdle, AsWeakPtr()),
634         base::TimeDelta::FromMilliseconds(1000));
635 }
636
637 void Printer::FetchPrintJobs() {
638   VLOG(3) << "Function: " << __FUNCTION__;
639   DCHECK(IsRegistered());
640   requester_->FetchPrintJobs(state_.device_id);
641 }
642
643 void Printer::GetLocalSettings() {
644   VLOG(3) << "Function: " << __FUNCTION__;
645   DCHECK(IsRegistered());
646   requester_->RequestLocalSettings(state_.device_id);
647 }
648
649 void Printer::ApplyLocalSettings(const LocalSettings& settings) {
650   state_.local_settings = settings;
651   SaveToFile();
652
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();
661   }
662   xmpp_listener_->set_ping_interval(state_.local_settings.xmpp_timeout_value);
663
664   requester_->SendLocalSettings(state_.device_id, state_.local_settings);
665 }
666
667 void Printer::OnPrinterDeleted() {
668   pending_deletion_ = false;
669
670   state_ = PrinterState();
671
672   SaveToFile();
673   Stop();
674   Start();
675 }
676
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();
688 }
689
690 void Printer::SetRegistrationError(const std::string& description) {
691   DCHECK(!IsRegistered());
692   state_.registration_state = PrinterState::REGISTRATION_ERROR;
693   state_.error_description = description;
694 }
695
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;
702
703   if (state_.registration_state != PrinterState::UNREGISTERED &&
704       user != state_.user) {
705     return PrivetHttpServer::REG_ERROR_DEVICE_BUSY;
706   }
707
708   if (state_.registration_state == PrinterState::REGISTRATION_ERROR)
709     return PrivetHttpServer::REG_ERROR_SERVER_ERROR;
710
711   DCHECK_EQ(connection_state_, ONLINE);
712
713   return PrivetHttpServer::REG_ERROR_OK;
714 }
715
716 void Printer::WaitUserConfirmation(base::Time valid_until) {
717   // TODO(maksymb): Move to separate class.
718
719   if (base::Time::Now() > valid_until) {
720     state_.confirmation_state = PrinterState::CONFIRMATION_TIMEOUT;
721     VLOG(0) << "Confirmation timeout reached.";
722     return;
723   }
724
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.";
733     }
734     return;
735   }
736
737   base::MessageLoop::current()->PostDelayedTask(
738       FROM_HERE,
739       base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until),
740       base::TimeDelta::FromMilliseconds(100));
741 }
742
743 std::string Printer::GenerateProxyId() const {
744   return "{" + base::GenerateGUID() +"}";
745 }
746
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_));
756
757   return txt;
758 }
759
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));
765
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";
770   }
771 }
772
773 bool Printer::LoadFromFile() {
774   state_ = PrinterState();
775
776   base::FilePath file_path;
777   file_path = file_path.AppendASCII(
778       command_line_reader::ReadStatePath(kPrinterStatePathDefault));
779
780   if (!base::PathExists(file_path)) {
781     VLOG(0) << "Printer state file not found";
782     return false;
783   }
784
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";
790   }
791
792   return true;
793 }
794
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;
799
800   base::MessageLoop::current()->PostTask(
801       FROM_HERE,
802       base::Bind(&Printer::OnIdle, AsWeakPtr()));
803 }
804
805 void Printer::CheckRegistrationExpiration() {
806   if (!registration_expiration_.is_null() &&
807       registration_expiration_ < base::Time::Now()) {
808     state_ = PrinterState();
809     InvalidateRegistrationExpiration();
810   }
811 }
812
813 void Printer::UpdateRegistrationExpiration() {
814   registration_expiration_ =
815       base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout);
816 }
817
818 void Printer::InvalidateRegistrationExpiration() {
819   registration_expiration_ = base::Time();
820 }
821
822 bool Printer::StartLocalDiscoveryServers() {
823   if (!StartHttpServer())
824     return false;
825   if (!StartDnsServer()) {
826     http_server_.Shutdown();
827     return false;
828   }
829   return true;
830 }
831
832 bool Printer::StartDnsServer() {
833   DCHECK(state_.local_settings.local_discovery);
834
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;
840   }
841   VLOG(0) << "Local address: " << net::IPAddressToString(ip);
842
843   uint16 port = command_line_reader::ReadHttpPort(kHttpPortDefault);
844
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                '.', '_');
850
851   std::string service_domain_name =
852       command_line_reader::ReadDomainName(
853           base::StringPrintf(kServiceDomainNameFormatDefault,
854                              base::RandInt(0, INT_MAX)));
855
856   ServiceParameters params(kServiceType, kSecondaryServiceType,
857                            service_name_prefix, service_domain_name,
858                            ip, GetLocalIp("", true), port);
859
860   return dns_server_.Start(params,
861                            command_line_reader::ReadTtl(kTtlDefault),
862                            CreateTxt());
863 }
864
865 bool Printer::StartHttpServer() {
866   DCHECK(state_.local_settings.local_discovery);
867   using command_line_reader::ReadHttpPort;
868   return http_server_.Start(ReadHttpPort(kHttpPortDefault));
869 }
870
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;
887   }
888 }
889
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";
900
901     default:
902       NOTREACHED();
903       return "";
904   }
905 }
906
907 void Printer::FallOffline(bool instant_reconnect) {
908   bool changed = ChangeState(OFFLINE);
909   DCHECK(changed) << "Falling offline from offline is now allowed";
910
911   if (!IsRegistered())
912     SetRegistrationError("Cannot access server during registration process");
913
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));
921   }
922 }
923
924 bool Printer::ChangeState(ConnectionState new_state) {
925   if (connection_state_ == new_state)
926     return false;
927
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");
933
934   dns_server_.UpdateMetadata(CreateTxt());
935
936   if (connection_state_ == OFFLINE) {
937     requester_.reset();
938     xmpp_listener_.reset();
939   }
940
941   return true;
942 }