1
/****************************************************************************
5
** Copyright Ā© 2011 Ruslan Nigmatullin <euroelessar@yandex.ru>
6
** Copyright Ā© 2011 Aleksey Sidorov <gorthauer87@yandex.ru>
8
*****************************************************************************
10
** $JREEN_BEGIN_LICENSE$
11
** This program is free software: you can redistribute it and/or modify
12
** it under the terms of the GNU General Public License as published by
13
** the Free Software Foundation, either version 2 of the License, or
14
** (at your option) any later version.
16
** This program is distributed in the hope that it will be useful,
17
** but WITHOUT ANY WARRANTY; without even the implied warranty of
18
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
** See the GNU General Public License for more details.
21
** You should have received a copy of the GNU General Public License
22
** along with this program. If not, see http://www.gnu.org/licenses/.
23
** $JREEN_END_LICENSE$
25
****************************************************************************/
26
#include "mucroom_p.h"
34
KickParticipantsAndVisitors,
40
static char mucPrivelegesByRole[RolePrivelegesCount][4] = {
48
bool checkParticipantPrivelege(MUCRolePrivilege priv, MUCRoom::Role role)
50
return role != MUCRoom::RoleNone && mucPrivelegesByRole[priv][role - 1];
53
// enum MUCAffiliationPrivilege
57
// KickParticipantsAndVisitors,
60
// RolePrivelegesCount
63
// char mucPrivelegesByAffiliation[RolePrivelegesCount][4] = {
71
// bool checkParticipantPrivelege(MUCAffiliationPrivilege priv, MUCRoom::Affiliation aff)
73
// return aff != MUCRoom::AffiliationOutcast && mucPrivelegesByAffiliation[priv][aff - 1];
76
class MUCRoom::ParticipantPrivate
79
ParticipantPrivate() : joined(false) {}
81
void init(const Presence &pres)
83
query = pres.payload<MUCRoomUserQuery>();
86
MUCRoomUserQuery::Ptr query;
90
MUCRoom::Participant::Participant() : d_ptr(new ParticipantPrivate)
94
MUCRoom::Participant::~Participant()
98
MUCRoom::Affiliation MUCRoom::Participant::affiliation() const
100
return d_func()->query->item.affiliation;
103
MUCRoom::Role MUCRoom::Participant::role() const
105
return d_func()->query->item.role;
108
bool MUCRoom::Participant::isSelf() const
110
return d_func()->query->flags & MUCRoomUserQuery::Self;
113
bool MUCRoom::Participant::isNickChanged() const
115
return d_func()->query->flags & MUCRoomUserQuery::NickChanged;
118
bool MUCRoom::Participant::isBanned() const
120
return d_func()->query->flags & MUCRoomUserQuery::Banned;
123
bool MUCRoom::Participant::isKicked() const
125
return d_func()->query->flags & MUCRoomUserQuery::Kicked;
128
bool MUCRoom::Participant::isJoined() const
130
return d_func()->joined;
133
QString MUCRoom::Participant::newNick() const
135
return d_func()->query->item.nick;
138
QString MUCRoom::Participant::reason() const
140
return d_func()->query->item.reason;
143
JID MUCRoom::Participant::realJID() const
145
return d_func()->query->item.jid;
148
class MUCRoom::ItemPrivate : public QSharedData
152
ItemPrivate(const ItemPrivate &o) : QSharedData(o), jid(o.jid), reason(o.reason) {}
157
MUCRoom::Item::Item(const JID &jid, const QString &reason) : d_ptr(new MUCRoom::ItemPrivate)
160
d_ptr->reason = reason;
163
MUCRoom::Item::Item(const MUCRoom::Item &o) : d_ptr(o.d_ptr)
167
MUCRoom::Item &MUCRoom::Item::operator =(const MUCRoom::Item &o)
173
MUCRoom::Item::~Item()
177
JID MUCRoom::Item::jid() const
182
void MUCRoom::Item::setJID(const JID &jid)
187
QString MUCRoom::Item::reason() const
189
return d_ptr->reason;
192
void MUCRoom::Item::setReason(const QString &reason)
194
d_ptr->reason = reason;
197
void MUCRoomPrivate::handlePresence(const Presence &pres)
200
Logger::debug() << "handle presence" << pres.from();
201
if (Error::Ptr e = pres.payload<Error>()) {
202
startedJoining = false;
206
MUCRoom::Participant part;
207
part.d_func()->query = pres.payload<MUCRoomUserQuery>();
208
if (!part.d_func()->query)
210
if (pres.subtype() == Presence::Unavailable) {
211
participantsHash.remove(pres.from().resource());
213
if (startedJoining) {
214
startedJoining = false;
215
QHashIterator<QString,MUCRoomUserQuery::Ptr> it(participantsHash);
216
MUCRoom::Participant tmp;
217
tmp.d_func()->query = MUCRoomUserQuery::Ptr::create();
218
Presence hookPres(Presence::Unavailable, client->jid());
219
JID bareJid = jid.bareJID();
220
while (it.hasNext()) {
221
QString nick = it.next().key();
222
hookPres.setFrom(bareJid.withResource(nick));
223
emit q->presenceReceived(hookPres, &tmp);
226
if (!participantsHash.contains(pres.from().resource()))
227
part.d_func()->joined = true;
228
participantsHash.insert(pres.from().resource(), part.d_func()->query);
230
if (part.isNickChanged() && pres.from().resource() == jid.resource())
231
jid.setResource(part.newNick());
232
emit q->presenceReceived(pres, &part);
233
if (pres.from().resource() == jid.resource()) {
235
affiliation = part.affiliation();
236
if(pres.subtype() == Presence::Unavailable) {
237
participantsHash.clear();
240
} else if (!isJoined) {
247
void MUCRoomPrivate::handleMessage(const Message &msg)
251
bool isPrivate = (msg.subtype() != Message::Groupchat);
252
if (msg.from() == jid.bare()) {
253
emit q->serviceMessageReceived(msg);
256
if (!msg.subject().isEmpty()) {
257
subject = msg.subject();
258
emit q->subjectChanged(subject, msg.from().resource());
261
// We want to receive "service" messages like chat states for private sessions
262
if (!nice && (isPrivate || !msg.body().isEmpty())) {
263
emit q->messageReceived(msg, isPrivate);
267
MUCRoom::MUCRoom(Client *client, const JID &jid) :
269
d_ptr(new MUCRoomPrivate(this))
274
d->session = new MUCMessageSession(this);
275
ClientPrivate::get(d->client)->rooms.insert(d->jid.bare(), d);
276
connect(client, SIGNAL(connected()), this, SLOT(onConnected()));
277
connect(client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), this, SLOT(onDisconnected()));
285
ClientPrivate::get(d->client)->rooms.remove(d->jid.bare());
288
QString MUCRoom::id() const
290
return d_func()->jid.bare();
293
QString MUCRoom::service() const
295
return d_func()->jid.domain();
298
void MUCRoom::setPassword(const QString &password)
300
d_func()->password = password;
303
bool MUCRoom::isJoined() const
305
return d_func()->isJoined;
308
Presence::Type MUCRoom::presence() const
310
return d_func()->currentPresence.subtype();
313
void MUCRoom::join(Presence::Type type, const QString &message, int priority)
316
if (d->startedJoining)
318
d->startedJoining = true;
319
Presence pres(type, d->jid, message, priority);
320
MUCRoomQuery *query = new MUCRoomQuery(d->password);
321
query->setMaxChars(d->maxChars);
322
query->setMaxStanzas(d->maxStanzas);
323
query->setSeconds(d->seconds);
324
query->setSince(d->since);
325
pres.addExtension(query);
326
d->currentPresence = pres;
327
d->client->send(pres);
333
Presence pres = d->client->presence();
334
join(pres.subtype(), pres.status(), pres.priority());
337
enum MUCRoomRequestContext
339
MUCRoomRequestConfig = 100,
342
MUCRoomEndRequestList = MUCRoomRequestList + 20,
346
void MUCRoom::requestRoomConfig()
349
IQ iq(IQ::Get, d->jid.bareJID());
350
iq.addExtension(new MUCRoomOwnerQuery);
351
d->client->send(iq, this, SLOT(handleIQ(Jreen::IQ,int)), MUCRoomRequestConfig);
354
void MUCRoom::requestList(Jreen::MUCRoom::Affiliation affiliation)
357
IQ iq(IQ::Get, d->jid.bareJID());
358
iq.addExtension(new MUCRoomAdminQuery(affiliation));
359
d->client->send(iq, this, SLOT(handleIQ(Jreen::IQ,int)), MUCRoomRequestList + affiliation);
362
void MUCRoom::setList(Jreen::MUCRoom::Affiliation affiliation, const Jreen::MUCRoom::ItemList &items)
365
IQ iq(IQ::Set, d->jid.bareJID());
366
MUCRoomAdminQuery *query = new MUCRoomAdminQuery;
369
foreach (const Item &item, items) {
371
tmp.affiliation = affiliation;
372
tmp.jid = item.jid();
373
tmp.reason = item.reason();
376
iq.addExtension(query);
377
d->client->send(iq, this, SLOT(handleIQ(Jreen::IQ,int)), MUCRoomSetList);
380
void MUCRoom::setRoomConfig(const Jreen::DataForm::Ptr &form)
383
IQ iq(IQ::Set, d->jid.bareJID());
384
iq.addExtension(new MUCRoomOwnerQuery(form));
385
d->client->send(iq, this, SLOT(handleIQ(Jreen::IQ,int)), MUCRoomSubmitConfig);
388
void MUCRoom::leave(const QString &message)
391
if (d->currentPresence.subtype() == Presence::Unavailable)
394
Presence pres(Presence::Unavailable, d->jid, message);
395
d->currentPresence = pres;
396
d->client->send(pres);
399
QString MUCRoom::nick() const
401
return d_func()->jid.resource();
404
JID MUCRoom::realJid(const QString &nick)
406
MUCRoomUserQuery::Ptr query = d_func()->participantsHash.value(nick);
407
return query ? query->item.jid : JID();
410
void MUCRoom::setNick(const QString &nick)
415
newJid.setResource(nick);
416
Presence pres(d->currentPresence.subtype(), newJid,
417
d->currentPresence.status(), d->currentPresence.priority());
418
d->client->send(pres);
420
d->jid.setResource(nick);
424
void MUCRoom::setHistoryMaxChars(int maxChars)
426
d_func()->maxChars = maxChars;
429
void MUCRoom::setHistoryMaxStanzas(int maxStanzas)
431
d_func()->maxStanzas = maxStanzas;
434
void MUCRoom::setHistorySeconds(int seconds)
436
d_func()->seconds = seconds;
439
void MUCRoom::setHistorySince(const QDateTime &since)
441
d_func()->since = since;
444
void MUCRoom::setPresence(Presence::Type type, const QString &message, int priority)
447
Presence pres(type, d->jid, message, priority);
448
d->client->send(pres);
451
void MUCRoom::invite(const JID &jid, const QString &reason, const QString &thread)
454
if (!d->isJoined || !d->client)
456
Message message(Message::Normal, jid);
457
message.addExtension(new MUCRoomUserQuery(MUCRoomUserQuery::Invite, jid, reason, thread));
458
d_func()->client->send(message);
461
void MUCRoom::kick(const QString &nick, const QString &reason)
463
setRole(nick, RoleNone, reason);
466
void MUCRoom::ban(const QString &nick, const QString &reason)
469
MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
472
// May be it's already full jid, who knows?
474
if (victim.node().isEmpty() || victim.domain().isEmpty())
477
victim = query->item.jid.bareJID();
479
setAffiliation(victim, AffiliationOutcast, reason);
482
void MUCRoom::setRole(const QString &nick, Role role, const QString &reason)
485
IQ iq(IQ::Set, d->jid.bareJID());
486
iq.addExtension(new MUCRoomAdminQuery(nick, role, reason));
490
void MUCRoom::setAffiliation(const JID &jid, Affiliation affiliation, const QString &reason)
493
IQ iq(IQ::Set, d->jid.bareJID());
494
iq.addExtension(new MUCRoomAdminQuery(jid, affiliation, reason));
498
void MUCRoom::setAffiliation(const QString &nick, Affiliation affiliation, const QString &reason)
501
IQ iq(IQ::Set, d->jid.bareJID());
502
iq.addExtension(new MUCRoomAdminQuery(nick, affiliation, reason));
506
void MUCRoom::send(const QString &message)
509
d->session->sendMessage(message);
512
QString MUCRoom::subject() const
514
return d_func()->subject;
517
void MUCRoom::setSubject(const QString &subject)
520
d->session->setSubject(subject);
523
MUCRoom::Affiliation MUCRoom::affiliation() const
525
return d_func()->affiliation;
528
MUCRoom::Role MUCRoom::role() const
530
return d_func()->role;
533
bool MUCRoom::canKick(const QString &nick)
536
MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
539
if (query->item.role == MUCRoom::RoleVisitor || query->item.role == MUCRoom::RoleParticipant)
540
return checkParticipantPrivelege(KickParticipantsAndVisitors, d->role);
544
bool MUCRoom::canBan(const QString &nick)
547
MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
550
if (d->affiliation != MUCRoom::AffiliationAdmin && d->affiliation != MUCRoom::AffiliationOwner)
552
return query->item.affiliation <= MUCRoom::AffiliationMember;
555
void MUCRoom::handleIQ(const Jreen::IQ &iq, int context)
557
if (Error::Ptr e = iq.payload<Error>()) {
561
if (context == MUCRoomRequestConfig) {
562
MUCRoomOwnerQuery::Ptr query = iq.payload<MUCRoomOwnerQuery>();
565
emit configurationReceived(query->form);
566
} else if (context >= MUCRoomRequestList && context < MUCRoomEndRequestList) {
568
MUCRoomAdminQuery::Ptr query = iq.payload<MUCRoomAdminQuery>();
571
foreach (const MUCRoomItem &item, query->items) {
572
if (!item.jid.isValid())
574
items << Item(item.jid, item.reason);
576
Affiliation affiliation = static_cast<Affiliation>(context - MUCRoomRequestList);
577
emit listReceived(affiliation, items);
581
void MUCRoom::onConnected()
584
if (d->currentPresence.subtype() != Presence::Unavailable)
585
join(d->currentPresence.subtype(), d->currentPresence.status(), d->currentPresence.priority());
588
void MUCRoom::onDisconnected()
591
d->startedJoining = false;
592
if (d->currentPresence.subtype() != Presence::Unavailable) {
593
d->participantsHash.clear();