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