~ubuntu-branches/ubuntu/trusty/jreen/trusty

« back to all changes in this revision

Viewing changes to src/mucroom.cpp

  • Committer: Package Import Robot
  • Author(s): Prasad Murthy
  • Date: 2013-03-08 00:00:33 UTC
  • Revision ID: package-import@ubuntu.com-20130308000033-x8thp6syo1kkh63s
Tags: upstream-1.1.1
ImportĀ upstreamĀ versionĀ 1.1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Jreen
 
4
**
 
5
** Copyright Ā© 2011 Ruslan Nigmatullin <euroelessar@yandex.ru>
 
6
** Copyright Ā© 2011 Aleksey Sidorov <gorthauer87@yandex.ru>
 
7
**
 
8
*****************************************************************************
 
9
**
 
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.
 
15
**
 
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.
 
20
**
 
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$
 
24
**
 
25
****************************************************************************/
 
26
#include "mucroom_p.h"
 
27
 
 
28
namespace Jreen
 
29
{
 
30
enum MUCRolePrivilege
 
31
{
 
32
        SendMessage,
 
33
        ModifySubject,
 
34
        KickParticipantsAndVisitors,
 
35
        GrantVoice,
 
36
        RevokeVoice,
 
37
        RolePrivelegesCount
 
38
};
 
39
 
 
40
static char mucPrivelegesByRole[RolePrivelegesCount][4] = {
 
41
        { 0, 1, 1 },
 
42
        { 0, 1, 1 },
 
43
        { 0, 0, 1 },
 
44
        { 0, 0, 1 },
 
45
        { 0, 0, 1 }
 
46
};
 
47
 
 
48
bool checkParticipantPrivelege(MUCRolePrivilege priv, MUCRoom::Role role)
 
49
{
 
50
        return role != MUCRoom::RoleNone && mucPrivelegesByRole[priv][role - 1];
 
51
}
 
52
 
 
53
//      enum MUCAffiliationPrivilege
 
54
//      {
 
55
//              SendMessage,
 
56
//              ModifySubject,
 
57
//              KickParticipantsAndVisitors,
 
58
//              GrantVoice,
 
59
//              RevokeVoice,
 
60
//              RolePrivelegesCount
 
61
//      };
 
62
 
 
63
//      char mucPrivelegesByAffiliation[RolePrivelegesCount][4] = {
 
64
//              { 0, 1, 1 },
 
65
//              { 0, 1, 1 },
 
66
//              { 0, 1, 1 },
 
67
//              { 0, 0, 1 },
 
68
//              { 0, 0, 1 }
 
69
//      };
 
70
 
 
71
//      bool checkParticipantPrivelege(MUCAffiliationPrivilege priv, MUCRoom::Affiliation aff)
 
72
//      {
 
73
//              return aff != MUCRoom::AffiliationOutcast && mucPrivelegesByAffiliation[priv][aff - 1];
 
74
//      }
 
75
 
 
76
class MUCRoom::ParticipantPrivate
 
77
{
 
78
public:
 
79
        ParticipantPrivate() : joined(false) {}
 
80
 
 
81
        void init(const Presence &pres)
 
82
        {
 
83
                query = pres.payload<MUCRoomUserQuery>();
 
84
        }
 
85
 
 
86
        MUCRoomUserQuery::Ptr query;
 
87
        bool joined;
 
88
};
 
89
 
 
90
MUCRoom::Participant::Participant() : d_ptr(new ParticipantPrivate)
 
91
{
 
92
}
 
93
 
 
94
MUCRoom::Participant::~Participant()
 
95
{
 
96
}
 
97
 
 
98
MUCRoom::Affiliation MUCRoom::Participant::affiliation() const
 
99
{
 
100
        return d_func()->query->item.affiliation;
 
101
}
 
102
 
 
103
MUCRoom::Role MUCRoom::Participant::role() const
 
104
{
 
105
        return d_func()->query->item.role;
 
106
}
 
107
 
 
108
bool MUCRoom::Participant::isSelf() const
 
109
{
 
110
        return d_func()->query->flags & MUCRoomUserQuery::Self;
 
111
}
 
112
 
 
113
bool MUCRoom::Participant::isNickChanged() const
 
114
{
 
115
        return d_func()->query->flags & MUCRoomUserQuery::NickChanged;
 
116
}
 
117
 
 
118
bool MUCRoom::Participant::isBanned() const
 
119
{
 
120
        return d_func()->query->flags & MUCRoomUserQuery::Banned;
 
121
}
 
122
 
 
123
bool MUCRoom::Participant::isKicked() const
 
124
{
 
125
        return d_func()->query->flags & MUCRoomUserQuery::Kicked;
 
126
}
 
127
 
 
128
bool MUCRoom::Participant::isJoined() const
 
129
{
 
130
        return d_func()->joined;
 
131
}
 
132
 
 
133
QString MUCRoom::Participant::newNick() const
 
134
{
 
135
        return d_func()->query->item.nick;
 
136
}
 
137
 
 
138
QString MUCRoom::Participant::reason() const
 
139
{
 
140
        return d_func()->query->item.reason;
 
141
}
 
142
 
 
143
JID MUCRoom::Participant::realJID() const
 
144
{
 
145
        return d_func()->query->item.jid;
 
146
}
 
147
 
 
148
class MUCRoom::ItemPrivate : public QSharedData
 
149
{
 
150
public:
 
151
        ItemPrivate() {}
 
152
        ItemPrivate(const ItemPrivate &o) : QSharedData(o), jid(o.jid), reason(o.reason) {}
 
153
        JID jid;
 
154
        QString reason;
 
155
};
 
156
 
 
157
MUCRoom::Item::Item(const JID &jid, const QString &reason) : d_ptr(new MUCRoom::ItemPrivate)
 
158
{
 
159
        d_ptr->jid = jid;
 
160
        d_ptr->reason = reason;
 
161
}
 
162
 
 
163
MUCRoom::Item::Item(const MUCRoom::Item &o) : d_ptr(o.d_ptr)
 
164
{
 
165
}
 
166
 
 
167
MUCRoom::Item &MUCRoom::Item::operator =(const MUCRoom::Item &o)
 
168
{
 
169
        d_ptr = o.d_ptr;
 
170
        return *this;
 
171
}
 
172
 
 
173
MUCRoom::Item::~Item()
 
174
{
 
175
}
 
176
 
 
177
JID MUCRoom::Item::jid() const
 
178
{
 
179
        return d_ptr->jid;
 
180
}
 
181
 
 
182
void MUCRoom::Item::setJID(const JID &jid)
 
183
{
 
184
        d_ptr->jid = jid;
 
185
}
 
186
 
 
187
QString MUCRoom::Item::reason() const
 
188
{
 
189
        return d_ptr->reason;
 
190
}
 
191
 
 
192
void MUCRoom::Item::setReason(const QString &reason)
 
193
{
 
194
        d_ptr->reason = reason;
 
195
}
 
196
 
 
197
void MUCRoomPrivate::handlePresence(const Presence &pres)
 
198
{
 
199
        Q_Q(MUCRoom);
 
200
        Logger::debug() << "handle presence" << pres.from();
 
201
        if (Error::Ptr e = pres.payload<Error>()) {
 
202
                startedJoining = false;
 
203
                emit q->error(e);
 
204
                return;
 
205
        }
 
206
        MUCRoom::Participant part;
 
207
        part.d_func()->query = pres.payload<MUCRoomUserQuery>();
 
208
        if (!part.d_func()->query)
 
209
                return;
 
210
        if (pres.subtype() == Presence::Unavailable) {
 
211
                participantsHash.remove(pres.from().resource());
 
212
        } else {
 
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);
 
224
                        }
 
225
                }
 
