blob: 9ac4223b05ad5dfca9ef801b0d8bdbf66f6f236b [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenkodea76b22015-06-01 13:18:06 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/notification/xmpp_iq_stanza_handler.h"
Alex Vakulenkodea76b22015-06-01 13:18:06 -07006
7#include <base/bind.h>
8#include <base/strings/string_number_conversions.h>
9#include <base/strings/stringprintf.h>
Vitaly Buka1e363672015-09-25 14:01:16 -070010#include <weave/provider/task_runner.h>
Alex Vakulenkodea76b22015-06-01 13:18:06 -070011
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020012#include "src/notification/xml_node.h"
13#include "src/notification/xmpp_channel.h"
Alex Vakulenkodea76b22015-06-01 13:18:06 -070014
Vitaly Bukab6f015a2015-07-09 14:59:23 -070015namespace weave {
Alex Vakulenkodea76b22015-06-01 13:18:06 -070016
17namespace {
18
19// Default timeout for <iq> requests to the server. If the response hasn't been
20// received within this time interval, the request is considered as failed.
21const int kTimeoutIntervalSeconds = 30;
22
23// Builds an XML stanza that looks like this:
24// <iq id='${id}' type='${type}' from='${from}' to='${to}'>$body</iq>
25// where 'to' and 'from' are optional attributes.
26std::string BuildIqStanza(const std::string& id,
27 const std::string& type,
28 const std::string& to,
29 const std::string& from,
30 const std::string& body) {
31 std::string to_attr;
32 if (!to.empty()) {
33 CHECK_EQ(std::string::npos, to.find_first_of("<'>"))
34 << "Destination address contains invalid XML characters";
35 base::StringAppendF(&to_attr, " to='%s'", to.c_str());
36 }
37 std::string from_attr;
38 if (!from.empty()) {
39 CHECK_EQ(std::string::npos, from.find_first_of("<'>"))
40 << "Source address contains invalid XML characters";
41 base::StringAppendF(&from_attr, " from='%s'", from.c_str());
42 }
Vitaly Bukaa647c852015-07-06 14:51:01 -070043 return base::StringPrintf("<iq id='%s' type='%s'%s%s>%s</iq>", id.c_str(),
44 type.c_str(), from_attr.c_str(), to_attr.c_str(),
45 body.c_str());
Alex Vakulenkodea76b22015-06-01 13:18:06 -070046}
47
48} // anonymous namespace
49
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070050IqStanzaHandler::IqStanzaHandler(XmppChannelInterface* xmpp_channel,
Vitaly Buka1e363672015-09-25 14:01:16 -070051 provider::TaskRunner* task_runner)
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070052 : xmpp_channel_{xmpp_channel}, task_runner_{task_runner} {}
Alex Vakulenkodea76b22015-06-01 13:18:06 -070053
Vitaly Bukaa647c852015-07-06 14:51:01 -070054void IqStanzaHandler::SendRequest(const std::string& type,
55 const std::string& from,
56 const std::string& to,
57 const std::string& body,
58 const ResponseCallback& response_callback,
59 const TimeoutCallback& timeout_callback) {
Vitaly Buka63cc3d22015-06-23 20:11:36 -070060 return SendRequestWithCustomTimeout(
61 type, from, to, body,
62 base::TimeDelta::FromSeconds(kTimeoutIntervalSeconds), response_callback,
63 timeout_callback);
64}
65
66void IqStanzaHandler::SendRequestWithCustomTimeout(
67 const std::string& type,
68 const std::string& from,
69 const std::string& to,
70 const std::string& body,
71 base::TimeDelta timeout,
72 const ResponseCallback& response_callback,
73 const TimeoutCallback& timeout_callback) {
Alex Vakulenkodea76b22015-06-01 13:18:06 -070074 // Remember the response callback to call later.
Vitaly Buka52d006a2015-11-21 17:14:51 -080075 requests_.insert(std::make_pair(++last_request_id_, response_callback));
Alex Vakulenkodea76b22015-06-01 13:18:06 -070076 // Schedule a time-out callback for this request.
Vitaly Buka63cc3d22015-06-23 20:11:36 -070077 if (timeout < base::TimeDelta::Max()) {
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070078 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -070079 FROM_HERE,
80 base::Bind(&IqStanzaHandler::OnTimeOut, weak_ptr_factory_.GetWeakPtr(),
81 last_request_id_, timeout_callback),
82 timeout);
83 }
Alex Vakulenkodea76b22015-06-01 13:18:06 -070084
Vitaly Bukaa647c852015-07-06 14:51:01 -070085 std::string message =
86 BuildIqStanza(std::to_string(last_request_id_), type, to, from, body);
Alex Vakulenkodea76b22015-06-01 13:18:06 -070087 xmpp_channel_->SendMessage(message);
88}
89
90bool IqStanzaHandler::HandleIqStanza(std::unique_ptr<XmlNode> stanza) {
91 std::string type;
92 if (!stanza->GetAttribute("type", &type)) {
93 LOG(ERROR) << "IQ stanza missing 'type' attribute";
94 return false;
95 }
96
97 std::string id_str;
98 if (!stanza->GetAttribute("id", &id_str)) {
99 LOG(ERROR) << "IQ stanza missing 'id' attribute";
100 return false;
101 }
102
103 if (type == "result" || type == "error") {
104 // These are response stanzas from the server.
105 // Find the corresponding request.
106 RequestId id;
107 if (!base::StringToInt(id_str, &id)) {
108 LOG(ERROR) << "IQ stanza's 'id' attribute is invalid";
109 return false;
110 }
111 auto p = requests_.find(id);
112 if (p != requests_.end()) {
Vitaly Buka823fdda2015-08-13 00:33:00 -0700113 task_runner_->PostDelayedTask(
114 FROM_HERE, base::Bind(p->second, base::Passed(std::move(stanza))),
115 {});
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700116 requests_.erase(p);
117 }
118 } else {
119 // We do not support server-initiated IQ requests ("set" / "get" / "query").
120 // So just reply with "not implemented" error (and swap "to"/"from" attrs).
121 std::string error_body =
122 "<error type='modify'>"
123 "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
124 "</error>";
Vitaly Bukaa647c852015-07-06 14:51:01 -0700125 std::string message =
126 BuildIqStanza(id_str, "error", stanza->GetAttributeOrEmpty("from"),
127 stanza->GetAttributeOrEmpty("to"), error_body);
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700128 xmpp_channel_->SendMessage(message);
129 }
130 return true;
131}
132
133void IqStanzaHandler::OnTimeOut(RequestId id,
134 const TimeoutCallback& timeout_callback) {
135 // Request has not been processed yes, so a real timeout occurred.
136 if (requests_.erase(id) > 0)
137 timeout_callback.Run();
138}
139
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700140} // namespace weave