3 * Copyright 2011, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #ifndef TALK_XMPP_PUBSUBSTATECLIENT_H_
29 #define TALK_XMPP_PUBSUBSTATECLIENT_H_
35 #include "talk/xmllite/qname.h"
36 #include "talk/xmllite/xmlelement.h"
37 #include "talk/xmpp/constants.h"
38 #include "talk/xmpp/jid.h"
39 #include "talk/xmpp/pubsubclient.h"
40 #include "webrtc/base/scoped_ptr.h"
41 #include "webrtc/base/sigslot.h"
42 #include "webrtc/base/sigslotrepeater.h"
46 // To handle retracts correctly, we need to remember certain details
47 // about an item. We could just cache the entire XML element, but
48 // that would take more memory and require re-parsing.
49 struct StateItemInfo {
50 std::string published_nick;
51 std::string publisher_nick;
54 // Represents a PubSub state change. Usually, the key is the nick,
55 // but not always. It's a per-state-type thing. Look below on how keys are
58 struct PubSubStateChange {
59 // The nick of the user changing the state.
60 std::string publisher_nick;
61 // The nick of the user whose state is changing.
62 std::string published_nick;
67 // Knows how to handle specific states and XML.
69 class PubSubStateSerializer {
71 virtual ~PubSubStateSerializer() {}
72 virtual XmlElement* Write(const QName& state_name, const C& state) = 0;
73 virtual void Parse(const XmlElement* state_elem, C* state_out) = 0;
76 // Knows how to create "keys" for states, which determines their
77 // uniqueness. Most states are per-nick, but block is
78 // per-blocker-and-blockee. This is independent of itemid, especially
79 // in the case of presenter state.
80 class PubSubStateKeySerializer {
82 virtual ~PubSubStateKeySerializer() {}
83 virtual std::string GetKey(const std::string& publisher_nick,
84 const std::string& published_nick) = 0;
87 class PublishedNickKeySerializer : public PubSubStateKeySerializer {
89 virtual std::string GetKey(const std::string& publisher_nick,
90 const std::string& published_nick);
93 class PublisherAndPublishedNicksKeySerializer
94 : public PubSubStateKeySerializer {
96 virtual std::string GetKey(const std::string& publisher_nick,
97 const std::string& published_nick);
100 // Adapts PubSubClient to be specifically suited for pub sub call
101 // states. Signals state changes and keeps track of keys, which are
103 template <typename C>
104 class PubSubStateClient : public sigslot::has_slots<> {
106 // Gets ownership of the serializers, but not the client.
107 PubSubStateClient(const std::string& publisher_nick,
108 PubSubClient* client,
109 const QName& state_name,
111 PubSubStateKeySerializer* key_serializer,
112 PubSubStateSerializer<C>* state_serializer)
113 : publisher_nick_(publisher_nick),
115 state_name_(state_name),
116 default_state_(default_state) {
117 key_serializer_.reset(key_serializer);
118 state_serializer_.reset(state_serializer);
119 client_->SignalItems.connect(
120 this, &PubSubStateClient<C>::OnItems);
121 client_->SignalPublishResult.connect(
122 this, &PubSubStateClient<C>::OnPublishResult);
123 client_->SignalPublishError.connect(
124 this, &PubSubStateClient<C>::OnPublishError);
125 client_->SignalRetractResult.connect(
126 this, &PubSubStateClient<C>::OnRetractResult);
127 client_->SignalRetractError.connect(
128 this, &PubSubStateClient<C>::OnRetractError);
131 virtual ~PubSubStateClient() {}
133 virtual void Publish(const std::string& published_nick,
135 std::string* task_id_out) {
136 std::string key = key_serializer_->GetKey(publisher_nick_, published_nick);
137 std::string itemid = state_name_.LocalPart() + ":" + key;
138 if (StatesEqual(state, default_state_)) {
139 client_->RetractItem(itemid, task_id_out);
141 XmlElement* state_elem = state_serializer_->Write(state_name_, state);
142 state_elem->AddAttr(QN_NICK, published_nick);
143 client_->PublishItem(itemid, state_elem, task_id_out);
147 sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange;
148 // Signal (task_id, item). item is NULL for retract.
149 sigslot::signal2<const std::string&,
150 const XmlElement*> SignalPublishResult;
151 // Signal (task_id, item, error stanza). item is NULL for retract.
152 sigslot::signal3<const std::string&,
154 const XmlElement*> SignalPublishError;
157 // return false if retracted item (no info or state given)
158 virtual bool ParseStateItem(const PubSubItem& item,
159 StateItemInfo* info_out,
161 const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
162 if (state_elem == NULL) {
166 info_out->publisher_nick =
167 client_->GetPublisherNickFromPubSubItem(item.elem);
168 info_out->published_nick = state_elem->Attr(QN_NICK);
169 state_serializer_->Parse(state_elem, state_out);
173 virtual bool StatesEqual(const C& state1, const C& state2) {
174 return state1 == state2;
177 PubSubClient* client() { return client_; }
178 const QName& state_name() { return state_name_; }
181 void OnItems(PubSubClient* pub_sub_client,
182 const std::vector<PubSubItem>& items) {
183 for (std::vector<PubSubItem>::const_iterator item = items.begin();
184 item != items.end(); ++item) {
189 void OnItem(const PubSubItem& item) {
190 const std::string& itemid = item.itemid;
194 bool retracted = !ParseStateItem(item, &info, &new_state);
197 (info_by_itemid_.find(itemid) != info_by_itemid_.end());
199 // Nothing to retract, and nothing to publish.
200 // Probably a different state type.
203 info = info_by_itemid_[itemid];
204 info_by_itemid_.erase(itemid);
205 new_state = default_state_;
208 // TODO: Assert new key matches the known key. It
210 info_by_itemid_[itemid] = info;
213 std::string key = key_serializer_->GetKey(
214 info.publisher_nick, info.published_nick);
215 bool has_old_state = (state_by_key_.find(key) != state_by_key_.end());
216 C old_state = has_old_state ? state_by_key_[key] : default_state_;
217 if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) {
218 // Nothing change, so don't bother signalling.
222 if (retracted || StatesEqual(new_state, default_state_)) {
223 // We treat a default state similar to a retract.
224 state_by_key_.erase(key);
226 state_by_key_[key] = new_state;
229 PubSubStateChange<C> change;
231 // Retracts do not have publisher information.
232 change.publisher_nick = info.publisher_nick;
234 change.published_nick = info.published_nick;
235 change.old_state = old_state;
236 change.new_state = new_state;
237 SignalStateChange(change);
240 void OnPublishResult(PubSubClient* pub_sub_client,
241 const std::string& task_id,
242 const XmlElement* item) {
243 SignalPublishResult(task_id, item);
246 void OnPublishError(PubSubClient* pub_sub_client,
247 const std::string& task_id,
248 const buzz::XmlElement* item,
249 const buzz::XmlElement* stanza) {
250 SignalPublishError(task_id, item, stanza);
253 void OnRetractResult(PubSubClient* pub_sub_client,
254 const std::string& task_id) {
255 // There's no point in differentiating between publish and retract
256 // errors, so we simplify by making them both signal a publish
258 const XmlElement* item = NULL;
259 SignalPublishResult(task_id, item);
262 void OnRetractError(PubSubClient* pub_sub_client,
263 const std::string& task_id,
264 const buzz::XmlElement* stanza) {
265 // There's no point in differentiating between publish and retract
266 // errors, so we simplify by making them both signal a publish
268 const XmlElement* item = NULL;
269 SignalPublishError(task_id, item, stanza);
272 std::string publisher_nick_;
273 PubSubClient* client_;
274 const QName state_name_;
276 rtc::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
277 rtc::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
279 std::map<std::string, C> state_by_key_;
280 // itemid => StateItemInfo
281 std::map<std::string, StateItemInfo> info_by_itemid_;
283 DISALLOW_COPY_AND_ASSIGN(PubSubStateClient);
287 #endif // TALK_XMPP_PUBSUBSTATECLIENT_H_