1/*
2 * libjingle
3 * Copyright 2011, 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/pubsubtasks.h"
29
30#include <string>
31#include <vector>
32
33#include "talk/xmpp/constants.h"
34#include "talk/xmpp/receivetask.h"
35
36// An implementation of the tasks for XEP-0060
37// (http://xmpp.org/extensions/xep-0060.html).
38
39namespace buzz {
40
41namespace {
42
43bool IsPubSubEventItemsElem(const XmlElement* stanza,
44                            const std::string& expected_node) {
45  if (stanza->Name() != QN_MESSAGE) {
46    return false;
47  }
48
49  const XmlElement* event_elem = stanza->FirstNamed(QN_PUBSUB_EVENT);
50  if (event_elem == NULL) {
51    return false;
52  }
53
54  const XmlElement* items_elem = event_elem->FirstNamed(QN_PUBSUB_EVENT_ITEMS);
55  if (items_elem == NULL) {
56    return false;
57  }
58
59  const std::string& actual_node = items_elem->Attr(QN_NODE);
60  return (actual_node == expected_node);
61}
62
63
64// Creates <pubsub node="node"><items></pubsub>
65XmlElement* CreatePubSubItemsElem(const std::string& node) {
66  XmlElement* items_elem = new XmlElement(QN_PUBSUB_ITEMS, false);
67  items_elem->AddAttr(QN_NODE, node);
68  XmlElement* pubsub_elem = new XmlElement(QN_PUBSUB, false);
69  pubsub_elem->AddElement(items_elem);
70  return pubsub_elem;
71}
72
73// Creates <pubsub node="node"><publish><item id="itemid">payload</item>...
74// Takes ownership of payload.
75XmlElement* CreatePubSubPublishItemElem(
76    const std::string& node,
77    const std::string& itemid,
78    const std::vector<XmlElement*>& children) {
79  XmlElement* pubsub_elem = new XmlElement(QN_PUBSUB, true);
80  XmlElement* publish_elem = new XmlElement(QN_PUBSUB_PUBLISH, false);
81  publish_elem->AddAttr(QN_NODE, node);
82  XmlElement* item_elem = new XmlElement(QN_PUBSUB_ITEM, false);
83  item_elem->AddAttr(QN_ID, itemid);
84  for (std::vector<XmlElement*>::const_iterator child = children.begin();
85       child != children.end(); ++child) {
86    item_elem->AddElement(*child);
87  }
88  publish_elem->AddElement(item_elem);
89  pubsub_elem->AddElement(publish_elem);
90  return pubsub_elem;
91}
92
93// Creates <pubsub node="node"><publish><item id="itemid">payload</item>...
94// Takes ownership of payload.
95XmlElement* CreatePubSubRetractItemElem(const std::string& node,
96                                        const std::string& itemid) {
97  XmlElement* pubsub_elem = new XmlElement(QN_PUBSUB, true);
98  XmlElement* retract_elem = new XmlElement(QN_PUBSUB_RETRACT, false);
99  retract_elem->AddAttr(QN_NODE, node);
100  retract_elem->AddAttr(QN_NOTIFY, "true");
101  XmlElement* item_elem = new XmlElement(QN_PUBSUB_ITEM, false);
102  item_elem->AddAttr(QN_ID, itemid);
103  retract_elem->AddElement(item_elem);
104  pubsub_elem->AddElement(retract_elem);
105  return pubsub_elem;
106}
107
108void ParseItem(const XmlElement* item_elem,
109               std::vector<PubSubItem>* items) {
110  PubSubItem item;
111  item.itemid = item_elem->Attr(QN_ID);
112  item.elem = item_elem;
113  items->push_back(item);
114}
115
116// Right now, <retract>s are treated the same as items with empty
117// payloads.  We may want to change it in the future, but right now
118// it's sufficient for our needs.
119void ParseRetract(const XmlElement* retract_elem,
120                  std::vector<PubSubItem>* items) {
121  ParseItem(retract_elem, items);
122}
123
124void ParseEventItemsElem(const XmlElement* stanza,
125                         std::vector<PubSubItem>* items) {
126  const XmlElement* event_elem = stanza->FirstNamed(QN_PUBSUB_EVENT);
127  if (event_elem != NULL) {
128    const XmlElement* items_elem =
129        event_elem->FirstNamed(QN_PUBSUB_EVENT_ITEMS);
130    if (items_elem != NULL) {
131      for (const XmlElement* item_elem =
132             items_elem->FirstNamed(QN_PUBSUB_EVENT_ITEM);
133           item_elem != NULL;
134           item_elem = item_elem->NextNamed(QN_PUBSUB_EVENT_ITEM)) {
135        ParseItem(item_elem, items);
136      }
137      for (const XmlElement* retract_elem =
138             items_elem->FirstNamed(QN_PUBSUB_EVENT_RETRACT);
139           retract_elem != NULL;
140           retract_elem = retract_elem->NextNamed(QN_PUBSUB_EVENT_RETRACT)) {
141        ParseRetract(retract_elem, items);
142      }
143    }
144  }
145}
146
147void ParsePubSubItemsElem(const XmlElement* stanza,
148                          std::vector<PubSubItem>* items) {
149  const XmlElement* pubsub_elem = stanza->FirstNamed(QN_PUBSUB);
150  if (pubsub_elem != NULL) {
151    const XmlElement* items_elem = pubsub_elem->FirstNamed(QN_PUBSUB_ITEMS);
152    if (items_elem != NULL) {
153      for (const XmlElement* item_elem = items_elem->FirstNamed(QN_PUBSUB_ITEM);
154           item_elem != NULL;
155           item_elem = item_elem->NextNamed(QN_PUBSUB_ITEM)) {
156        ParseItem(item_elem, items);
157      }
158    }
159  }
160}
161
162}  // namespace
163
164PubSubRequestTask::PubSubRequestTask(XmppTaskParentInterface* parent,
165                                     const Jid& pubsubjid,
166                                     const std::string& node)
167    : IqTask(parent, STR_GET, pubsubjid, CreatePubSubItemsElem(node)) {
168}
169
170void PubSubRequestTask::HandleResult(const XmlElement* stanza) {
171  std::vector<PubSubItem> items;
172  ParsePubSubItemsElem(stanza, &items);
173  SignalResult(this, items);
174}
175
176int PubSubReceiveTask::ProcessStart() {
177  if (SignalUpdate.is_empty()) {
178    return STATE_DONE;
179  }
180  return ReceiveTask::ProcessStart();
181}
182
183bool PubSubReceiveTask::WantsStanza(const XmlElement* stanza) {
184  return MatchStanzaFrom(stanza, pubsubjid_) &&
185      IsPubSubEventItemsElem(stanza, node_) && !SignalUpdate.is_empty();
186}
187
188void PubSubReceiveTask::ReceiveStanza(const XmlElement* stanza) {
189  std::vector<PubSubItem> items;
190  ParseEventItemsElem(stanza, &items);
191  SignalUpdate(this, items);
192}
193
194PubSubPublishTask::PubSubPublishTask(XmppTaskParentInterface* parent,
195                                     const Jid& pubsubjid,
196                                     const std::string& node,
197                                     const std::string& itemid,
198                                     const std::vector<XmlElement*>& children)
199    : IqTask(parent, STR_SET, pubsubjid,
200             CreatePubSubPublishItemElem(node, itemid, children)),
201      itemid_(itemid) {
202}
203
204void PubSubPublishTask::HandleResult(const XmlElement* stanza) {
205  SignalResult(this);
206}
207
208PubSubRetractTask::PubSubRetractTask(XmppTaskParentInterface* parent,
209                                     const Jid& pubsubjid,
210                                     const std::string& node,
211                                     const std::string& itemid)
212    : IqTask(parent, STR_SET, pubsubjid,
213             CreatePubSubRetractItemElem(node, itemid)),
214      itemid_(itemid) {
215}
216
217void PubSubRetractTask::HandleResult(const XmlElement* stanza) {
218  SignalResult(this);
219}
220
221}  // namespace buzz
222