226
                if (!participantsHash.contains(pres.from().resource()))
 
227
                        part.d_func()->joined = true;
 
228
                participantsHash.insert(pres.from().resource(), part.d_func()->query);
 
229
        }
 
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()) {
 
234
                role = part.role();
 
235
                affiliation = part.affiliation();
 
236
                if(pres.subtype() == Presence::Unavailable) {
 
237
                        participantsHash.clear();
 
238
                        isJoined = false;
 
239
                        emit q->leaved();
 
240
                } else if (!isJoined) {
 
241
                        isJoined = true;
 
242
                        emit q->joined();
 
243
                }
 
244
        }
 
245
}
 
246
 
 
247
void MUCRoomPrivate::handleMessage(const Message &msg)
 
248
{
 
249
        Q_Q(MUCRoom);
 
250
        bool nice = false;
 
251
        bool isPrivate = (msg.subtype() != Message::Groupchat);
 
252
        if (msg.from() == jid.bare()) {
 
253
                emit q->serviceMessageReceived(msg);
 
254
                nice = true;
 
255
        }
 
256
        if (!msg.subject().isEmpty()) {
 
257
                subject = msg.subject();
 
258
                emit q->subjectChanged(subject, msg.from().resource());
 
259
                nice = true;
 
260
        }
 
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);
 
