Home | History | Annotate | Download | only in xmpp
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/libjingle/xmpp/pubsub_task.h"
     12 
     13 #include <map>
     14 #include <string>
     15 
     16 #include "webrtc/libjingle/xmpp/constants.h"
     17 #include "webrtc/libjingle/xmpp/xmppengine.h"
     18 #include "webrtc/base/common.h"
     19 
     20 namespace buzz {
     21 
     22 PubsubTask::PubsubTask(XmppTaskParentInterface* parent,
     23                        const buzz::Jid& pubsub_node_jid)
     24     : buzz::XmppTask(parent, buzz::XmppEngine::HL_SENDER),
     25       pubsub_node_jid_(pubsub_node_jid) {
     26 }
     27 
     28 PubsubTask::~PubsubTask() {
     29 }
     30 
     31 // Checks for pubsub publish events as well as responses to get IQs.
     32 bool PubsubTask::HandleStanza(const buzz::XmlElement* stanza) {
     33   const buzz::QName& stanza_name(stanza->Name());
     34   if (stanza_name == buzz::QN_MESSAGE) {
     35     if (MatchStanzaFrom(stanza, pubsub_node_jid_)) {
     36       const buzz::XmlElement* pubsub_event_item =
     37           stanza->FirstNamed(QN_PUBSUB_EVENT);
     38       if (pubsub_event_item != NULL) {
     39         QueueStanza(pubsub_event_item);
     40         return true;
     41       }
     42     }
     43   } else if (stanza_name == buzz::QN_IQ) {
     44     if (MatchResponseIq(stanza, pubsub_node_jid_, task_id())) {
     45       const buzz::XmlElement* pubsub_item = stanza->FirstNamed(QN_PUBSUB);
     46       if (pubsub_item != NULL) {
     47         QueueStanza(pubsub_item);
     48         return true;
     49       }
     50     }
     51   }
     52   return false;
     53 }
     54 
     55 int PubsubTask::ProcessResponse() {
     56   const buzz::XmlElement* stanza = NextStanza();
     57   if (stanza == NULL) {
     58     return STATE_BLOCKED;
     59   }
     60 
     61   if (stanza->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) {
     62     OnPubsubError(stanza->FirstNamed(buzz::QN_ERROR));
     63     return STATE_RESPONSE;
     64   }
     65 
     66   const buzz::QName& stanza_name(stanza->Name());
     67   if (stanza_name == QN_PUBSUB_EVENT) {
     68     HandlePubsubEventMessage(stanza);
     69   } else if (stanza_name == QN_PUBSUB) {
     70     HandlePubsubIqGetResponse(stanza);
     71   }
     72 
     73   return STATE_RESPONSE;
     74 }
     75 
     76 // Registers a function pointer to be called when the value of the pubsub
     77 // node changes.
     78 // Note that this does not actually change the XMPP pubsub
     79 // subscription. All publish events are always received by everyone in the
     80 // MUC. This function just controls whether the handle function will get
     81 // called when the event is received.
     82 bool PubsubTask::SubscribeToNode(const std::string& pubsub_node,
     83                                  NodeHandler handler) {
     84   subscribed_nodes_[pubsub_node] = handler;
     85   rtc::scoped_ptr<buzz::XmlElement> get_iq_request(
     86       MakeIq(buzz::STR_GET, pubsub_node_jid_, task_id()));
     87   if (!get_iq_request) {
     88     return false;
     89   }
     90   buzz::XmlElement* pubsub_element = new buzz::XmlElement(QN_PUBSUB, true);
     91   buzz::XmlElement* items_element = new buzz::XmlElement(QN_PUBSUB_ITEMS, true);
     92 
     93   items_element->AddAttr(buzz::QN_NODE, pubsub_node);
     94   pubsub_element->AddElement(items_element);
     95   get_iq_request->AddElement(pubsub_element);
     96 
     97   if (SendStanza(get_iq_request.get()) != buzz::XMPP_RETURN_OK) {
     98     return false;
     99   }
    100 
    101   return true;
    102 }
    103 
    104 void PubsubTask::UnsubscribeFromNode(const std::string& pubsub_node) {
    105   subscribed_nodes_.erase(pubsub_node);
    106 }
    107 
    108 void PubsubTask::OnPubsubError(const buzz::XmlElement* error_stanza) {
    109 }
    110 
    111 // Checks for a pubsub event message like the following:
    112 //
    113 //  <message from="muvc-private-chat-some-id (at) groupchat.google.com"
    114 //   to="john (at) site.com/gcomm582B14C9">
    115 //    <event xmlns:"http://jabber.org/protocol/pubsub#event">
    116 //      <items node="node-name">
    117 //        <item id="some-id">
    118 //          <payload/>
    119 //        </item>
    120 //      </items>
    121 //    </event>
    122 //  </message>
    123 //
    124 // It also checks for retraction event messages like the following:
    125 //
    126 //  <message from="muvc-private-chat-some-id (at) groupchat.google.com"
    127 //   to="john (at) site.com/gcomm582B14C9">
    128 //    <event xmlns:"http://jabber.org/protocol/pubsub#event">
    129 //      <items node="node-name">
    130 //        <retract id="some-id"/>
    131 //      </items>
    132 //    </event>
    133 //  </message>
    134 void PubsubTask::HandlePubsubEventMessage(
    135     const buzz::XmlElement* pubsub_event) {
    136   ASSERT(pubsub_event->Name() == QN_PUBSUB_EVENT);
    137   for (const buzz::XmlChild* child = pubsub_event->FirstChild();
    138        child != NULL;
    139        child = child->NextChild()) {
    140     const buzz::XmlElement* child_element = child->AsElement();
    141     const buzz::QName& child_name(child_element->Name());
    142     if (child_name == QN_PUBSUB_EVENT_ITEMS) {
    143       HandlePubsubItems(child_element);
    144     }
    145   }
    146 }
    147 
    148 // Checks for a response to an pubsub IQ get like the following:
    149 //
    150 //  <iq from="muvc-private-chat-some-id (at) groupchat.google.com"
    151 //   to="john (at) site.com/gcomm582B14C9"
    152 //   type="result">
    153 //    <pubsub xmlns:"http://jabber.org/protocol/pubsub">
    154 //      <items node="node-name">
    155 //        <item id="some-id">
    156 //          <payload/>
    157 //        </item>
    158 //      </items>
    159 //    </event>
    160 //  </message>
    161 void PubsubTask::HandlePubsubIqGetResponse(
    162     const buzz::XmlElement* pubsub_iq_response) {
    163   ASSERT(pubsub_iq_response->Name() == QN_PUBSUB);
    164   for (const buzz::XmlChild* child = pubsub_iq_response->FirstChild();
    165        child != NULL;
    166        child = child->NextChild()) {
    167     const buzz::XmlElement* child_element = child->AsElement();
    168     const buzz::QName& child_name(child_element->Name());
    169     if (child_name == QN_PUBSUB_ITEMS) {
    170       HandlePubsubItems(child_element);
    171     }
    172   }
    173 }
    174 
    175 // Calls registered handlers in response to pubsub event or response to
    176 // IQ pubsub get.
    177 // 'items' is the child of a pubsub#event:event node or pubsub:pubsub node.
    178 void PubsubTask::HandlePubsubItems(const buzz::XmlElement* items) {
    179   ASSERT(items->HasAttr(QN_NODE));
    180   const std::string& node_name(items->Attr(QN_NODE));
    181   NodeSubscriptions::iterator iter = subscribed_nodes_.find(node_name);
    182   if (iter != subscribed_nodes_.end()) {
    183     NodeHandler handler = iter->second;
    184     const buzz::XmlElement* item = items->FirstElement();
    185     while (item != NULL) {
    186       const buzz::QName& item_name(item->Name());
    187       if (item_name != QN_PUBSUB_EVENT_ITEM &&
    188           item_name != QN_PUBSUB_EVENT_RETRACT &&
    189           item_name != QN_PUBSUB_ITEM) {
    190         continue;
    191       }
    192 
    193       (this->*handler)(item);
    194       item = item->NextElement();
    195     }
    196     return;
    197   }
    198 }
    199 
    200 }
    201