Home | History | Annotate | Download | only in xmpp
      1 /*
      2  * libjingle
      3  * Copyright 2004--2006, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/xmpp/pubsub_task.h"
     29 
     30 #include <map>
     31 #include <string>
     32 
     33 #include "talk/xmpp/constants.h"
     34 #include "talk/xmpp/xmppengine.h"
     35 #include "webrtc/base/common.h"
     36 
     37 namespace buzz {
     38 
     39 PubsubTask::PubsubTask(XmppTaskParentInterface* parent,
     40                        const buzz::Jid& pubsub_node_jid)
     41     : buzz::XmppTask(parent, buzz::XmppEngine::HL_SENDER),
     42       pubsub_node_jid_(pubsub_node_jid) {
     43 }
     44 
     45 PubsubTask::~PubsubTask() {
     46 }
     47 
     48 // Checks for pubsub publish events as well as responses to get IQs.
     49 bool PubsubTask::HandleStanza(const buzz::XmlElement* stanza) {
     50   const buzz::QName& stanza_name(stanza->Name());
     51   if (stanza_name == buzz::QN_MESSAGE) {
     52     if (MatchStanzaFrom(stanza, pubsub_node_jid_)) {
     53       const buzz::XmlElement* pubsub_event_item =
     54           stanza->FirstNamed(QN_PUBSUB_EVENT);
     55       if (pubsub_event_item != NULL) {
     56         QueueStanza(pubsub_event_item);
     57         return true;
     58       }
     59     }
     60   } else if (stanza_name == buzz::QN_IQ) {
     61     if (MatchResponseIq(stanza, pubsub_node_jid_, task_id())) {
     62       const buzz::XmlElement* pubsub_item = stanza->FirstNamed(QN_PUBSUB);
     63       if (pubsub_item != NULL) {
     64         QueueStanza(pubsub_item);
     65         return true;
     66       }
     67     }
     68   }
     69   return false;
     70 }
     71 
     72 int PubsubTask::ProcessResponse() {
     73   const buzz::XmlElement* stanza = NextStanza();
     74   if (stanza == NULL) {
     75     return STATE_BLOCKED;
     76   }
     77 
     78   if (stanza->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) {
     79     OnPubsubError(stanza->FirstNamed(buzz::QN_ERROR));
     80     return STATE_RESPONSE;
     81   }
     82 
     83   const buzz::QName& stanza_name(stanza->Name());
     84   if (stanza_name == QN_PUBSUB_EVENT) {
     85     HandlePubsubEventMessage(stanza);
     86   } else if (stanza_name == QN_PUBSUB) {
     87     HandlePubsubIqGetResponse(stanza);
     88   }
     89 
     90   return STATE_RESPONSE;
     91 }
     92 
     93 // Registers a function pointer to be called when the value of the pubsub
     94 // node changes.
     95 // Note that this does not actually change the XMPP pubsub
     96 // subscription. All publish events are always received by everyone in the
     97 // MUC. This function just controls whether the handle function will get
     98 // called when the event is received.
     99 bool PubsubTask::SubscribeToNode(const std::string& pubsub_node,
    100                                  NodeHandler handler) {
    101   subscribed_nodes_[pubsub_node] = handler;
    102   rtc::scoped_ptr<buzz::XmlElement> get_iq_request(
    103       MakeIq(buzz::STR_GET, pubsub_node_jid_, task_id()));
    104   if (!get_iq_request) {
    105     return false;
    106   }
    107   buzz::XmlElement* pubsub_element = new buzz::XmlElement(QN_PUBSUB, true);
    108   buzz::XmlElement* items_element = new buzz::XmlElement(QN_PUBSUB_ITEMS, true);
    109 
    110   items_element->AddAttr(buzz::QN_NODE, pubsub_node);
    111   pubsub_element->AddElement(items_element);
    112   get_iq_request->AddElement(pubsub_element);
    113 
    114   if (SendStanza(get_iq_request.get()) != buzz::XMPP_RETURN_OK) {
    115     return false;
    116   }
    117 
    118   return true;
    119 }
    120 
    121 void PubsubTask::UnsubscribeFromNode(const std::string& pubsub_node) {
    122   subscribed_nodes_.erase(pubsub_node);
    123 }
    124 
    125 void PubsubTask::OnPubsubError(const buzz::XmlElement* error_stanza) {
    126 }
    127 
    128 // Checks for a pubsub event message like the following:
    129 //
    130 //  <message from="muvc-private-chat-some-id (at) groupchat.google.com"
    131 //   to="john (at) site.com/gcomm582B14C9">
    132 //    <event xmlns:"http://jabber.org/protocol/pubsub#event">
    133 //      <items node="node-name">
    134 //        <item id="some-id">
    135 //          <payload/>
    136 //        </item>
    137 //      </items>
    138 //    </event>
    139 //  </message>
    140 //
    141 // It also checks for retraction event messages like the following:
    142 //
    143 //  <message from="muvc-private-chat-some-id (at) groupchat.google.com"
    144 //   to="john (at) site.com/gcomm582B14C9">
    145 //    <event xmlns:"http://jabber.org/protocol/pubsub#event">
    146 //      <items node="node-name">
    147 //        <retract id="some-id"/>
    148 //      </items>
    149 //    </event>
    150 //  </message>
    151 void PubsubTask::HandlePubsubEventMessage(
    152     const buzz::XmlElement* pubsub_event) {
    153   ASSERT(pubsub_event->Name() == QN_PUBSUB_EVENT);
    154   for (const buzz::XmlChild* child = pubsub_event->FirstChild();
    155        child != NULL;
    156        child = child->NextChild()) {
    157     const buzz::XmlElement* child_element = child->AsElement();
    158     const buzz::QName& child_name(child_element->Name());
    159     if (child_name == QN_PUBSUB_EVENT_ITEMS) {
    160       HandlePubsubItems(child_element);
    161     }
    162   }
    163 }
    164 
    165 // Checks for a response to an pubsub IQ get like the following:
    166 //
    167 //  <iq from="muvc-private-chat-some-id (at) groupchat.google.com"
    168 //   to="john (at) site.com/gcomm582B14C9"
    169 //   type="result">
    170 //    <pubsub xmlns:"http://jabber.org/protocol/pubsub">
    171 //      <items node="node-name">
    172 //        <item id="some-id">
    173 //          <payload/>
    174 //        </item>
    175 //      </items>
    176 //    </event>
    177 //  </message>
    178 void PubsubTask::HandlePubsubIqGetResponse(
    179     const buzz::XmlElement* pubsub_iq_response) {
    180   ASSERT(pubsub_iq_response->Name() == QN_PUBSUB);
    181   for (const buzz::XmlChild* child = pubsub_iq_response->FirstChild();
    182        child != NULL;
    183        child = child->NextChild()) {
    184     const buzz::XmlElement* child_element = child->AsElement();
    185     const buzz::QName& child_name(child_element->Name());
    186     if (child_name == QN_PUBSUB_ITEMS) {
    187       HandlePubsubItems(child_element);
    188     }
    189   }
    190 }
    191 
    192 // Calls registered handlers in response to pubsub event or response to
    193 // IQ pubsub get.
    194 // 'items' is the child of a pubsub#event:event node or pubsub:pubsub node.
    195 void PubsubTask::HandlePubsubItems(const buzz::XmlElement* items) {
    196   ASSERT(items->HasAttr(QN_NODE));
    197   const std::string& node_name(items->Attr(QN_NODE));
    198   NodeSubscriptions::iterator iter = subscribed_nodes_.find(node_name);
    199   if (iter != subscribed_nodes_.end()) {
    200     NodeHandler handler = iter->second;
    201     const buzz::XmlElement* item = items->FirstElement();
    202     while (item != NULL) {
    203       const buzz::QName& item_name(item->Name());
    204       if (item_name != QN_PUBSUB_EVENT_ITEM &&
    205           item_name != QN_PUBSUB_EVENT_RETRACT &&
    206           item_name != QN_PUBSUB_ITEM) {
    207         continue;
    208       }
    209 
    210       (this->*handler)(item);
    211       item = item->NextElement();
    212     }
    213     return;
    214   }
    215 }
    216 
    217 }
    218