15976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org/*
25976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * libjingle
35976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * Copyright 2011, Google Inc.
45976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
55976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * Redistribution and use in source and binary forms, with or without
65976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * modification, are permitted provided that the following conditions are met:
75976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
85976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  1. Redistributions of source code must retain the above copyright notice,
95976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     this list of conditions and the following disclaimer.
105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  2. Redistributions in binary form must reproduce the above copyright notice,
115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     this list of conditions and the following disclaimer in the documentation
125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     and/or other materials provided with the distribution.
135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  3. The name of the author may not be used to endorse or promote products
145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     derived from this software without specific prior written permission.
155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org */
275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/xmpp/hangoutpubsubclient.h"
295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/logging.h"
315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/xmpp/constants.h"
325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/xmpp/jid.h"
335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/xmllite/qname.h"
345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/xmllite/xmlelement.h"
355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Gives a high-level API for MUC call PubSub needs such as
385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// presenter state, recording state, mute state, and remote mute.
395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgnamespace buzz {
415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgnamespace {
435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst char kPresenting[] = "s";
445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst char kNotPresenting[] = "o";
455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst char kEmpty[] = "";
465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst std::string GetPublisherNickFromPubSubItem(const XmlElement* item_elem) {
485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (item_elem == NULL) {
495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return "";
505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return Jid(item_elem->Attr(QN_ATTR_PUBLISHER)).resource();
535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}  // namespace
565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Knows how to handle specific states and XML.
595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgtemplate <typename C>
605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PubSubStateSerializer {
615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual ~PubSubStateSerializer() {}
635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual XmlElement* Write(const QName& state_name, const C& state) = 0;
645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual C Parse(const XmlElement* state_elem) = 0;
655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Knows how to create "keys" for states, which determines their
685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// uniqueness.  Most states are per-nick, but block is
695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// per-blocker-and-blockee.  This is independent of itemid, especially
705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// in the case of presenter state.
715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PubSubStateKeySerializer {
725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual ~PubSubStateKeySerializer() {}
745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual std::string GetKey(const std::string& publisher_nick,
755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                             const std::string& published_nick) = 0;
765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PublishedNickKeySerializer : public PubSubStateKeySerializer {
795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual std::string GetKey(const std::string& publisher_nick,
815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                             const std::string& published_nick) {
825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return published_nick;
835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PublisherAndPublishedNicksKeySerializer
875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    : public PubSubStateKeySerializer {
885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual std::string GetKey(const std::string& publisher_nick,
905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                             const std::string& published_nick) {
915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return publisher_nick + ":" + published_nick;
925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// A simple serialiazer where presence of item => true, lack of item
965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// => false.
975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass BoolStateSerializer : public PubSubStateSerializer<bool> {
985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual XmlElement* Write(const QName& state_name, const bool& state) {
995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (!state) {
1005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return NULL;
1015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return new XmlElement(state_name, true);
1045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool Parse(const XmlElement* state_elem) {
1075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return state_elem != NULL;
1085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
1105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Adapts PubSubClient to be specifically suited for pub sub call
1125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// states.  Signals state changes and keeps track of keys, which are
1135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// normally nicks.
1145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// TODO: Expose this as a generally useful class, not just
1155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// private to hangouts.
1165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgtemplate <typename C>
1175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PubSubStateClient : public sigslot::has_slots<> {
1185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
1195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Gets ownership of the serializers, but not the client.
1205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PubSubStateClient(const std::string& publisher_nick,
1215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                    PubSubClient* client,
1225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                    const QName& state_name,
1235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                    C default_state,
1245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                    PubSubStateKeySerializer* key_serializer,
1255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                    PubSubStateSerializer<C>* state_serializer)
1265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      : publisher_nick_(publisher_nick),
1275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        client_(client),
1285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        state_name_(state_name),
1295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        default_state_(default_state) {
1305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    key_serializer_.reset(key_serializer);
1315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    state_serializer_.reset(state_serializer);
1325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client_->SignalItems.connect(
1335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        this, &PubSubStateClient<C>::OnItems);
1345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client_->SignalPublishResult.connect(
1355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        this, &PubSubStateClient<C>::OnPublishResult);
1365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client_->SignalPublishError.connect(
1375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        this, &PubSubStateClient<C>::OnPublishError);
1385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client_->SignalRetractResult.connect(
1395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        this, &PubSubStateClient<C>::OnRetractResult);
1405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client_->SignalRetractError.connect(
1415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        this, &PubSubStateClient<C>::OnRetractError);
1425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual ~PubSubStateClient() {}
1455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual void Publish(const std::string& published_nick,
1475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const C& state,
1485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       std::string* task_id_out) {
1495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    std::string key = key_serializer_->GetKey(publisher_nick_, published_nick);
1505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    std::string itemid = state_name_.LocalPart() + ":" + key;
1515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (StatesEqual(state, default_state_)) {
1525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      client_->RetractItem(itemid, task_id_out);
1535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    } else {
1545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      XmlElement* state_elem = state_serializer_->Write(state_name_, state);
1555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      state_elem->AddAttr(QN_NICK, published_nick);
1565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      client_->PublishItem(itemid, state_elem, task_id_out);
1575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  };
1595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange;
1615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Signal (task_id, item).  item is NULL for retract.
1625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  sigslot::signal2<const std::string&,
1635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                   const XmlElement*> SignalPublishResult;
1645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Signal (task_id, item, error stanza).  item is NULL for retract.
1655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  sigslot::signal3<const std::string&,
1665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                   const XmlElement*,
1675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                   const XmlElement*> SignalPublishError;
1685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org protected:
1705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // return false if retracted item (no info or state given)
1715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool ParseStateItem(const PubSubItem& item,
1725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                              StateItemInfo* info_out,
1735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                              bool* state_out) {
1745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
1755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (state_elem == NULL) {
1765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return false;
1775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem);
1805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    info_out->published_nick = state_elem->Attr(QN_NICK);
1815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    *state_out = state_serializer_->Parse(state_elem);
1825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return true;
1835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  };
1845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool StatesEqual(C state1, C state2) {
1865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return state1 == state2;
1875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PubSubClient* client() { return client_; }
1905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org private:
1925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnItems(PubSubClient* pub_sub_client,
1935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org               const std::vector<PubSubItem>& items) {
1945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    for (std::vector<PubSubItem>::const_iterator item = items.begin();
1955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org         item != items.end(); ++item) {
1965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      OnItem(*item);
1975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnItem(const PubSubItem& item) {
2015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& itemid = item.itemid;
2025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    StateItemInfo info;
2035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    C new_state;
2045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool retracted = !ParseStateItem(item, &info, &new_state);
2065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (retracted) {
2075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      bool known_itemid =
2085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          (info_by_itemid_.find(itemid) != info_by_itemid_.end());
2095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (!known_itemid) {
2105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        // Nothing to retract, and nothing to publish.
2115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        // Probably a different state type.
2125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        return;
2135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      } else {
2145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        info = info_by_itemid_[itemid];
2155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        info_by_itemid_.erase(itemid);
2165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        new_state = default_state_;
2175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
2185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    } else {
2195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // TODO: Assert new key matches the known key. It
2205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // shouldn't change!
2215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      info_by_itemid_[itemid] = info;
2225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    std::string key = key_serializer_->GetKey(
2255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        info.publisher_nick, info.published_nick);
2265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool has_old_state = (state_by_key_.find(key) != state_by_key_.end());
2275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    C old_state = has_old_state ? state_by_key_[key] : default_state_;
2285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) {
2295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // Nothing change, so don't bother signalling.
2305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return;
2315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (retracted || StatesEqual(new_state, default_state_)) {
2345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // We treat a default state similar to a retract.
2355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      state_by_key_.erase(key);
2365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    } else {
2375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      state_by_key_[key] = new_state;
2385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    PubSubStateChange<C> change;
2415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    change.publisher_nick = info.publisher_nick;
2425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    change.published_nick = info.published_nick;
2435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    change.old_state = old_state;
2445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    change.new_state = new_state;
2455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalStateChange(change);
2465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org }
2475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnPublishResult(PubSubClient* pub_sub_client,
2495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const std::string& task_id,
2505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const XmlElement* item) {
2515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishResult(task_id, item);
2525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnPublishError(PubSubClient* pub_sub_client,
2555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                      const std::string& task_id,
2565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                      const buzz::XmlElement* item,
2575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                      const buzz::XmlElement* stanza) {
2585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishError(task_id, item, stanza);
2595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnRetractResult(PubSubClient* pub_sub_client,
2625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const std::string& task_id) {
2635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // There's no point in differentiating between publish and retract
2645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // errors, so we simplify by making them both signal a publish
2655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // result.
2665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* item = NULL;
2675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishResult(task_id, item);
2685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void OnRetractError(PubSubClient* pub_sub_client,
2715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                      const std::string& task_id,
2725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                      const buzz::XmlElement* stanza) {
2735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // There's no point in differentiating between publish and retract
2745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // errors, so we simplify by making them both signal a publish
2755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // error.
2765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* item = NULL;
2775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishError(task_id, item, stanza);
2785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  std::string publisher_nick_;
2815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PubSubClient* client_;
2825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const QName state_name_;
2835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  C default_state_;
2845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  talk_base::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
2855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  talk_base::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
2865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // key => state
2875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  std::map<std::string, C> state_by_key_;
2885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // itemid => StateItemInfo
2895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  std::map<std::string, StateItemInfo> info_by_itemid_;
2905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
2915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass PresenterStateClient : public PubSubStateClient<bool> {
2935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
2945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PresenterStateClient(const std::string& publisher_nick,
2955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       PubSubClient* client,
2965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const QName& state_name,
2975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       bool default_state)
2985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      : PubSubStateClient<bool>(
2995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          publisher_nick, client, state_name, default_state,
3005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          new PublishedNickKeySerializer(), NULL) {
3015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual void Publish(const std::string& published_nick,
3045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       const bool& state,
3055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                       std::string* task_id_out) {
3065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    XmlElement* presenter_elem = new XmlElement(QN_PRESENTER_PRESENTER, true);
3075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    presenter_elem->AddAttr(QN_NICK, published_nick);
3085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    XmlElement* presentation_item_elem =
3105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        new XmlElement(QN_PRESENTER_PRESENTATION_ITEM, false);
3115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& presentation_type = state ? kPresenting : kNotPresenting;
3125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    presentation_item_elem->AddAttr(
3135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        QN_PRESENTER_PRESENTATION_TYPE, presentation_type);
3145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // The Presenter state is kind of dumb in that it doesn't always use
3165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    // retracts.  It relies on setting the "type" to a special value.
3175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    std::string itemid = published_nick;
3185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    std::vector<XmlElement*> children;
3195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    children.push_back(presenter_elem);
3205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    children.push_back(presentation_item_elem);
3215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    client()->PublishItem(itemid, children, task_id_out);
3225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org protected:
3255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool ParseStateItem(const PubSubItem& item,
3265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                              StateItemInfo* info_out,
3275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                              bool* state_out) {
3285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* presenter_elem =
3295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        item.elem->FirstNamed(QN_PRESENTER_PRESENTER);
3305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* presentation_item_elem =
3315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        item.elem->FirstNamed(QN_PRESENTER_PRESENTATION_ITEM);
3325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (presentation_item_elem == NULL || presenter_elem == NULL) {
3335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return false;
3345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
3355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem);
3375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    info_out->published_nick = presenter_elem->Attr(QN_NICK);
3385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    *state_out = (presentation_item_elem->Attr(
3395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        QN_PRESENTER_PRESENTATION_TYPE) != kNotPresenting);
3405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return true;
3415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool StatesEqual(bool state1, bool state2) {
3445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;  // Make every item trigger an event, even if state doesn't change.
3455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
3475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgHangoutPubSubClient::HangoutPubSubClient(XmppTaskParentInterface* parent,
3495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                                         const Jid& mucjid,
3505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                                         const std::string& nick)
3515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    : mucjid_(mucjid),
3525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_(nick) {
3535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_client_.reset(new PubSubClient(parent, mucjid, NS_PRESENTER));
3545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_client_->SignalRequestError.connect(
3555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnPresenterRequestError);
3565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_client_.reset(new PubSubClient(parent, mucjid, NS_GOOGLE_MUC_MEDIA));
3585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_client_->SignalRequestError.connect(
3595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnMediaRequestError);
3605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_state_client_.reset(new PresenterStateClient(
3625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, presenter_client_.get(), QN_PRESENTER_PRESENTER, false));
3635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_state_client_->SignalStateChange.connect(
3645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnPresenterStateChange);
3655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_state_client_->SignalPublishResult.connect(
3665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnPresenterPublishResult);
3675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_state_client_->SignalPublishError.connect(
3685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnPresenterPublishError);
3695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_.reset(new PubSubStateClient<bool>(
3715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, media_client_.get(), QN_GOOGLE_MUC_AUDIO_MUTE, false,
3725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new PublishedNickKeySerializer(), new BoolStateSerializer()));
3735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Can't just repeat because we need to watch for remote mutes.
3745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_->SignalStateChange.connect(
3755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnAudioMuteStateChange);
3765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_->SignalPublishResult.connect(
3775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnAudioMutePublishResult);
3785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_->SignalPublishError.connect(
3795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnAudioMutePublishError);
3805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_mute_state_client_.reset(new PubSubStateClient<bool>(
3825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, media_client_.get(), QN_GOOGLE_MUC_VIDEO_MUTE, false,
3835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new PublishedNickKeySerializer(), new BoolStateSerializer()));
3845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Can't just repeat because we need to watch for remote mutes.
3855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_mute_state_client_->SignalStateChange.connect(
3865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoMuteStateChange);
3875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_mute_state_client_->SignalPublishResult.connect(
3885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoMutePublishResult);
3895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_mute_state_client_->SignalPublishError.connect(
3905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoMutePublishError);
3915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_pause_state_client_.reset(new PubSubStateClient<bool>(
3935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, media_client_.get(), QN_GOOGLE_MUC_VIDEO_PAUSE, false,
3945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new PublishedNickKeySerializer(), new BoolStateSerializer()));
3955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_pause_state_client_->SignalStateChange.connect(
3965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoPauseStateChange);
3975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_pause_state_client_->SignalPublishResult.connect(
3985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoPausePublishResult);
3995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_pause_state_client_->SignalPublishError.connect(
4005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnVideoPausePublishError);
4015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  recording_state_client_.reset(new PubSubStateClient<bool>(
4035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, media_client_.get(), QN_GOOGLE_MUC_RECORDING, false,
4045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new PublishedNickKeySerializer(), new BoolStateSerializer()));
4055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  recording_state_client_->SignalStateChange.connect(
4065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnRecordingStateChange);
4075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  recording_state_client_->SignalPublishResult.connect(
4085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnRecordingPublishResult);
4095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  recording_state_client_->SignalPublishError.connect(
4105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnRecordingPublishError);
4115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_block_state_client_.reset(new PubSubStateClient<bool>(
4135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      nick_, media_client_.get(), QN_GOOGLE_MUC_MEDIA_BLOCK, false,
4145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new PublisherAndPublishedNicksKeySerializer(),
4155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      new BoolStateSerializer()));
4165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_block_state_client_->SignalStateChange.connect(
4175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnMediaBlockStateChange);
4185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_block_state_client_->SignalPublishResult.connect(
4195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnMediaBlockPublishResult);
4205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_block_state_client_->SignalPublishError.connect(
4215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      this, &HangoutPubSubClient::OnMediaBlockPublishError);
4225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgHangoutPubSubClient::~HangoutPubSubClient() {
4255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::RequestAll() {
4285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_client_->RequestItems();
4295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_client_->RequestItems();
4305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnPresenterRequestError(
4335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    PubSubClient* client, const XmlElement* stanza) {
4345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalRequestError(client->node(), stanza);
4355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnMediaRequestError(
4385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    PubSubClient* client, const XmlElement* stanza) {
4395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalRequestError(client->node(), stanza);
4405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::PublishPresenterState(
4435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool presenting, std::string* task_id_out) {
4445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  presenter_state_client_->Publish(nick_, presenting, task_id_out);
4455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::PublishAudioMuteState(
4485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool muted, std::string* task_id_out) {
4495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_->Publish(nick_, muted, task_id_out);
4505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::PublishVideoMuteState(
4535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool muted, std::string* task_id_out) {
4545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_mute_state_client_->Publish(nick_, muted, task_id_out);
4555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::PublishVideoPauseState(
4585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool paused, std::string* task_id_out) {
4595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_pause_state_client_->Publish(nick_, paused, task_id_out);
4605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::PublishRecordingState(
4635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool recording, std::string* task_id_out) {
4645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  recording_state_client_->Publish(nick_, recording, task_id_out);
4655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Remote mute is accomplished by setting another client's mute state.
4685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::RemoteMute(
4695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& mutee_nick, std::string* task_id_out) {
4705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_mute_state_client_->Publish(mutee_nick, true, task_id_out);
4715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Block media is accomplished by setting another client's block
4745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// state, kind of like remote mute.
4755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::BlockMedia(
4765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& blockee_nick, std::string* task_id_out) {
4775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  media_block_state_client_->Publish(blockee_nick, true, task_id_out);
4785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnPresenterStateChange(
4815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
4825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPresenterStateChange(
4835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      change.published_nick, change.old_state, change.new_state);
4845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnPresenterPublishResult(
4875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
4885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishPresenterResult(task_id);
4895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnPresenterPublishError(
4925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
4935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
4945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishPresenterError(task_id, stanza);
4955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Since a remote mute is accomplished by another client setting our
4985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// mute state, if our state changes to muted, we should mute ourselves.
4995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Note that remote un-muting is disallowed by the RoomServer.
5005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnAudioMuteStateChange(
5015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
5025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool was_muted = change.old_state;
5035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool is_muted = change.new_state;
5045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool remote_action = (!change.publisher_nick.empty() &&
5055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                        (change.publisher_nick != change.published_nick));
5065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (remote_action) {
5075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& mutee_nick = change.published_nick;
5085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& muter_nick = change.publisher_nick;
5095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (!is_muted) {
5105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // The server should prevent remote un-mute.
5115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      LOG(LS_WARNING) << muter_nick << " remote unmuted " << mutee_nick;
5125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return;
5135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
5145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool should_mute_locally = (mutee_nick == nick_);
5155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalRemoteMute(mutee_nick, muter_nick, should_mute_locally);
5165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  } else {
5175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalAudioMuteStateChange(change.published_nick, was_muted, is_muted);
5185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
5195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst std::string GetAudioMuteNickFromItem(const XmlElement* item) {
5225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (item != NULL) {
5235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* audio_mute_state =
5245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        item->FirstNamed(QN_GOOGLE_MUC_AUDIO_MUTE);
5255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (audio_mute_state != NULL) {
5265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return audio_mute_state->Attr(QN_NICK);
5275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
5285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
5295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return std::string();
5305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgconst std::string GetBlockeeNickFromItem(const XmlElement* item) {
5335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (item != NULL) {
5345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* media_block_state =
5355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        item->FirstNamed(QN_GOOGLE_MUC_MEDIA_BLOCK);
5365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (media_block_state != NULL) {
5375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return media_block_state->Attr(QN_NICK);
5385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
5395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
5405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return std::string();
5415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnAudioMutePublishResult(
5445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
5455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& mutee_nick = GetAudioMuteNickFromItem(item);
5465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (mutee_nick != nick_) {
5475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalRemoteMuteResult(task_id, mutee_nick);
5485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  } else {
5495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishAudioMuteResult(task_id);
5505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
5515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnAudioMutePublishError(
5545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
5555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
5565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& mutee_nick = GetAudioMuteNickFromItem(item);
5575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (mutee_nick != nick_) {
5585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalRemoteMuteError(task_id, mutee_nick, stanza);
5595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  } else {
5605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalPublishAudioMuteError(task_id, stanza);
5615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
5625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoMuteStateChange(
5655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
5665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalVideoMuteStateChange(
5675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      change.published_nick, change.old_state, change.new_state);
5685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoMutePublishResult(
5715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
5725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishVideoMuteResult(task_id);
5735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoMutePublishError(
5765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
5775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
5785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishVideoMuteError(task_id, stanza);
5795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoPauseStateChange(
5825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
5835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalVideoPauseStateChange(
5845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      change.published_nick, change.old_state, change.new_state);
5855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoPausePublishResult(
5885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
5895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishVideoPauseResult(task_id);
5905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnVideoPausePublishError(
5935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
5945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
5955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishVideoPauseError(task_id, stanza);
5965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
5975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
5985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnRecordingStateChange(
5995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
6005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalRecordingStateChange(
6015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      change.published_nick, change.old_state, change.new_state);
6025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnRecordingPublishResult(
6055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
6065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishRecordingResult(task_id);
6075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnRecordingPublishError(
6105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
6115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
6125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalPublishRecordingError(task_id, stanza);
6135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnMediaBlockStateChange(
6165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const PubSubStateChange<bool>& change) {
6175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& blockee_nick = change.published_nick;
6185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& blocker_nick = change.publisher_nick;
6195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool was_blockee = change.old_state;
6215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool is_blockee = change.new_state;
6225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!was_blockee && is_blockee) {
6235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    SignalMediaBlock(blockee_nick, blocker_nick);
6245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
6255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // TODO: Should we bother signaling unblock? Currently
6265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // it isn't allowed, but it might happen when a participant leaves
6275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // the room and the item is retracted.
6285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnMediaBlockPublishResult(
6315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item) {
6325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& blockee_nick = GetBlockeeNickFromItem(item);
6335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalMediaBlockResult(task_id, blockee_nick);
6345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid HangoutPubSubClient::OnMediaBlockPublishError(
6375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const std::string& task_id, const XmlElement* item,
6385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    const XmlElement* stanza) {
6395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  const std::string& blockee_nick = GetBlockeeNickFromItem(item);
6405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  SignalMediaBlockError(task_id, blockee_nick, stanza);
6415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
6425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
6435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}  // namespace buzz
644