QXmppMucManager.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /*
  2. * Copyright (C) 2008-2012 The QXmpp developers
  3. *
  4. * Author:
  5. * Jeremy Lainé
  6. *
  7. * Source:
  8. * http://code.google.com/p/qxmpp
  9. *
  10. * This file is a part of QXmpp library.
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. */
  23. #include <QDomElement>
  24. #include <QMap>
  25. #include "QXmppClient.h"
  26. #include "QXmppConstants.h"
  27. #include "QXmppDiscoveryManager.h"
  28. #include "QXmppMessage.h"
  29. #include "QXmppMucIq.h"
  30. #include "QXmppMucManager.h"
  31. #include "QXmppUtils.h"
  32. class QXmppMucManagerPrivate
  33. {
  34. public:
  35. QMap<QString, QXmppMucRoom*> rooms;
  36. };
  37. class QXmppMucRoomPrivate
  38. {
  39. public:
  40. QString ownJid() const { return jid + "/" + nickName; }
  41. QXmppClient *client;
  42. QXmppDiscoveryManager *discoManager;
  43. QXmppMucRoom::Actions allowedActions;
  44. QString jid;
  45. QString name;
  46. QMap<QString, QXmppPresence> participants;
  47. QString password;
  48. QMap<QString, QXmppMucItem> permissions;
  49. QSet<QString> permissionsQueue;
  50. QString nickName;
  51. QString subject;
  52. };
  53. /// Constructs a new QXmppMucManager.
  54. QXmppMucManager::QXmppMucManager()
  55. {
  56. d = new QXmppMucManagerPrivate;
  57. }
  58. /// Destroys a QXmppMucManager.
  59. QXmppMucManager::~QXmppMucManager()
  60. {
  61. delete d;
  62. }
  63. /// Adds the given chat room to the set of managed rooms.
  64. ///
  65. /// \param roomJid
  66. QXmppMucRoom *QXmppMucManager::addRoom(const QString &roomJid)
  67. {
  68. QXmppMucRoom *room = d->rooms.value(roomJid);
  69. if (!room) {
  70. room = new QXmppMucRoom(client(), roomJid, this);
  71. d->rooms.insert(roomJid, room);
  72. connect(room, SIGNAL(destroyed(QObject*)),
  73. this, SLOT(_q_roomDestroyed(QObject*)));
  74. // emit signal
  75. emit roomAdded(room);
  76. }
  77. return room;
  78. }
  79. /// Returns the list of managed rooms.
  80. QList<QXmppMucRoom*> QXmppMucManager::rooms() const
  81. {
  82. return d->rooms.values();
  83. }
  84. /// \cond
  85. QStringList QXmppMucManager::discoveryFeatures() const
  86. {
  87. // XEP-0045: Multi-User Chat
  88. return QStringList()
  89. << ns_muc
  90. << ns_muc_admin
  91. << ns_muc_owner
  92. << ns_muc_user
  93. << ns_conference;
  94. }
  95. bool QXmppMucManager::handleStanza(const QDomElement &element)
  96. {
  97. if (element.tagName() == "iq")
  98. {
  99. if (QXmppMucAdminIq::isMucAdminIq(element))
  100. {
  101. QXmppMucAdminIq iq;
  102. iq.parse(element);
  103. QXmppMucRoom *room = d->rooms.value(iq.from());
  104. if (room && iq.type() == QXmppIq::Result && room->d->permissionsQueue.remove(iq.id())) {
  105. foreach (const QXmppMucItem &item, iq.items()) {
  106. const QString jid = item.jid();
  107. if (!room->d->permissions.contains(jid))
  108. room->d->permissions.insert(jid, item);
  109. }
  110. if (room->d->permissionsQueue.isEmpty()) {
  111. emit room->permissionsReceived(room->d->permissions.values());
  112. }
  113. }
  114. return true;
  115. }
  116. else if (QXmppMucOwnerIq::isMucOwnerIq(element))
  117. {
  118. QXmppMucOwnerIq iq;
  119. iq.parse(element);
  120. QXmppMucRoom *room = d->rooms.value(iq.from());
  121. if (room && iq.type() == QXmppIq::Result && !iq.form().isNull())
  122. emit room->configurationReceived(iq.form());
  123. return true;
  124. }
  125. }
  126. return false;
  127. }
  128. void QXmppMucManager::setClient(QXmppClient* client)
  129. {
  130. bool check;
  131. Q_UNUSED(check);
  132. QXmppClientExtension::setClient(client);
  133. check = connect(client, SIGNAL(messageReceived(QXmppMessage)),
  134. this, SLOT(_q_messageReceived(QXmppMessage)));
  135. Q_ASSERT(check);
  136. }
  137. /// \endcond
  138. void QXmppMucManager::_q_messageReceived(const QXmppMessage &msg)
  139. {
  140. if (msg.type() != QXmppMessage::Normal)
  141. return;
  142. // process room invitations
  143. const QString roomJid = msg.mucInvitationJid();
  144. if (!roomJid.isEmpty() && (!d->rooms.contains(roomJid) || !d->rooms.value(roomJid)->isJoined())) {
  145. emit invitationReceived(roomJid, msg.from(), msg.mucInvitationReason());
  146. }
  147. }
  148. void QXmppMucManager::_q_roomDestroyed(QObject *object)
  149. {
  150. const QString key = d->rooms.key(static_cast<QXmppMucRoom*>(object));
  151. d->rooms.remove(key);
  152. }
  153. /// Constructs a new QXmppMucRoom.
  154. ///
  155. /// \param parent
  156. QXmppMucRoom::QXmppMucRoom(QXmppClient *client, const QString &jid, QObject *parent)
  157. : QObject(parent)
  158. {
  159. bool check;
  160. Q_UNUSED(check);
  161. d = new QXmppMucRoomPrivate;
  162. d->allowedActions = NoAction;
  163. d->client = client;
  164. d->discoManager = client->findExtension<QXmppDiscoveryManager>();
  165. d->jid = jid;
  166. check = connect(d->client, SIGNAL(disconnected()),
  167. this, SLOT(_q_disconnected()));
  168. Q_ASSERT(check);
  169. check = connect(d->client, SIGNAL(messageReceived(QXmppMessage)),
  170. this, SLOT(_q_messageReceived(QXmppMessage)));
  171. Q_ASSERT(check);
  172. check = connect(d->client, SIGNAL(presenceReceived(QXmppPresence)),
  173. this, SLOT(_q_presenceReceived(QXmppPresence)));
  174. Q_ASSERT(check);
  175. if (d->discoManager) {
  176. check = connect(d->discoManager, SIGNAL(infoReceived(QXmppDiscoveryIq)),
  177. this, SLOT(_q_discoveryInfoReceived(QXmppDiscoveryIq)));
  178. Q_ASSERT(check);
  179. }
  180. // convenience signals for properties
  181. check = connect(this, SIGNAL(joined()), this, SIGNAL(isJoinedChanged()));
  182. Q_ASSERT(check);
  183. check = connect(this, SIGNAL(left()), this, SIGNAL(isJoinedChanged()));
  184. Q_ASSERT(check);
  185. }
  186. /// Destroys a QXmppMucRoom.
  187. QXmppMucRoom::~QXmppMucRoom()
  188. {
  189. delete d;
  190. }
  191. /// Returns the actions you are allowed to perform on the room.
  192. QXmppMucRoom::Actions QXmppMucRoom::allowedActions() const
  193. {
  194. return d->allowedActions;
  195. }
  196. /// Bans the specified user from the chat room.
  197. ///
  198. /// The specified \a jid is the Bare JID of the form "user@host".
  199. ///
  200. /// \return true if the request was sent, false otherwise
  201. bool QXmppMucRoom::ban(const QString &jid, const QString &reason)
  202. {
  203. if (!QXmppUtils::jidToResource(jid).isEmpty()) {
  204. qWarning("QXmppMucRoom::ban expects a bare JID");
  205. return false;
  206. }
  207. QXmppMucItem item;
  208. item.setAffiliation(QXmppMucItem::OutcastAffiliation);
  209. item.setJid(jid);
  210. item.setReason(reason);
  211. QXmppMucAdminIq iq;
  212. iq.setType(QXmppIq::Set);
  213. iq.setTo(d->jid);
  214. iq.setItems(QList<QXmppMucItem>() << item);
  215. return d->client->sendPacket(iq);
  216. }
  217. /// Returns true if you are currently in the room.
  218. bool QXmppMucRoom::isJoined() const
  219. {
  220. return d->participants.contains(d->ownJid());
  221. }
  222. /// Returns the chat room's bare JID.
  223. QString QXmppMucRoom::jid() const
  224. {
  225. return d->jid;
  226. }
  227. /// Joins the chat room.
  228. ///
  229. /// \return true if the request was sent, false otherwise
  230. bool QXmppMucRoom::join()
  231. {
  232. if (isJoined() || d->nickName.isEmpty())
  233. return false;
  234. // reflect our current presence in the chat room
  235. QXmppPresence packet = d->client->clientPresence();
  236. packet.setTo(d->ownJid());
  237. packet.setType(QXmppPresence::Available);
  238. packet.setMucPassword(d->password);
  239. packet.setMucSupported(true);
  240. return d->client->sendPacket(packet);
  241. }
  242. /// Kicks the specified user from the chat room.
  243. ///
  244. /// The specified \a jid is the Occupant JID of the form "room@service/nick".
  245. ///
  246. /// \return true if the request was sent, false otherwise
  247. bool QXmppMucRoom::kick(const QString &jid, const QString &reason)
  248. {
  249. QXmppMucItem item;
  250. item.setNick(QXmppUtils::jidToResource(jid));
  251. item.setRole(QXmppMucItem::NoRole);
  252. item.setReason(reason);
  253. QXmppMucAdminIq iq;
  254. iq.setType(QXmppIq::Set);
  255. iq.setTo(d->jid);
  256. iq.setItems(QList<QXmppMucItem>() << item);
  257. return d->client->sendPacket(iq);
  258. }
  259. /// Leaves the chat room.
  260. ///
  261. /// \param message An optional message.
  262. ///
  263. /// \return true if the request was sent, false otherwise
  264. bool QXmppMucRoom::leave(const QString &message)
  265. {
  266. QXmppPresence packet;
  267. packet.setTo(d->ownJid());
  268. packet.setType(QXmppPresence::Unavailable);
  269. packet.setStatusText(message);
  270. return d->client->sendPacket(packet);
  271. }
  272. /// Returns the chat room's human-readable name.
  273. ///
  274. /// This name will only be available after the room has been joined.
  275. QString QXmppMucRoom::name() const
  276. {
  277. return d->name;
  278. }
  279. /// Returns your own nickname.
  280. QString QXmppMucRoom::nickName() const
  281. {
  282. return d->nickName;
  283. }
  284. /// Invites a user to the chat room.
  285. ///
  286. /// \param jid
  287. /// \param reason
  288. ///
  289. /// \return true if the request was sent, false otherwise
  290. bool QXmppMucRoom::sendInvitation(const QString &jid, const QString &reason)
  291. {
  292. QXmppMessage message;
  293. message.setTo(jid);
  294. message.setType(QXmppMessage::Normal);
  295. message.setMucInvitationJid(d->jid);
  296. message.setMucInvitationReason(reason);
  297. return d->client->sendPacket(message);
  298. }
  299. /// Sends a message to the room.
  300. ///
  301. /// \return true if the request was sent, false otherwise
  302. bool QXmppMucRoom::sendMessage(const QString &text)
  303. {
  304. QXmppMessage msg;
  305. msg.setTo(d->jid);
  306. msg.setType(QXmppMessage::GroupChat);
  307. msg.setBody(text);
  308. return d->client->sendPacket(msg);
  309. }
  310. /// Sets your own nickname.
  311. ///
  312. /// You need to set your nickname before calling join().
  313. ///
  314. /// \param nickName
  315. void QXmppMucRoom::setNickName(const QString &nickName)
  316. {
  317. if (nickName == d->nickName)
  318. return;
  319. const bool wasJoined = isJoined();
  320. d->nickName = nickName;
  321. emit nickNameChanged(nickName);
  322. // if we had already joined the room, request nickname change
  323. if (wasJoined) {
  324. QXmppPresence packet = d->client->clientPresence();
  325. packet.setTo(d->ownJid());
  326. packet.setType(QXmppPresence::Available);
  327. d->client->sendPacket(packet);
  328. }
  329. }
  330. /// Returns the "Full JID" of the given participant.
  331. ///
  332. /// The specified \a jid is the Occupant JID of the form "room@service/nick".
  333. QString QXmppMucRoom::participantFullJid(const QString &jid) const
  334. {
  335. if (d->participants.contains(jid))
  336. return d->participants.value(jid).mucItem().jid();
  337. else
  338. return QString();
  339. }
  340. /// Returns the presence for the given participant.
  341. ///
  342. /// The specified \a jid is the Occupant JID of the form "room@service/nick".
  343. QXmppPresence QXmppMucRoom::participantPresence(const QString &jid) const
  344. {
  345. if (d->participants.contains(jid))
  346. return d->participants.value(jid);
  347. QXmppPresence presence;
  348. presence.setFrom(jid);
  349. presence.setType(QXmppPresence::Unavailable);
  350. return presence;
  351. }
  352. /// Returns the list of participant JIDs.
  353. ///
  354. /// These JIDs are Occupant JIDs of the form "room@service/nick".
  355. QStringList QXmppMucRoom::participants() const
  356. {
  357. return d->participants.keys();
  358. }
  359. /// Returns the chat room password.
  360. QString QXmppMucRoom::password() const
  361. {
  362. return d->password;
  363. }
  364. /// Sets the chat room password.
  365. ///
  366. /// \param password
  367. void QXmppMucRoom::setPassword(const QString &password)
  368. {
  369. d->password = password;
  370. }
  371. /// Returns the room's subject.
  372. QString QXmppMucRoom::subject() const
  373. {
  374. return d->subject;
  375. }
  376. /// Sets the chat room's subject.
  377. ///
  378. /// \param subject
  379. void QXmppMucRoom::setSubject(const QString &subject)
  380. {
  381. QXmppMessage msg;
  382. msg.setTo(d->jid);
  383. msg.setType(QXmppMessage::GroupChat);
  384. msg.setSubject(subject);
  385. d->client->sendPacket(msg);
  386. }
  387. /// Request the configuration form for the chat room.
  388. ///
  389. /// \return true if the request was sent, false otherwise
  390. ///
  391. /// \sa configurationReceived()
  392. bool QXmppMucRoom::requestConfiguration()
  393. {
  394. QXmppMucOwnerIq iq;
  395. iq.setTo(d->jid);
  396. return d->client->sendPacket(iq);
  397. }
  398. /// Send the configuration form for the chat room.
  399. ///
  400. /// \param form
  401. ///
  402. /// \return true if the request was sent, false otherwise
  403. bool QXmppMucRoom::setConfiguration(const QXmppDataForm &form)
  404. {
  405. QXmppMucOwnerIq iqPacket;
  406. iqPacket.setType(QXmppIq::Set);
  407. iqPacket.setTo(d->jid);
  408. iqPacket.setForm(form);
  409. return d->client->sendPacket(iqPacket);
  410. }
  411. /// Request the room's permissions.
  412. ///
  413. /// \return true if the request was sent, false otherwise
  414. ///
  415. /// \sa permissionsReceived()
  416. bool QXmppMucRoom::requestPermissions()
  417. {
  418. QList<QXmppMucItem::Affiliation> affiliations;
  419. affiliations << QXmppMucItem::OwnerAffiliation;
  420. affiliations << QXmppMucItem::AdminAffiliation;
  421. affiliations << QXmppMucItem::MemberAffiliation;
  422. affiliations << QXmppMucItem::OutcastAffiliation;
  423. d->permissions.clear();
  424. d->permissionsQueue.clear();
  425. foreach (QXmppMucItem::Affiliation affiliation, affiliations) {
  426. QXmppMucItem item;
  427. item.setAffiliation(affiliation);
  428. QXmppMucAdminIq iq;
  429. iq.setTo(d->jid);
  430. iq.setItems(QList<QXmppMucItem>() << item);
  431. if (!d->client->sendPacket(iq))
  432. return false;
  433. d->permissionsQueue += iq.id();
  434. }
  435. return true;
  436. }
  437. /// Sets the room's permissions.
  438. ///
  439. /// \param permissions
  440. ///
  441. /// \return true if the request was sent, false otherwise
  442. bool QXmppMucRoom::setPermissions(const QList<QXmppMucItem> &permissions)
  443. {
  444. QList<QXmppMucItem> items;
  445. // Process changed members
  446. foreach (const QXmppMucItem &item, permissions) {
  447. const QString jid = item.jid();
  448. if (d->permissions.value(jid).affiliation() != item.affiliation())
  449. items << item;
  450. d->permissions.remove(jid);
  451. }
  452. // Process deleted members
  453. foreach (const QString &jid, d->permissions.keys()) {
  454. QXmppMucItem item;
  455. item.setAffiliation(QXmppMucItem::NoAffiliation);
  456. item.setJid(jid);
  457. items << item;
  458. d->permissions.remove(jid);
  459. }
  460. // Don't send request if there are no changes
  461. if (items.isEmpty())
  462. return false;
  463. QXmppMucAdminIq iq;
  464. iq.setTo(d->jid);
  465. iq.setType(QXmppIq::Set);
  466. iq.setItems(items);
  467. return d->client->sendPacket(iq);
  468. }
  469. void QXmppMucRoom::_q_disconnected()
  470. {
  471. const bool wasJoined = isJoined();
  472. // clear chat room participants
  473. const QStringList removed = d->participants.keys();
  474. d->participants.clear();
  475. foreach (const QString &jid, removed)
  476. emit participantRemoved(jid);
  477. emit participantsChanged();
  478. // update available actions
  479. if (d->allowedActions != NoAction) {
  480. d->allowedActions = NoAction;
  481. emit allowedActionsChanged(d->allowedActions);
  482. }
  483. // emit "left" signal if we had joined the room
  484. if (wasJoined)
  485. emit left();
  486. }
  487. void QXmppMucRoom::_q_discoveryInfoReceived(const QXmppDiscoveryIq &iq)
  488. {
  489. if (iq.from() == d->jid) {
  490. QString name;
  491. foreach (const QXmppDiscoveryIq::Identity &identity, iq.identities()) {
  492. if (identity.category() == "conference") {
  493. name = identity.name();
  494. break;
  495. }
  496. }
  497. if (name != d->name) {
  498. d->name = name;
  499. emit nameChanged(name);
  500. }
  501. }
  502. }
  503. void QXmppMucRoom::_q_messageReceived(const QXmppMessage &message)
  504. {
  505. if (QXmppUtils::jidToBareJid(message.from())!= d->jid)
  506. return;
  507. // handle message subject
  508. const QString subject = message.subject();
  509. if (!subject.isEmpty()) {
  510. d->subject = subject;
  511. emit subjectChanged(subject);
  512. }
  513. emit messageReceived(message);
  514. }
  515. void QXmppMucRoom::_q_presenceReceived(const QXmppPresence &presence)
  516. {
  517. const QString jid = presence.from();
  518. // if our own presence changes, reflect it in the chat room
  519. if (isJoined() && jid == d->client->configuration().jid()) {
  520. QXmppPresence packet = d->client->clientPresence();
  521. packet.setTo(d->ownJid());
  522. d->client->sendPacket(packet);
  523. }
  524. if (QXmppUtils::jidToBareJid(jid) != d->jid)
  525. return;
  526. if (presence.type() == QXmppPresence::Available) {
  527. const bool added = !d->participants.contains(jid);
  528. d->participants.insert(jid, presence);
  529. // refresh allowed actions
  530. if (jid == d->ownJid()) {
  531. QXmppMucItem mucItem = presence.mucItem();
  532. Actions newActions = NoAction;
  533. // role
  534. if (mucItem.role() == QXmppMucItem::ModeratorRole)
  535. newActions |= (KickAction | SubjectAction);
  536. // affiliation
  537. if (mucItem.affiliation() == QXmppMucItem::OwnerAffiliation)
  538. newActions |= (ConfigurationAction | PermissionsAction | SubjectAction);
  539. else if (mucItem.affiliation() == QXmppMucItem::AdminAffiliation)
  540. newActions |= (PermissionsAction | SubjectAction);
  541. if (newActions != d->allowedActions) {
  542. d->allowedActions = newActions;
  543. emit allowedActionsChanged(d->allowedActions);
  544. }
  545. }
  546. if (added) {
  547. emit participantAdded(jid);
  548. emit participantsChanged();
  549. if (jid == d->ownJid()) {
  550. // request room information
  551. if (d->discoManager)
  552. d->discoManager->requestInfo(d->jid);
  553. emit joined();
  554. }
  555. } else {
  556. emit participantChanged(jid);
  557. }
  558. }
  559. else if (presence.type() == QXmppPresence::Unavailable) {
  560. if (d->participants.contains(jid)) {
  561. d->participants.insert(jid, presence);
  562. emit participantRemoved(jid);
  563. d->participants.remove(jid);
  564. emit participantsChanged();
  565. // check whether this was our own presence
  566. if (jid == d->ownJid()) {
  567. // check whether we were kicked
  568. if (presence.mucStatusCodes().contains(307)) {
  569. const QString actor = presence.mucItem().actor();
  570. const QString reason = presence.mucItem().reason();
  571. emit kicked(actor, reason);
  572. }
  573. // clear chat room participants
  574. const QStringList removed = d->participants.keys();
  575. d->participants.clear();
  576. foreach (const QString &jid, removed)
  577. emit participantRemoved(jid);
  578. emit participantsChanged();
  579. // update available actions
  580. if (d->allowedActions != NoAction) {
  581. d->allowedActions = NoAction;
  582. emit allowedActionsChanged(d->allowedActions);
  583. }
  584. // notify user we left the room
  585. emit left();
  586. }
  587. }
  588. }
  589. else if (presence.type() == QXmppPresence::Error) {
  590. if (presence.isMucSupported()) {
  591. // emit error
  592. emit error(presence.error());
  593. // notify the user we left the room
  594. emit left();
  595. }
  596. }
  597. }