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 "remoting/host/host_status_sender.h"
6
7#include "base/strings/string_number_conversions.h"
8#include "base/strings/stringize_macros.h"
9#include "base/time/time.h"
10#include "remoting/base/constants.h"
11#include "remoting/base/logging.h"
12#include "remoting/host/server_log_entry_host.h"
13#include "remoting/signaling/iq_sender.h"
14#include "remoting/signaling/server_log_entry.h"
15#include "remoting/signaling/signal_strategy.h"
16#include "third_party/libjingle/source/talk/xmpp/constants.h"
17#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
18
19using buzz::QName;
20using buzz::XmlElement;
21
22namespace remoting {
23
24namespace {
25
26const char kHostStatusTag[] = "host-status";
27const char kHostIdAttr[] = "hostid";
28const char kExitCodeAttr[] = "exit-code";
29const char kHostVersionTag[] = "host-version";
30const char kSignatureTag[] = "signature";
31const char kStatusAttr[] = "status";
32const char kSignatureTimeAttr[] = "time";
33
34}  // namespace
35
36const char* const HostStatusSender::host_status_strings_[] =
37{"OFFLINE", "ONLINE"};
38
39HostStatusSender::HostStatusSender(
40    const std::string& host_id,
41    SignalStrategy* signal_strategy,
42    scoped_refptr<RsaKeyPair> key_pair,
43    const std::string& directory_bot_jid)
44    : host_id_(host_id),
45      signal_strategy_(signal_strategy),
46      key_pair_(key_pair),
47      directory_bot_jid_(directory_bot_jid) {
48  DCHECK(signal_strategy_);
49  DCHECK(key_pair_.get());
50
51  signal_strategy_->AddListener(this);
52}
53
54HostStatusSender::~HostStatusSender() {
55  signal_strategy_->RemoveListener(this);
56}
57
58void HostStatusSender::OnSignalStrategyStateChange(
59    SignalStrategy::State state) {
60  if (state == SignalStrategy::CONNECTED)
61    iq_sender_.reset(new IqSender(signal_strategy_));
62  else if (state == SignalStrategy::DISCONNECTED)
63    iq_sender_.reset();
64}
65
66bool HostStatusSender::OnSignalStrategyIncomingStanza(
67    const XmlElement* stanza) {
68  return false;
69}
70
71void HostStatusSender::SendOfflineStatus(HostExitCodes exit_code) {
72  SendHostStatus(OFFLINE, exit_code);
73}
74
75void HostStatusSender::SendOnlineStatus() {
76  SendHostStatus(ONLINE, kSuccessExitCode);
77}
78
79void HostStatusSender::SendHostStatus(HostStatus status,
80                                      HostExitCodes exit_code) {
81  SignalStrategy::State state = signal_strategy_->GetState();
82  if (state == SignalStrategy::CONNECTED) {
83    HOST_LOG << "Sending host status '"
84              << HostStatusToString(status)
85              << "' to "
86              << directory_bot_jid_;
87
88    iq_sender_->SendIq(buzz::STR_SET,
89                       directory_bot_jid_,
90                       CreateHostStatusMessage(status, exit_code),
91                       IqSender::ReplyCallback());
92  } else {
93    HOST_LOG << "Cannot send host status to '"
94              << directory_bot_jid_
95              << " ' because the state of the SignalStrategy is "
96              << state;
97  }
98}
99
100scoped_ptr<XmlElement> HostStatusSender::CreateHostStatusMessage(
101    HostStatus status, HostExitCodes exit_code) {
102  // Create host status stanza.
103  scoped_ptr<XmlElement> host_status(new XmlElement(
104      QName(kChromotingXmlNamespace, kHostStatusTag)));
105  host_status->AddAttr(
106      QName(kChromotingXmlNamespace, kHostIdAttr), host_id_);
107  host_status->AddAttr(
108      QName(kChromotingXmlNamespace, kStatusAttr), HostStatusToString(status));
109
110  if (status == OFFLINE) {
111    host_status->AddAttr(
112        QName(kChromotingXmlNamespace, kExitCodeAttr),
113        ExitCodeToString(exit_code));
114  }
115
116  host_status->AddElement(CreateSignature(status, exit_code).release());
117
118  // Append host version.
119  scoped_ptr<XmlElement> version_tag(new XmlElement(
120      QName(kChromotingXmlNamespace, kHostVersionTag)));
121  version_tag->AddText(STRINGIZE(VERSION));
122  host_status->AddElement(version_tag.release());
123
124  // Append log message (which isn't signed).
125  scoped_ptr<XmlElement> log(ServerLogEntry::MakeStanza());
126  scoped_ptr<ServerLogEntry> log_entry(
127      MakeLogEntryForHostStatus(status, exit_code));
128  AddHostFieldsToLogEntry(log_entry.get());
129  log->AddElement(log_entry->ToStanza().release());
130  host_status->AddElement(log.release());
131  return host_status.Pass();
132}
133
134scoped_ptr<XmlElement> HostStatusSender::CreateSignature(
135    HostStatus status, HostExitCodes exit_code) {
136  scoped_ptr<XmlElement> signature_tag(new XmlElement(
137      QName(kChromotingXmlNamespace, kSignatureTag)));
138
139  // Number of seconds since epoch (Jan 1, 1970).
140  int64 time = static_cast<int64>(base::Time::Now().ToDoubleT());
141  std::string time_str(base::Int64ToString(time));
142
143  signature_tag->AddAttr(
144      QName(kChromotingXmlNamespace, kSignatureTimeAttr), time_str);
145
146  // Add a time stamp to the signature to prevent replay attacks.
147  std::string message =
148      signal_strategy_->GetLocalJid() +
149      " " +
150      time_str +
151      " " +
152      HostStatusToString(status);
153
154  if (status == OFFLINE)
155    message += std::string(" ") + ExitCodeToString(exit_code);
156
157  std::string signature(key_pair_->SignMessage(message));
158  signature_tag->AddText(signature);
159
160  return signature_tag.Pass();
161}
162
163}  // namespace remoting
164