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