264
        }
 
265
}
 
266
 
 
267
MUCRoom::MUCRoom(Client *client, const JID &jid) :
 
268
        QObject(client),
 
269
        d_ptr(new MUCRoomPrivate(this))
 
270
{
 
271
        Q_D(MUCRoom);
 
272
        d->client = client;
 
273
        d->jid = jid;
 
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()));
 
278
}
 
279
 
 
280
MUCRoom::~MUCRoom()
 
281
{
 
282
        Q_D(MUCRoom);
 
283
        if (!d->client)
 
284
                return;
 
285
        ClientPrivate::get(d->client)->rooms.remove(d->jid.bare());
 
286
}
 
287
 
 
288
QString MUCRoom::id() const
 
289
{
 
290
        return d_func()->jid.bare();
 
291
}
 
292
 
 
293
QString MUCRoom::service() const
 
294
{
 
295
        return d_func()->jid.domain();
 
296
}
 
297
 
 
298
void MUCRoom::setPassword(const QString &password)
 
299
{
 
300
        d_func()->password = password;
 
301
}
 
302
 
 
303
bool MUCRoom::isJoined() const
 
304
{
 
305
        return d_func()->isJoined;
 
306
}
 
307
 
 
308
Presence::Type MUCRoom::presence() const
 
309
{
 
310
        return d_func()->currentPresence.subtype();
 
311
}
 
312
 
 
313
void MUCRoom::join(Presence::Type type, const QString &message, int priority)
 
314
{
 
315
        Q_D(MUCRoom);
 
316
        if (d->startedJoining)
 
317
                return;
 
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);
 
328
}
 
329
 
 
330
void MUCRoom::join()
 
331
{
 
332
        Q_D(MUCRoom);
 
333
        Presence pres = d->client->presence();
 
334
        join(pres.subtype(), pres.status(), pres.priority());
 
335
}
 
336
 
 
337
enum MUCRoomRequestContext
 
338
{
 
339
        MUCRoomRequestConfig = 100,
 
340
        MUCRoomSubmitConfig,
 
341
        MUCRoomRequestList,
 
342
        MUCRoomEndRequestList = MUCRoomRequestList + 20,
 
343
        MUCRoomSetList
 
344
};
 
345
 
 
346
void MUCRoom::requestRoomConfig()
 
347
{
 
348
        Q_D(MUCRoom);
 
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);
 
352
}
 
353
 
 
354
void MUCRoom::requestList(Jreen::MUCRoom::Affiliation affiliation)
 
355
{
 
356
        Q_D(MUCRoom);
 
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);
 
360
}
 
361
 
 
362
void MUCRoom::setList(Jreen::MUCRoom::Affiliation affiliation, const Jreen::MUCRoom::ItemList &items)
 
363
{
 
364
        Q_D(MUCRoom);
 
365
        IQ iq(IQ::Set, d->jid.bareJID());
 
366
        MUCRoomAdminQuery *query = new MUCRoomAdminQuery;
 
367
        if (items.isEmpty())
 
368
                return;
 
369
        foreach (const Item &item, items) {
 
370
                MUCRoomItem tmp;
 
371
                tmp.affiliation = affiliation;
 
372
                tmp.jid = item.jid();
 
373
                tmp.reason = item.reason();
 
374
                query->items << tmp;
 
375
        }
 
376
        iq.addExtension(query);
 
377
        d->client->send(iq, this, SLOT(handleIQ(Jreen::IQ,int)), MUCRoomSetList);
 
378
}
 
379
 
 
380
void MUCRoom::setRoomConfig(const Jreen::DataForm::Ptr &form)
 
381
{
 
382
        Q_D(MUCRoom);
 
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);
 
386
}
 
