• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #ifndef WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_
12 #define WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_
13 
14 #include <map>
15 #include <string>
16 #include <vector>
17 
18 #include "webrtc/libjingle/xmllite/qname.h"
19 #include "webrtc/libjingle/xmllite/xmlelement.h"
20 #include "webrtc/libjingle/xmpp/constants.h"
21 #include "webrtc/libjingle/xmpp/jid.h"
22 #include "webrtc/libjingle/xmpp/pubsubclient.h"
23 #include "webrtc/base/scoped_ptr.h"
24 #include "webrtc/base/sigslot.h"
25 #include "webrtc/base/sigslotrepeater.h"
26 
27 namespace buzz {
28 
29 // To handle retracts correctly, we need to remember certain details
30 // about an item.  We could just cache the entire XML element, but
31 // that would take more memory and require re-parsing.
32 struct StateItemInfo {
33   std::string published_nick;
34   std::string publisher_nick;
35 };
36 
37 // Represents a PubSub state change.  Usually, the key is the nick,
38 // but not always.  It's a per-state-type thing.  Look below on how keys are
39 // computed.
40 template <typename C>
41 struct PubSubStateChange {
42   // The nick of the user changing the state.
43   std::string publisher_nick;
44   // The nick of the user whose state is changing.
45   std::string published_nick;
46   C old_state;
47   C new_state;
48 };
49 
50 // Knows how to handle specific states and XML.
51 template <typename C>
52 class PubSubStateSerializer {
53  public:
~PubSubStateSerializer()54   virtual ~PubSubStateSerializer() {}
55   virtual XmlElement* Write(const QName& state_name, const C& state) = 0;
56   virtual void Parse(const XmlElement* state_elem, C* state_out) = 0;
57 };
58 
59 // Knows how to create "keys" for states, which determines their
60 // uniqueness.  Most states are per-nick, but block is
61 // per-blocker-and-blockee.  This is independent of itemid, especially
62 // in the case of presenter state.
63 class PubSubStateKeySerializer {
64  public:
~PubSubStateKeySerializer()65   virtual ~PubSubStateKeySerializer() {}
66   virtual std::string GetKey(const std::string& publisher_nick,
67                              const std::string& published_nick) = 0;
68 };
69 
70 class PublishedNickKeySerializer : public PubSubStateKeySerializer {
71  public:
72   virtual std::string GetKey(const std::string& publisher_nick,
73                              const std::string& published_nick);
74 };
75 
76 class PublisherAndPublishedNicksKeySerializer
77   : public PubSubStateKeySerializer {
78  public:
79   virtual std::string GetKey(const std::string& publisher_nick,
80                              const std::string& published_nick);
81 };
82 
83 // Adapts PubSubClient to be specifically suited for pub sub call
84 // states.  Signals state changes and keeps track of keys, which are
85 // normally nicks.
86 template <typename C>
87 class PubSubStateClient : public sigslot::has_slots<> {
88  public:
89   // Gets ownership of the serializers, but not the client.
PubSubStateClient(const std::string & publisher_nick,PubSubClient * client,const QName & state_name,C default_state,PubSubStateKeySerializer * key_serializer,PubSubStateSerializer<C> * state_serializer)90   PubSubStateClient(const std::string& publisher_nick,
91                     PubSubClient* client,
92                     const QName& state_name,
93                     C default_state,
94                     PubSubStateKeySerializer* key_serializer,
95                     PubSubStateSerializer<C>* state_serializer)
96       : publisher_nick_(publisher_nick),
97         client_(client),
98         state_name_(state_name),
99         default_state_(default_state) {
100     key_serializer_.reset(key_serializer);
101     state_serializer_.reset(state_serializer);
102     client_->SignalItems.connect(
103         this, &PubSubStateClient<C>::OnItems);
104     client_->SignalPublishResult.connect(
105         this, &PubSubStateClient<C>::OnPublishResult);
106     client_->SignalPublishError.connect(
107         this, &PubSubStateClient<C>::OnPublishError);
108     client_->SignalRetractResult.connect(
109         this, &PubSubStateClient<C>::OnRetractResult);
110     client_->SignalRetractError.connect(
111         this, &PubSubStateClient<C>::OnRetractError);
112   }
113 
~PubSubStateClient()114   virtual ~PubSubStateClient() {}
115 
Publish(const std::string & published_nick,const C & state,std::string * task_id_out)116   virtual void Publish(const std::string& published_nick,
117                        const C& state,
118                        std::string* task_id_out) {
119     std::string key = key_serializer_->GetKey(publisher_nick_, published_nick);
120     std::string itemid = state_name_.LocalPart() + ":" + key;
121     if (StatesEqual(state, default_state_)) {
122       client_->RetractItem(itemid, task_id_out);
123     } else {
124       XmlElement* state_elem = state_serializer_->Write(state_name_, state);
125       state_elem->AddAttr(QN_NICK, published_nick);
126       client_->PublishItem(itemid, state_elem, task_id_out);
127     }
128   }
129 
130   sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange;
131   // Signal (task_id, item).  item is NULL for retract.
132   sigslot::signal2<const std::string&,
133   const XmlElement*> SignalPublishResult;
134   // Signal (task_id, item, error stanza).  item is NULL for retract.
135   sigslot::signal3<const std::string&,
136   const XmlElement*,
137   const XmlElement*> SignalPublishError;
138 
139  protected:
140   // return false if retracted item (no info or state given)
ParseStateItem(const PubSubItem & item,StateItemInfo * info_out,C * state_out)141   virtual bool ParseStateItem(const PubSubItem& item,
142                               StateItemInfo* info_out,
143                               C* state_out) {
144     const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
145     if (state_elem == NULL) {
146       return false;
147     }
148 
149     info_out->publisher_nick =
150         client_->GetPublisherNickFromPubSubItem(item.elem);
151     info_out->published_nick = state_elem->Attr(QN_NICK);
152     state_serializer_->Parse(state_elem, state_out);
153     return true;
154   }
155 
StatesEqual(const C & state1,const C & state2)156   virtual bool StatesEqual(const C& state1, const C& state2) {
157     return state1 == state2;
158   }
159 
client()160   PubSubClient* client() { return client_; }
state_name()161   const QName& state_name() { return state_name_; }
162 
163  private:
OnItems(PubSubClient * pub_sub_client,const std::vector<PubSubItem> & items)164   void OnItems(PubSubClient* pub_sub_client,
165                const std::vector<PubSubItem>& items) {
166     for (std::vector<PubSubItem>::const_iterator item = items.begin();
167          item != items.end(); ++item) {
168       OnItem(*item);
169     }
170   }
171 
OnItem(const PubSubItem & item)172   void OnItem(const PubSubItem& item) {
173     const std::string& itemid = item.itemid;
174     StateItemInfo info;
175     C new_state;
176 
177     bool retracted = !ParseStateItem(item, &info, &new_state);
178     if (retracted) {
179       bool known_itemid =
180           (info_by_itemid_.find(itemid) != info_by_itemid_.end());
181       if (!known_itemid) {
182         // Nothing to retract, and nothing to publish.
183         // Probably a different state type.
184         return;
185       } else {
186         info = info_by_itemid_[itemid];
187         info_by_itemid_.erase(itemid);
188         new_state = default_state_;
189       }
190     } else {
191       // TODO: Assert new key matches the known key. It
192       // shouldn't change!
193       info_by_itemid_[itemid] = info;
194     }
195 
196     std::string key = key_serializer_->GetKey(
197         info.publisher_nick, info.published_nick);
198     bool has_old_state = (state_by_key_.find(key) != state_by_key_.end());
199     C old_state = has_old_state ? state_by_key_[key] : default_state_;
200     if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) {
201       // Nothing change, so don't bother signalling.
202       return;
203     }
204 
205     if (retracted || StatesEqual(new_state, default_state_)) {
206       // We treat a default state similar to a retract.
207       state_by_key_.erase(key);
208     } else {
209       state_by_key_[key] = new_state;
210     }
211 
212     PubSubStateChange<C> change;
213     if (!retracted) {
214       // Retracts do not have publisher information.
215       change.publisher_nick = info.publisher_nick;
216     }
217     change.published_nick = info.published_nick;
218     change.old_state = old_state;
219     change.new_state = new_state;
220     SignalStateChange(change);
221   }
222 
OnPublishResult(PubSubClient * pub_sub_client,const std::string & task_id,const XmlElement * item)223   void OnPublishResult(PubSubClient* pub_sub_client,
224                        const std::string& task_id,
225                        const XmlElement* item) {
226     SignalPublishResult(task_id, item);
227   }
228 
OnPublishError(PubSubClient * pub_sub_client,const std::string & task_id,const buzz::XmlElement * item,const buzz::XmlElement * stanza)229   void OnPublishError(PubSubClient* pub_sub_client,
230                       const std::string& task_id,
231                       const buzz::XmlElement* item,
232                       const buzz::XmlElement* stanza) {
233     SignalPublishError(task_id, item, stanza);
234   }
235 
OnRetractResult(PubSubClient * pub_sub_client,const std::string & task_id)236   void OnRetractResult(PubSubClient* pub_sub_client,
237                        const std::string& task_id) {
238     // There's no point in differentiating between publish and retract
239     // errors, so we simplify by making them both signal a publish
240     // result.
241     const XmlElement* item = NULL;
242     SignalPublishResult(task_id, item);
243   }
244 
OnRetractError(PubSubClient * pub_sub_client,const std::string & task_id,const buzz::XmlElement * stanza)245   void OnRetractError(PubSubClient* pub_sub_client,
246                       const std::string& task_id,
247                       const buzz::XmlElement* stanza) {
248     // There's no point in differentiating between publish and retract
249     // errors, so we simplify by making them both signal a publish
250     // error.
251     const XmlElement* item = NULL;
252     SignalPublishError(task_id, item, stanza);
253   }
254 
255   std::string publisher_nick_;
256   PubSubClient* client_;
257   const QName state_name_;
258   C default_state_;
259   rtc::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
260   rtc::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
261   // key => state
262   std::map<std::string, C> state_by_key_;
263   // itemid => StateItemInfo
264   std::map<std::string, StateItemInfo> info_by_itemid_;
265 
266   RTC_DISALLOW_COPY_AND_ASSIGN(PubSubStateClient);
267 };
268 }  // namespace buzz
269 
270 #endif  // WEBRTC_LIBJINGLE_XMPP_PUBSUBSTATECLIENT_H_
271