blob: 812cb95832cfef34d3116c2381511031e8532969 [file] [log] [blame]
/*
* 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 <memory>
#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;
std::unique_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;
}
}
}