387
 
 
388
void MUCRoom::leave(const QString &message)
 
389
{
 
390
        Q_D(MUCRoom);
 
391
        if (d->currentPresence.subtype() == Presence::Unavailable)
 
392
                return;
 
393
        d->isJoined = false;
 
394
        Presence pres(Presence::Unavailable, d->jid, message);
 
395
        d->currentPresence = pres;
 
396
        d->client->send(pres);
 
397
}
 
398
 
 
399
QString MUCRoom::nick() const
 
400
{
 
401
        return d_func()->jid.resource();
 
402
}
 
403
 
 
404
JID MUCRoom::realJid(const QString &nick)
 
405
{
 
406
        MUCRoomUserQuery::Ptr query = d_func()->participantsHash.value(nick);
 
407
        return query ? query->item.jid : JID();
 
408
}
 
409
 
 
410
void MUCRoom::setNick(const QString &nick)
 
411
{
 
412
        Q_D(MUCRoom);
 
413
        if (d->isJoined) {
 
414
                JID newJid = d->jid;
 
415
                newJid.setResource(nick);
 
416
                Presence pres(d->currentPresence.subtype(), newJid,
 
417
                                          d->currentPresence.status(), d->currentPresence.priority());
 
418
                d->client->send(pres);
 
419
        } else {
 
420
                d->jid.setResource(nick);
 
421
        }
 
422
}
 
423
 
 
424
void MUCRoom::setHistoryMaxChars(int maxChars)
 
425
{
 
426
        d_func()->maxChars = maxChars;
 
427
}
 
428
 
 
429
void MUCRoom::setHistoryMaxStanzas(int maxStanzas)
 
430
{
 
431
        d_func()->maxStanzas = maxStanzas;
 
432
}
 
433
 
 
434
void MUCRoom::setHistorySeconds(int seconds)
 
435
{
 
436
        d_func()->seconds = seconds;
 
437
}
 
438
 
 
439
void MUCRoom::setHistorySince(const QDateTime &since)
 
440
{
 
441
        d_func()->since = since;
 
442
}
 
443
 
 
444
void MUCRoom::setPresence(Presence::Type type, const QString &message, int priority)
 
445
{
 
446
        Q_D(MUCRoom);
 
447
        Presence pres(type, d->jid, message, priority);
 
448
        d->client->send(pres);
 
449
}
 
450
 
 
451
void MUCRoom::invite(const JID &jid, const QString &reason, const QString &thread)
 
452
{
 
453
        Q_D(MUCRoom);
 
454
        if (!d->isJoined || !d->client)
 
455
                return;
 
456
        Message message(Message::Normal, jid);
 
457
        message.addExtension(new MUCRoomUserQuery(MUCRoomUserQuery::Invite, jid, reason, thread));
 
458
        d_func()->client->send(message);
 
459
}
 
460
 
 
461
void MUCRoom::kick(const QString &nick, const QString &reason)
 
462
{
 
463
        setRole(nick, RoleNone, reason);
 
464
}
 
465
 
 
466
void MUCRoom::ban(const QString &nick, const QString &reason)
 
467
{
 
468
        Q_D(MUCRoom);
 
469
        MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
 
470
        JID victim;
 
471
        if (!query) {
 
472
                // May be it's already full jid, who knows?
 
473
                victim = nick;
 
474
                if (victim.node().isEmpty() || victim.domain().isEmpty())
 
475
                        return;
 
476
        } else {
 
477
                victim = query->item.jid.bareJID();
 
478
        }
 
479
        setAffiliation(victim, AffiliationOutcast, reason);
 
480
}
 
481
 
 
482
void MUCRoom::setRole(const QString &nick, Role role, const QString &reason)
 
483
{
 
484
        Q_D(MUCRoom);
 
485
        IQ iq(IQ::Set, d->jid.bareJID());
 
486
        iq.addExtension(new MUCRoomAdminQuery(nick, role, reason));
 
487
        d->client->send(iq);
 
488
}
 
489
 
 
490
void MUCRoom::setAffiliation(const JID &jid, Affiliation affiliation, const QString &reason)
 
491
{
 
492
        Q_D(MUCRoom);
 
493
        IQ iq(IQ::Set, d->jid.bareJID());
 
494
        iq.addExtension(new MUCRoomAdminQuery(jid, affiliation, reason));
 
495
        d->client->send(iq);
 
496
}
 
