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