/* * libjingle * Copyright 2004--2006, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "talk/xmpp/pubsub_task.h" #include #include #include "talk/base/common.h" #include "talk/xmpp/constants.h" #include "talk/xmpp/xmppengine.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; talk_base::scoped_ptr 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: // // // // // // // // // // // // It also checks for retraction event messages like the following: // // // // // // // // 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: // // // // // // // // // // 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; } } }