1 /*
2 * pepmanager.cpp - Classes for PEP
3 * Copyright (C) 2006 Remko Troncon
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "pepmanager.h"
22
23 #include <QtDebug>
24 #include "xmpp_xmlcommon.h"
25 #include "xmpp_tasks.h"
26 #include "serverinfomanager.h"
27
28 using namespace XMPP;
29
30 // TODO: Get affiliations upon startup, and only create nodes based on that.
31 // (subscriptions is not accurate, since one doesn't subscribe to the
32 // avatar data node)
33
34 // -----------------------------------------------------------------------------
35
36 class PEPGetTask : public Task
37 {
38 public:
PEPGetTask(Task * parent,const QString & jid,const QString & node,const QString & itemID)39 PEPGetTask(Task* parent, const QString& jid, const QString& node, const QString& itemID) : Task(parent), jid_(jid), node_(node) {
40 iq_ = createIQ(doc(), "get", jid_, id());
41
42 QDomElement pubsub = doc()->createElement("pubsub");
43 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
44 iq_.appendChild(pubsub);
45
46 QDomElement items = doc()->createElement("items");
47 items.setAttribute("node", node);
48 pubsub.appendChild(items);
49
50 QDomElement item = doc()->createElement("item");
51 item.setAttribute("id", itemID);
52 items.appendChild(item);
53 }
54
onGo()55 void onGo() {
56 send(iq_);
57 }
58
take(const QDomElement & x)59 bool take(const QDomElement &x) {
60 if(!iqVerify(x, jid_, id()))
61 return false;
62
63 if(x.attribute("type") == "result") {
64 // FIXME Check namespace...
65 QDomElement e = x.firstChildElement("pubsub");
66 if (!e.isNull()) {
67 QDomElement i = e.firstChildElement("items");
68 if (!i.isNull()) {
69 QString iname = "item";
70 for(QDomElement e1 = i.firstChildElement(iname); !e1.isNull(); e1 = e1.nextSiblingElement(iname)) {
71 for(QDomElement e2 = e1.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
72 items_ += PubSubItem(e1.attribute("id"),e2);
73 }
74 }
75 }
76 }
77 setSuccess();
78 return true;
79 }
80 else {
81 setError(x);
82 return true;
83 }
84 }
85
items() const86 const QList<PubSubItem>& items() const {
87 return items_;
88 }
89
jid() const90 const QString& jid() const {
91 return jid_;
92 }
93
node() const94 const QString& node() const {
95 return node_;
96 }
97
98 private:
99 QDomElement iq_;
100 QString jid_;
101 QString node_;
102 QList<PubSubItem> items_;
103 };
104
105
106 // -----------------------------------------------------------------------------
107
108 /*
109 class PEPUnsubscribeTask : public Task
110 {
111 public:
112 PEPUnsubscribeTask(Task* parent, const QString& jid, const QString& node) : Task(parent), jid_(jid) {
113 iq_ = createIQ(doc(), "set", jid_, id());
114
115 QDomElement pubsub = doc()->createElement("pubsub");
116 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
117 iq_.appendChild(pubsub);
118
119 QDomElement unsubscribe = doc()->createElement("unsubscribe");
120 unsubscribe.setAttribute("node", node);
121 unsubscribe.setAttribute("jid", client()->jid().bare());
122 pubsub.appendChild(unsubscribe);
123 }
124
125 void onGo() {
126 send(iq_);
127 }
128
129 bool take(const QDomElement& x) {
130 if(!iqVerify(x, jid_, id()))
131 return false;
132
133 if(x.attribute("type") == "result") {
134 setSuccess();
135 }
136 else {
137 setError(x);
138 }
139 return true;
140 }
141
142 private:
143 QDomElement iq_;
144 QString jid_;
145 };
146
147 // -----------------------------------------------------------------------------
148
149 class PEPSubscribeTask : public Task
150 {
151 public:
152 PEPSubscribeTask(Task* parent, const QString& jid, const QString& node) : Task(parent), jid_(jid) {
153 iq_ = createIQ(doc(), "set", jid_, id());
154
155 QDomElement pubsub = doc()->createElement("pubsub");
156 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
157 iq_.appendChild(pubsub);
158
159 QDomElement subscribe = doc()->createElement("subscribe");
160 subscribe.setAttribute("node", node);
161 subscribe.setAttribute("jid", client()->jid().bare());
162 pubsub.appendChild(subscribe);
163 }
164
165 void onGo() {
166 send(iq_);
167 }
168
169 bool take(const QDomElement& x) {
170 if(!iqVerify(x, jid_, id()))
171 return false;
172
173 if(x.attribute("type") == "result") {
174 setSuccess();
175 }
176 else {
177 setError(x);
178 }
179 return true;
180 }
181
182 private:
183 QDomElement iq_;
184 QString jid_;
185 };*/
186
187 // -----------------------------------------------------------------------------
188
189 /*
190 class PEPCreateNodeTask : public Task
191 {
192 public:
193 PEPCreateNodeTask(Task* parent, const QString& node) : Task(parent) {
194 node_ = node;
195 iq_ = createIQ(doc(), "set", "", id());
196
197 QDomElement pubsub = doc()->createElement("pubsub");
198 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
199 iq_.appendChild(pubsub);
200
201 QDomElement subscribe = doc()->createElement("create");
202 subscribe.setAttribute("node", node);
203 pubsub.appendChild(subscribe);
204
205 QDomElement configure = doc()->createElement("configure");
206 pubsub.appendChild(configure);
207 }
208
209 const QString& node() const {
210 return node_;
211 }
212
213 void onGo() {
214 send(iq_);
215 }
216
217 bool take(const QDomElement& x) {
218 if(!iqVerify(x, "", id()))
219 return false;
220
221 if(x.attribute("type") == "result") {
222 setSuccess();
223 }
224 else {
225 setError(x);
226 }
227 return true;
228 }
229
230 private:
231 QDomElement iq_;
232 QString node_;
233 };*/
234
235 // -----------------------------------------------------------------------------
236
237 class PEPPublishTask : public Task
238 {
239 public:
PEPPublishTask(Task * parent,const QString & node,const PubSubItem & it,PEPManager::Access access)240 PEPPublishTask(Task* parent, const QString& node, const PubSubItem& it, PEPManager::Access access) : Task(parent), node_(node), item_(it) {
241 iq_ = createIQ(doc(), "set", "", id());
242
243 QDomElement pubsub = doc()->createElement("pubsub");
244 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
245 iq_.appendChild(pubsub);
246
247 QDomElement publish = doc()->createElement("publish");
248 publish.setAttribute("node", node);
249 pubsub.appendChild(publish);
250
251 QDomElement item = doc()->createElement("item");
252 item.setAttribute("id", it.id());
253 publish.appendChild(item);
254
255 if (access != PEPManager::DefaultAccess) {
256 QDomElement conf = doc()->createElement("configure");
257 QDomElement conf_x = doc()->createElementNS("jabber:x:data","x");
258
259 // Form type
260 QDomElement conf_x_field_type = doc()->createElement("field");
261 conf_x_field_type.setAttribute("var","FORM_TYPE");
262 conf_x_field_type.setAttribute("type","hidden");
263 QDomElement conf_x_field_type_value = doc()->createElement("value");
264 conf_x_field_type_value.appendChild(doc()->createTextNode("http://jabber.org/protocol/pubsub#node_config"));
265 conf_x_field_type.appendChild(conf_x_field_type_value);
266 conf_x.appendChild(conf_x_field_type);
267
268 // Access model
269 QDomElement access_model = doc()->createElement("field");
270 access_model.setAttribute("var","pubsub#access_model");
271 QDomElement access_model_value = doc()->createElement("value");
272 access_model.appendChild(access_model_value);
273 if (access == PEPManager::PublicAccess) {
274 access_model_value.appendChild(doc()->createTextNode("open"));
275 }
276 else if (access == PEPManager::PresenceAccess) {
277 access_model_value.appendChild(doc()->createTextNode("presence"));
278 }
279 conf_x.appendChild(access_model);
280
281
282 conf.appendChild(conf_x);
283 pubsub.appendChild(conf);
284 }
285
286 item.appendChild(it.payload());
287 }
288
take(const QDomElement & x)289 bool take(const QDomElement& x) {
290 if(!iqVerify(x, "", id()))
291 return false;
292
293 if(x.attribute("type") == "result") {
294 setSuccess();
295 }
296 else {
297 setError(x);
298 }
299 return true;
300 }
301
onGo()302 void onGo() {
303 send(iq_);
304 }
305
item() const306 const PubSubItem& item() const {
307 return item_;
308 }
309
node() const310 const QString& node() const {
311 return node_;
312 }
313
314 private:
315 QDomElement iq_;
316 QString node_;
317 PubSubItem item_;
318 };
319
320
321 // -----------------------------------------------------------------------------
322
323 class PEPRetractTask : public Task
324 {
325 public:
PEPRetractTask(Task * parent,const QString & node,const QString & itemId)326 PEPRetractTask(Task* parent, const QString& node, const QString& itemId) : Task(parent), node_(node), itemId_(itemId) {
327 iq_ = createIQ(doc(), "set", "", id());
328
329 QDomElement pubsub = doc()->createElement("pubsub");
330 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
331 iq_.appendChild(pubsub);
332
333 QDomElement retract = doc()->createElement("retract");
334 retract.setAttribute("node", node);
335 retract.setAttribute("notify", "1");
336 pubsub.appendChild(retract);
337
338 QDomElement item = doc()->createElement("item");
339 item.setAttribute("id", itemId);
340 retract.appendChild(item);
341 }
342
take(const QDomElement & x)343 bool take(const QDomElement& x) {
344 if(!iqVerify(x, "", id()))
345 return false;
346
347 if(x.attribute("type") == "result") {
348 setSuccess();
349 }
350 else {
351 setError(x);
352 }
353 return true;
354 }
355
onGo()356 void onGo() {
357 send(iq_);
358 }
359
node() const360 const QString& node() const {
361 return node_;
362 }
363
364 private:
365 QDomElement iq_;
366 QString node_;
367 QString itemId_;
368 };
369
370
371 // -----------------------------------------------------------------------------
372
373 /*
374 class GetPubSubSubscriptionsTask : public Task
375 {
376 public:
377 GetPubSubSubscriptionsTask(Task* parent, const Jid& jid) : Task(parent), jid_(jid) {
378 iq_ = createIQ(doc(), "get", jid_.bare(), id());
379
380 QDomElement pubsub = doc()->createElement("pubsub");
381 pubsub.setAttribute("xmlns", "http://jabber.org/protocol/pubsub");
382 iq_.appendChild(pubsub);
383
384 QDomElement subscriptions = doc()->createElement("subscriptions");
385 pubsub.appendChild(subscriptions);
386 }
387
388 bool take(const QDomElement &x) {
389 if(!iqVerify(x, jid_.bare(), id()))
390 return false;
391
392 if(x.attribute("type") == "result") {
393 subscriptions_.clear();
394 for(QDomNode m = x.firstChild(); !m.isNull(); m = m.nextSibling()) {
395 QDomElement me = m.toElement();
396 if (me.tagName() != "pubsub")
397 continue;
398
399 for(QDomNode n = me.firstChild(); !n.isNull(); n = n.nextSibling()) {
400 QDomElement s = n.toElement();
401 if (s.tagName() != "subscriptions")
402 continue;
403
404 for(QDomNode sub_node = s.firstChild(); !sub_node.isNull(); sub_node = sub_node.nextSibling()) {
405 // TODO: Check if the jid is the right one (ours) ?
406 PubSubSubscription sub(sub_node.toElement());
407 subscriptions_ += sub;
408 }
409 }
410 }
411
412 setSuccess();
413 return true;
414 }
415 else {
416 setError(x);
417 return true;
418 }
419 }
420
421 const Jid& jid() const {
422 return jid_;
423 }
424
425 const QList<PubSubSubscription> subscriptions() const {
426 return subscriptions_;
427 }
428
429 void onGo() {
430 send(iq_);
431 }
432
433 private:
434 QDomElement iq_;
435 Jid jid_;
436 QList<PubSubSubscription> subscriptions_;
437 };*/
438
439 // -----------------------------------------------------------------------------
440
PEPManager(Client * client,ServerInfoManager * serverInfo)441 PEPManager::PEPManager(Client* client, ServerInfoManager* serverInfo) : client_(client), serverInfo_(serverInfo)
442 {
443 connect(client_, SIGNAL(messageReceived(const Message &)), SLOT(messageReceived(const Message &)));
444 }
445
446 /*void PEPManager::setAvailable(bool a)
447 {
448 if (available_ != a) {
449 available_ = a;
450 emit available(available_);
451 }
452 }
453
454 void PEPManager::registerNode(const QString& node)
455 {
456 if (nodes_.contains(node))
457 return;
458
459 if (available_) {
460 createNode(node);
461 }
462
463 nodes_ += node;
464 }
465
466 void PEPManager::registerNodes(const QStringList& nodes)
467 {
468 foreach(QString node, nodes) {
469 registerNode(node);
470 }
471 }
472
473 bool PEPManager::canPublish(const QString& node) const
474 {
475 return ensured_nodes_.contains(node);
476 }
477
478 void PEPManager::saveSubscriptions()
479 {
480 }
481
482 void PEPManager::createNode(const QString& node)
483 {
484 PEPCreateNodeTask* t = new PEPCreateNodeTask(client_->rootTask(),node);
485 connect(t,SIGNAL(finished()),SLOT(createFinished()));
486 t->go(true);
487 }
488
489
490 void PEPManager::subscribe(const QString& jid, const QString& ns)
491 {
492 PEPSubscribeTask* t = new PEPSubscribeTask(client_->rootTask(),jid,ns);
493 connect(t,SIGNAL(finished()),SLOT(subscribeFinished()));
494 t->go(true);
495 }
496
497 void PEPManager::createFinished()
498 {
499 PEPCreateNodeTask* task = (PEPCreateNodeTask*) sender();
500 if (task->success() || task->statusCode() == 409) {
501 if (task->statusCode() == 409)
502 qWarning(QString("[%1] PEP Node already exists. Ignoring.").arg(client_->jid().full()));
503
504 // Subscribe to our own nodes
505 if (task->node() != "urn:xmpp:avatar:data")
506 subscribe(client_->jid().bare(),task->node());
507
508 // Notify
509 ensured_nodes_ += task->node();
510 emit ready(task->node());
511 }
512 else {
513 qWarning(QString("[%3] PEP Create failed: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full()));
514 }
515 }
516
517 void PEPManager::subscribeFinished()
518 {
519 PEPSubscribeTask* task = (PEPSubscribeTask*) sender();
520 if (task->success()) {
521 //subscriptions_ += task->subscription();
522 saveSubscriptions();
523 }
524 else {
525 qWarning(QString("[%3] PEP Subscribe failed: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full()));
526 }
527 }
528
529 void PEPManager::unsubscribe(const QString& jid, const QString& node)
530 {
531 PEPUnsubscribeTask* t = new PEPUnsubscribeTask(client_->rootTask(),jid,node);
532 connect(t,SIGNAL(finished()),SLOT(unsubscribeFinished()));
533 t->go(true);
534 }
535
536 void PEPManager::unsubscribeFinished()
537 {
538 PEPUnsubscribeTask* task = (PEPUnsubscribeTask*) sender();
539 if (!task->success()) {
540 qWarning(QString("[%3] PEP Unsubscribe failed: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full()));
541 }
542
543 // We're being conservative about unsubscribing. Remove subscription,
544 // even if there was an error.
545 //subscriptions_.remove(task->subscription());
546 saveSubscriptions();
547 }*/
548
publish(const QString & node,const PubSubItem & it,Access access)549 void PEPManager::publish(const QString& node, const PubSubItem& it, Access access)
550 {
551 //if (!canPublish(node))
552 // return;
553 if (!serverInfo_->hasPEP())
554 return;
555
556 PEPPublishTask* tp = new PEPPublishTask(client_->rootTask(),node,it,access);
557 connect(tp, SIGNAL(finished()), SLOT(publishFinished()));
558 tp->go(true);
559 }
560
561
retract(const QString & node,const QString & id)562 void PEPManager::retract(const QString& node, const QString& id)
563 {
564 if (!serverInfo_->hasPEP())
565 return;
566
567 PEPRetractTask* tp = new PEPRetractTask(client_->rootTask(),node,id);
568 // FIXME: add notification of success/failure
569 tp->go(true);
570 }
571
572
disable(const QString & tagName,const QString & node,const QString & id)573 void PEPManager::disable(const QString& tagName, const QString& node, const QString& id)
574 {
575 // disable by publishing an empty element
576 QDomElement element = client_->rootTask()->doc()->createElement(tagName);
577 element.setAttribute("xmlns", node);
578
579 publish(node, PubSubItem(id,element));
580 }
581
582
publishFinished()583 void PEPManager::publishFinished()
584 {
585 PEPPublishTask* task = (PEPPublishTask*) sender();
586 if (task->success()) {
587 emit publish_success(task->node(),task->item());
588 }
589 else {
590 qWarning() << QString("[%3] PEP Publish failed: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full());
591 emit publish_error(task->node(),task->item());
592 }
593 }
594
get(const Jid & jid,const QString & node,const QString & id)595 void PEPManager::get(const Jid& jid, const QString& node, const QString& id)
596 {
597 PEPGetTask* g = new PEPGetTask(client_->rootTask(),jid.bare(),node,id);
598 connect(g, SIGNAL(finished()), SLOT(getFinished()));
599 g->go(true);
600 }
601
messageReceived(const Message & m)602 void PEPManager::messageReceived(const Message& m)
603 {
604 if (m.type() != "error") {
605 foreach(PubSubRetraction i, m.pubsubRetractions()) {
606 emit itemRetracted(m.from(),m.pubsubNode(), i);
607 }
608 foreach(PubSubItem i, m.pubsubItems()) {
609 emit itemPublished(m.from(),m.pubsubNode(),i);
610 }
611 }
612 }
613
614 /*void PEPManager::serverFeaturesChanged()
615 {
616 if (!available_ && serverInfo_->hasPEP()) {
617 GetPubSubSubscriptionsTask* task = new GetPubSubSubscriptionsTask(client_->rootTask(),client_->jid());
618 connect(task,SIGNAL(finished()),SLOT(getSelfSubscriptionsTaskFinished()));
619 task->go(true);
620 }
621 else if (available_ && !serverInfo_->hasPEP()) {
622 ensured_nodes_.clear();
623 setAvailable(false);
624 }
625 }*/
626
getFinished()627 void PEPManager::getFinished()
628 {
629 PEPGetTask* task = (PEPGetTask*) sender();
630 if (task->success()) {
631 // Act as if the item was published. This is a convenience
632 // implementation, probably should be changed later.
633 if (!task->items().isEmpty()) {
634 emit itemPublished(task->jid(),task->node(),task->items().first());
635 }
636 }
637 else {
638 qWarning() << QString("[%3] PEP Get failed: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full());
639 }
640 }
641
642 /*
643 void PEPManager::getSubscriptions(const Jid& jid)
644 {
645 GetPubSubSubscriptionsTask* task = new GetPubSubSubscriptionsTask(client_->rootTask(),jid);
646 connect(task,SIGNAL(finished()),SLOT(getSubscriptionsTaskFinished()));
647 task->go(true);
648 }
649
650 void PEPManager::getSelfSubscriptionsTaskFinished()
651 {
652 GetPubSubSubscriptionsTask* task = (GetPubSubSubscriptionsTask*) sender();
653 if (task->success() || task->statusCode() == 404) {
654 QStringList nodes = nodes_;
655 foreach(PubSubSubscription s, task->subscriptions()) {
656 if (nodes.contains(s.node())) {
657 nodes.remove(s.node());
658 ensured_nodes_ += s.node();
659 emit ready(s.node());
660
661 // Subscribe to our own nodes
662 if (s.state() == PubSubSubscription::None && s.node() != "urn:xmpp:avatar:data") {
663 subscribe(client_->jid().bare(),s.node());
664 }
665 }
666 }
667
668 // Create remaining nodes
669 foreach(QString node, nodes) {
670 createNode(node);
671 }
672 }
673 else {
674 qWarning(QString("[%3] Error getting own subscriptions: '%1' (%2)").arg(task->statusString()).arg(QString::number(task->statusCode())).arg(client_->jid().full()));
675 }
676 }
677
678 void PEPManager::getSubscriptionsTaskFinished()
679 {
680 GetPubSubSubscriptionsTask* task = (GetPubSubSubscriptionsTask*) sender();
681 if (task->success()) {
682 emit getSubscriptions_success(task->jid(), task->subscriptions());
683 }
684 else {
685 emit getSubscriptions_error(task->jid(),task->statusCode(), task->statusString());
686 }
687 }*/
688