|  | /* | 
|  | *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "webrtc/libjingle/xmpp/pubsub_task.h" | 
|  |  | 
|  | #include <map> | 
|  | #include <string> | 
|  |  | 
|  | #include "webrtc/libjingle/xmpp/constants.h" | 
|  | #include "webrtc/libjingle/xmpp/xmppengine.h" | 
|  | #include "webrtc/base/common.h" | 
|  |  | 
|  | namespace buzz { | 
|  |  | 
|  | PubsubTask::PubsubTask(XmppTaskParentInterface* parent, | 
|  | const buzz::Jid& pubsub_node_jid) | 
|  | : buzz::XmppTask(parent, buzz::XmppEngine::HL_SENDER), | 
|  | pubsub_node_jid_(pubsub_node_jid) { | 
|  | } | 
|  |  | 
|  | PubsubTask::~PubsubTask() { | 
|  | } | 
|  |  | 
|  | // Checks for pubsub publish events as well as responses to get IQs. | 
|  | bool PubsubTask::HandleStanza(const buzz::XmlElement* stanza) { | 
|  | const buzz::QName& stanza_name(stanza->Name()); | 
|  | if (stanza_name == buzz::QN_MESSAGE) { | 
|  | if (MatchStanzaFrom(stanza, pubsub_node_jid_)) { | 
|  | const buzz::XmlElement* pubsub_event_item = | 
|  | stanza->FirstNamed(QN_PUBSUB_EVENT); | 
|  | if (pubsub_event_item != NULL) { | 
|  | QueueStanza(pubsub_event_item); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } else if (stanza_name == buzz::QN_IQ) { | 
|  | if (MatchResponseIq(stanza, pubsub_node_jid_, task_id())) { | 
|  | const buzz::XmlElement* pubsub_item = stanza->FirstNamed(QN_PUBSUB); | 
|  | if (pubsub_item != NULL) { | 
|  | QueueStanza(pubsub_item); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int PubsubTask::ProcessResponse() { | 
|  | const buzz::XmlElement* stanza = NextStanza(); | 
|  | if (stanza == NULL) { | 
|  | return STATE_BLOCKED; | 
|  | } | 
|  |  | 
|  | if (stanza->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) { | 
|  | OnPubsubError(stanza->FirstNamed(buzz::QN_ERROR)); | 
|  | return STATE_RESPONSE; | 
|  | } | 
|  |  | 
|  | const buzz::QName& stanza_name(stanza->Name()); | 
|  | if (stanza_name == QN_PUBSUB_EVENT) { | 
|  | HandlePubsubEventMessage(stanza); | 
|  | } else if (stanza_name == QN_PUBSUB) { | 
|  | HandlePubsubIqGetResponse(stanza); | 
|  | } | 
|  |  | 
|  | return STATE_RESPONSE; | 
|  | } | 
|  |  | 
|  | // Registers a function pointer to be called when the value of the pubsub | 
|  | // node changes. | 
|  | // Note that this does not actually change the XMPP pubsub | 
|  | // subscription. All publish events are always received by everyone in the | 
|  | // MUC. This function just controls whether the handle function will get | 
|  | // called when the event is received. | 
|  | bool PubsubTask::SubscribeToNode(const std::string& pubsub_node, | 
|  | NodeHandler handler) { | 
|  | subscribed_nodes_[pubsub_node] = handler; | 
|  | rtc::scoped_ptr<buzz::XmlElement> get_iq_request( | 
|  | MakeIq(buzz::STR_GET, pubsub_node_jid_, task_id())); | 
|  | if (!get_iq_request) { | 
|  | return false; | 
|  | } | 
|  | buzz::XmlElement* pubsub_element = new buzz::XmlElement(QN_PUBSUB, true); | 
|  | buzz::XmlElement* items_element = new buzz::XmlElement(QN_PUBSUB_ITEMS, true); | 
|  |  | 
|  | items_element->AddAttr(buzz::QN_NODE, pubsub_node); | 
|  | pubsub_element->AddElement(items_element); | 
|  | get_iq_request->AddElement(pubsub_element); | 
|  |  | 
|  | if (SendStanza(get_iq_request.get()) != buzz::XMPP_RETURN_OK) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void PubsubTask::UnsubscribeFromNode(const std::string& pubsub_node) { | 
|  | subscribed_nodes_.erase(pubsub_node); | 
|  | } | 
|  |  | 
|  | void PubsubTask::OnPubsubError(const buzz::XmlElement* error_stanza) { | 
|  | } | 
|  |  | 
|  | // Checks for a pubsub event message like the following: | 
|  | // | 
|  | //  <message from="muvc-private-chat-some-id@groupchat.google.com" | 
|  | //   to="john@site.com/gcomm582B14C9"> | 
|  | //    <event xmlns:"http://jabber.org/protocol/pubsub#event"> | 
|  | //      <items node="node-name"> | 
|  | //        <item id="some-id"> | 
|  | //          <payload/> | 
|  | //        </item> | 
|  | //      </items> | 
|  | //    </event> | 
|  | //  </message> | 
|  | // | 
|  | // It also checks for retraction event messages like the following: | 
|  | // | 
|  | //  <message from="muvc-private-chat-some-id@groupchat.google.com" | 
|  | //   to="john@site.com/gcomm582B14C9"> | 
|  | //    <event xmlns:"http://jabber.org/protocol/pubsub#event"> | 
|  | //      <items node="node-name"> | 
|  | //        <retract id="some-id"/> | 
|  | //      </items> | 
|  | //    </event> | 
|  | //  </message> | 
|  | void PubsubTask::HandlePubsubEventMessage( | 
|  | const buzz::XmlElement* pubsub_event) { | 
|  | ASSERT(pubsub_event->Name() == QN_PUBSUB_EVENT); | 
|  | for (const buzz::XmlChild* child = pubsub_event->FirstChild(); | 
|  | child != NULL; | 
|  | child = child->NextChild()) { | 
|  | const buzz::XmlElement* child_element = child->AsElement(); | 
|  | const buzz::QName& child_name(child_element->Name()); | 
|  | if (child_name == QN_PUBSUB_EVENT_ITEMS) { | 
|  | HandlePubsubItems(child_element); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Checks for a response to an pubsub IQ get like the following: | 
|  | // | 
|  | //  <iq from="muvc-private-chat-some-id@groupchat.google.com" | 
|  | //   to="john@site.com/gcomm582B14C9" | 
|  | //   type="result"> | 
|  | //    <pubsub xmlns:"http://jabber.org/protocol/pubsub"> | 
|  | //      <items node="node-name"> | 
|  | //        <item id="some-id"> | 
|  | //          <payload/> | 
|  | //        </item> | 
|  | //      </items> | 
|  | //    </event> | 
|  | //  </message> | 
|  | void PubsubTask::HandlePubsubIqGetResponse( | 
|  | const buzz::XmlElement* pubsub_iq_response) { | 
|  | ASSERT(pubsub_iq_response->Name() == QN_PUBSUB); | 
|  | for (const buzz::XmlChild* child = pubsub_iq_response->FirstChild(); | 
|  | child != NULL; | 
|  | child = child->NextChild()) { | 
|  | const buzz::XmlElement* child_element = child->AsElement(); | 
|  | const buzz::QName& child_name(child_element->Name()); | 
|  | if (child_name == QN_PUBSUB_ITEMS) { | 
|  | HandlePubsubItems(child_element); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Calls registered handlers in response to pubsub event or response to | 
|  | // IQ pubsub get. | 
|  | // 'items' is the child of a pubsub#event:event node or pubsub:pubsub node. | 
|  | void PubsubTask::HandlePubsubItems(const buzz::XmlElement* items) { | 
|  | ASSERT(items->HasAttr(QN_NODE)); | 
|  | const std::string& node_name(items->Attr(QN_NODE)); | 
|  | NodeSubscriptions::iterator iter = subscribed_nodes_.find(node_name); | 
|  | if (iter != subscribed_nodes_.end()) { | 
|  | NodeHandler handler = iter->second; | 
|  | const buzz::XmlElement* item = items->FirstElement(); | 
|  | while (item != NULL) { | 
|  | const buzz::QName& item_name(item->Name()); | 
|  | if (item_name != QN_PUBSUB_EVENT_ITEM && | 
|  | item_name != QN_PUBSUB_EVENT_RETRACT && | 
|  | item_name != QN_PUBSUB_ITEM) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | (this->*handler)(item); | 
|  | item = item->NextElement(); | 
|  | } | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | } |