• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2011, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
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.
15  *
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.
26  */
27 
28 #ifndef TALK_XMPP_PUBSUBSTATECLIENT_H_
29 #define TALK_XMPP_PUBSUBSTATECLIENT_H_
30 
31 #include <map>
32 #include <string>
33 #include <vector>
34 
35 #include "webrtc/libjingle/xmllite/qname.h"
36 #include "webrtc/libjingle/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"
43 
44 namespace buzz {
45 
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;
52 };
53 
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
56 // computed.
57 template <typename C>
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;
63   C old_state;
64   C new_state;
65 };
66 
67 // Knows how to handle specific states and XML.
68 template <typename C>
69 class PubSubStateSerializer {
70  public:
~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;
74 };
75 
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 {
81  public:
~PubSubStateKeySerializer()82   virtual ~PubSubStateKeySerializer() {}
83   virtual std::string GetKey(const std::string& publisher_nick,
84                              const std::string& published_nick) = 0;
85 };
86 
87 class PublishedNickKeySerializer : public PubSubStateKeySerializer {
88  public:
89   virtual std::string GetKey(const std::string& publisher_nick,
90                              const std::string& published_nick);
91 };
92 
93 class PublisherAndPublishedNicksKeySerializer
94   : public PubSubStateKeySerializer {
95  public:
96   virtual std::string GetKey(const std::string& publisher_nick,
97                              const std::string& published_nick);
98 };
99 
100 // Adapts PubSubClient to be specifically suited for pub sub call
101 // states.  Signals state changes and keeps track of keys, which are
102 // normally nicks.
103 template <typename C>
104 class PubSubStateClient : public sigslot::has_slots<> {
105  public:
106   // 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)107   PubSubStateClient(const std::string& publisher_nick,
108                     PubSubClient* client,
109                     const QName& state_name,
110                     C default_state,
111                     PubSubStateKeySerializer* key_serializer,
112                     PubSubStateSerializer<C>* state_serializer)
113       : publisher_nick_(publisher_nick),
114         client_(client),
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);
129   }
130 
~PubSubStateClient()131   virtual ~PubSubStateClient() {}
132 
Publish(const std::string & published_nick,const C & state,std::string * task_id_out)133   virtual void Publish(const std::string& published_nick,
134                        const C& state,
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);
140     } else {
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);
144     }
145   }
146 
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&,
153   const XmlElement*,
154   const XmlElement*> SignalPublishError;
155 
156  protected:
157   // return false if retracted item (no info or state given)
ParseStateItem(const PubSubItem & item,StateItemInfo * info_out,C * state_out)158   virtual bool ParseStateItem(const PubSubItem& item,
159                               StateItemInfo* info_out,
160                               C* state_out) {
161     const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
162     if (state_elem == NULL) {
163       return false;
164     }
165 
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);
170     return true;
171   }
172 
StatesEqual(const C & state1,const C & state2)173   virtual bool StatesEqual(const C& state1, const C& state2) {
174     return state1 == state2;
175   }
176 
client()177   PubSubClient* client() { return client_; }
state_name()178   const QName& state_name() { return state_name_; }
179 
180  private:
OnItems(PubSubClient * pub_sub_client,const std::vector<PubSubItem> & items)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) {
185       OnItem(*item);
186     }
187   }
188 
OnItem(const PubSubItem & item)189   void OnItem(const PubSubItem& item) {
190     const std::string& itemid = item.itemid;
191     StateItemInfo info;
192     C new_state;
193 
194     bool retracted = !ParseStateItem(item, &info, &new_state);
195     if (retracted) {
196       bool known_itemid =
197           (info_by_itemid_.find(itemid) != info_by_itemid_.end());
198       if (!known_itemid) {
199         // Nothing to retract, and nothing to publish.
200         // Probably a different state type.
201         return;
202       } else {
203         info = info_by_itemid_[itemid];
204         info_by_itemid_.erase(itemid);
205         new_state = default_state_;
206       }
207     } else {
208       // TODO: Assert new key matches the known key. It
209       // shouldn't change!
210       info_by_itemid_[itemid] = info;
211     }
212 
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.
219       return;
220     }
221 
222     if (retracted || StatesEqual(new_state, default_state_)) {
223       // We treat a default state similar to a retract.
224       state_by_key_.erase(key);
225     } else {
226       state_by_key_[key] = new_state;
227     }
228 
229     PubSubStateChange<C> change;
230     if (!retracted) {
231       // Retracts do not have publisher information.
232       change.publisher_nick = info.publisher_nick;
233     }
234     change.published_nick = info.published_nick;
235     change.old_state = old_state;
236     change.new_state = new_state;
237     SignalStateChange(change);
238   }
239 
OnPublishResult(PubSubClient * pub_sub_client,const std::string & task_id,const XmlElement * item)240   void OnPublishResult(PubSubClient* pub_sub_client,
241                        const std::string& task_id,
242                        const XmlElement* item) {
243     SignalPublishResult(task_id, item);
244   }
245 
OnPublishError(PubSubClient * pub_sub_client,const std::string & task_id,const buzz::XmlElement * item,const buzz::XmlElement * stanza)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);
251   }
252 
OnRetractResult(PubSubClient * pub_sub_client,const std::string & task_id)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
257     // result.
258     const XmlElement* item = NULL;
259     SignalPublishResult(task_id, item);
260   }
261 
OnRetractError(PubSubClient * pub_sub_client,const std::string & task_id,const buzz::XmlElement * stanza)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
267     // error.
268     const XmlElement* item = NULL;
269     SignalPublishError(task_id, item, stanza);
270   }
271 
272   std::string publisher_nick_;
273   PubSubClient* client_;
274   const QName state_name_;
275   C default_state_;
276   rtc::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
277   rtc::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
278   // key => state
279   std::map<std::string, C> state_by_key_;
280   // itemid => StateItemInfo
281   std::map<std::string, StateItemInfo> info_by_itemid_;
282 
283   DISALLOW_COPY_AND_ASSIGN(PubSubStateClient);
284 };
285 }  // namespace buzz
286 
287 #endif  // TALK_XMPP_PUBSUBSTATECLIENT_H_
288