497
 
 
498
void MUCRoom::setAffiliation(const QString &nick, Affiliation affiliation, const QString &reason)
 
499
{
 
500
        Q_D(MUCRoom);
 
501
        IQ iq(IQ::Set, d->jid.bareJID());
 
502
        iq.addExtension(new MUCRoomAdminQuery(nick, affiliation, reason));
 
503
        d->client->send(iq);
 
504
}
 
505
 
 
506
void MUCRoom::send(const QString &message)
 
507
{
 
508
        Q_D(MUCRoom);
 
509
        d->session->sendMessage(message);
 
510
}
 
511
 
 
512
QString MUCRoom::subject() const
 
513
{
 
514
        return d_func()->subject;
 
515
}
 
516
 
 
517
void MUCRoom::setSubject(const QString &subject)
 
518
{
 
519
        Q_D(MUCRoom);
 
520
        d->session->setSubject(subject);
 
521
}
 
522
 
 
523
MUCRoom::Affiliation MUCRoom::affiliation() const
 
524
{
 
525
        return d_func()->affiliation;
 
526
}
 
527
 
 
528
MUCRoom::Role MUCRoom::role() const
 
529
{
 
530
        return d_func()->role;
 
531
}
 
532
 
 
533
bool MUCRoom::canKick(const QString &nick)
 
534
{
 
535
        Q_D(MUCRoom);
 
536
        MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
 
537
        if (!query)
 
538
                return false;
 
539
        if (query->item.role == MUCRoom::RoleVisitor || query->item.role == MUCRoom::RoleParticipant)
 
540
                return checkParticipantPrivelege(KickParticipantsAndVisitors, d->role);
 
541
        return false;
 
542
}
 
543
 
 
544
bool MUCRoom::canBan(const QString &nick)
 
545
{
 
546
        Q_D(MUCRoom);
 
547
        MUCRoomUserQuery::Ptr query = d->participantsHash.value(nick);
 
548
        if (!query)
 
549
                return false;
 
550
        if (d->affiliation != MUCRoom::AffiliationAdmin && d->affiliation != MUCRoom::AffiliationOwner)
 
551
                return false;
 
552
        return query->item.affiliation <= MUCRoom::AffiliationMember;
 
553
}
 
554
 
 
555
void MUCRoom::handleIQ(const Jreen::IQ &iq, int context)
 
556
{
 
557
        if (Error::Ptr e = iq.payload<Error>()) {
 
558
                emit error(e);
 
559
                return;
 
560
        }
 
561
        if (context == MUCRoomRequestConfig) {
 
562
                MUCRoomOwnerQuery::Ptr query = iq.payload<MUCRoomOwnerQuery>();
 
563
                if (!query)
 
564
                        return;
 
565
                emit configurationReceived(query->form);
 
566
        } else if (context >= MUCRoomRequestList && context < MUCRoomEndRequestList) {
 
567
                ItemList items;
 
568
                MUCRoomAdminQuery::Ptr query = iq.payload<MUCRoomAdminQuery>();
 
569
                if (!query)
 
570
                        return;
 
571
                foreach (const MUCRoomItem &item, query->items) {
 
572
                        if (!item.jid.isValid())
 
573
                                continue;
 
574
                        items << Item(item.jid, item.reason);
 
575
                }
 
576
                Affiliation affiliation = static_cast<Affiliation>(context - MUCRoomRequestList);
 
577
                emit listReceived(affiliation, items);
 
578
        }
 
579
}
 
580
 
 
581
void MUCRoom::onConnected()
 
582
{
 
583
        Q_D(MUCRoom);
 
584
        if (d->currentPresence.subtype() != Presence::Unavailable)
 
585
                join(d->currentPresence.subtype(), d->currentPresence.status(), d->currentPresence.priority());
 
586
}
 
587
 
 
588
void MUCRoom::onDisconnected()
 
589
{
 
590
        Q_D(MUCRoom);
 
591
        d->startedJoining = false;
 
592
        if (d->currentPresence.subtype() != Presence::Unavailable) {
 
593
                d->participantsHash.clear();
 
594
                d->isJoined = false;
 
595
                emit leaved();
 
596
        }
 
597
}
 
598
}