09323067151fbfec4da59e944413a8830933c6f2
[quassel.git] / src / common / ircchannel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
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 program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "ircchannel.h"
22
23 #include "network.h"
24 #include "ircuser.h"
25 #include "util.h"
26
27 #include <QMapIterator>
28 #include <QHashIterator>
29 #include <QTextCodec>
30
31 #include <QDebug>
32
33 INIT_SYNCABLE_OBJECT(IrcChannel)
34 IrcChannel::IrcChannel(const QString &channelname, Network *network)
35     : SyncableObject(network),
36     _initialized(false),
37     _name(channelname),
38     _topic(QString()),
39     _encrypted(false),
40     _network(network),
41     _codecForEncoding(0),
42     _codecForDecoding(0)
43 {
44     setObjectName(QString::number(network->networkId().toInt()) + "/" +  channelname);
45 }
46
47
48 IrcChannel::~IrcChannel()
49 {
50 }
51
52
53 // ====================
54 //  PUBLIC:
55 // ====================
56 bool IrcChannel::isKnownUser(IrcUser *ircuser) const
57 {
58     if (ircuser == 0) {
59         qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
60         return false;
61     }
62
63     if (!_userModes.contains(ircuser)) {
64         // This can happen e.g. when disconnecting from a network, so don't log a warning
65         return false;
66     }
67
68     return true;
69 }
70
71
72 bool IrcChannel::isValidChannelUserMode(const QString &mode) const
73 {
74     bool isvalid = true;
75     if (mode.size() > 1) {
76         qWarning() << "Channel" << name() << "received Channel User Mode which is longer than 1 Char:" << mode;
77         isvalid = false;
78     }
79     return isvalid;
80 }
81
82
83 QString IrcChannel::userModes(IrcUser *ircuser) const
84 {
85     if (_userModes.contains(ircuser))
86         return _userModes[ircuser];
87     else
88         return QString();
89 }
90
91
92 QString IrcChannel::userModes(const QString &nick) const
93 {
94     return userModes(network()->ircUser(nick));
95 }
96
97
98 void IrcChannel::setCodecForEncoding(const QString &name)
99 {
100     setCodecForEncoding(QTextCodec::codecForName(name.toLatin1()));
101 }
102
103
104 void IrcChannel::setCodecForEncoding(QTextCodec *codec)
105 {
106     _codecForEncoding = codec;
107 }
108
109
110 void IrcChannel::setCodecForDecoding(const QString &name)
111 {
112     setCodecForDecoding(QTextCodec::codecForName(name.toLatin1()));
113 }
114
115
116 void IrcChannel::setCodecForDecoding(QTextCodec *codec)
117 {
118     _codecForDecoding = codec;
119 }
120
121
122 QString IrcChannel::decodeString(const QByteArray &text) const
123 {
124     if (!codecForDecoding()) return network()->decodeString(text);
125     return ::decodeString(text, _codecForDecoding);
126 }
127
128
129 QByteArray IrcChannel::encodeString(const QString &string) const
130 {
131     if (codecForEncoding()) {
132         return _codecForEncoding->fromUnicode(string);
133     }
134     return network()->encodeString(string);
135 }
136
137
138 // ====================
139 //  PUBLIC SLOTS:
140 // ====================
141 void IrcChannel::setTopic(const QString &topic)
142 {
143     _topic = topic;
144     SYNC(ARG(topic))
145     emit topicSet(topic);
146 }
147
148
149 void IrcChannel::setPassword(const QString &password)
150 {
151     _password = password;
152     SYNC(ARG(password))
153 }
154
155 void IrcChannel::setEncrypted(bool encrypted)
156 {
157     _encrypted = encrypted;
158     SYNC(ARG(encrypted))
159     emit encryptedSet(encrypted);
160 }
161
162
163 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
164 {
165     if (users.isEmpty())
166         return;
167
168     if (users.count() != modes.count()) {
169         qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
170         return;
171     }
172
173     // Sort user modes first
174     const QStringList sortedModes = network()->sortPrefixModes(modes);
175
176     QStringList newNicks;
177     QStringList newModes;
178     QList<IrcUser *> newUsers;
179
180     IrcUser *ircuser;
181     for (int i = 0; i < users.count(); i++) {
182         ircuser = users[i];
183         if (!ircuser)
184             continue;
185         if (_userModes.contains(ircuser)) {
186             if (sortedModes[i].count() > 1) {
187                 // Multiple modes received, do it one at a time
188                 // TODO Better way of syncing this without breaking protocol?
189                 for (int i_m = 0; i_m < sortedModes[i].count(); ++i_m) {
190                     addUserMode(ircuser, sortedModes[i][i_m]);
191                 }
192             } else {
193                 addUserMode(ircuser, sortedModes[i]);
194             }
195             continue;
196         }
197
198         _userModes[ircuser] = sortedModes[i];
199         ircuser->joinChannel(this, true);
200         connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
201
202         // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
203         // If you wonder why there is no counterpart to ircUserJoined:
204         // the joins are propagated by the ircuser. The signal ircUserJoined is only for convenience
205
206         newNicks << ircuser->nick();
207         newModes << sortedModes[i];
208         newUsers << ircuser;
209     }
210
211     if (newNicks.isEmpty())
212         return;
213
214     SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
215     emit ircUsersJoined(newUsers);
216 }
217
218
219 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
220 {
221     QList<IrcUser *> users;
222     foreach(QString nick, nicks)
223     users << network()->newIrcUser(nick);
224     joinIrcUsers(users, modes);
225 }
226
227
228 void IrcChannel::joinIrcUser(IrcUser *ircuser)
229 {
230     QList<IrcUser *> users;
231     users << ircuser;
232     QStringList modes;
233     modes << QString();
234     joinIrcUsers(users, modes);
235 }
236
237
238 void IrcChannel::part(IrcUser *ircuser)
239 {
240     if (isKnownUser(ircuser)) {
241         _userModes.remove(ircuser);
242         ircuser->partChannel(this);
243         // If you wonder why there is no counterpart to ircUserParted:
244         // the joins are propagted by the ircuser. The signal ircUserParted is only for convenience
245         disconnect(ircuser, 0, this, 0);
246         emit ircUserParted(ircuser);
247
248         if (network()->isMe(ircuser) || _userModes.isEmpty()) {
249             // in either case we're no longer in the channel
250             //  -> clean up the channel and destroy it
251             QList<IrcUser *> users = _userModes.keys();
252             _userModes.clear();
253             foreach(IrcUser *user, users) {
254                 disconnect(user, 0, this, 0);
255                 user->partChannel(this);
256             }
257             emit parted();
258             network()->removeIrcChannel(this);
259         }
260     }
261 }
262
263
264 void IrcChannel::part(const QString &nick)
265 {
266     part(network()->ircUser(nick));
267 }
268
269
270 // SET USER MODE
271 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
272 {
273     if (isKnownUser(ircuser)) {
274         // Keep user modes sorted
275         _userModes[ircuser] = network()->sortPrefixModes(modes);
276         QString nick = ircuser->nick();
277         SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
278         emit ircUserModesSet(ircuser, modes);
279     }
280 }
281
282
283 void IrcChannel::setUserModes(const QString &nick, const QString &modes)
284 {
285     setUserModes(network()->ircUser(nick), modes);
286 }
287
288
289 // ADD USER MODE
290 void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
291 {
292     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
293         return;
294
295     if (!_userModes[ircuser].contains(mode)) {
296         // Keep user modes sorted
297         _userModes[ircuser] = network()->sortPrefixModes(_userModes[ircuser] + mode);
298         QString nick = ircuser->nick();
299         SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
300         emit ircUserModeAdded(ircuser, mode);
301     }
302 }
303
304
305 void IrcChannel::addUserMode(const QString &nick, const QString &mode)
306 {
307     addUserMode(network()->ircUser(nick), mode);
308 }
309
310
311 // REMOVE USER MODE
312 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
313 {
314     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
315         return;
316
317     if (_userModes[ircuser].contains(mode)) {
318         // Removing modes shouldn't mess up ordering
319         _userModes[ircuser].remove(mode);
320         QString nick = ircuser->nick();
321         SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
322         emit ircUserModeRemoved(ircuser, mode);
323     }
324 }
325
326
327 void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
328 {
329     removeUserMode(network()->ircUser(nick), mode);
330 }
331
332
333 // INIT SET USER MODES
334 QVariantMap IrcChannel::initUserModes() const
335 {
336     QVariantMap usermodes;
337     QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
338     while (iter != _userModes.constEnd()) {
339         usermodes[iter.key()->nick()] = iter.value();
340         ++iter;
341     }
342     return usermodes;
343 }
344
345
346 void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
347 {
348     QList<IrcUser *> users;
349     QStringList modes;
350     QVariantMap::const_iterator iter = usermodes.constBegin();
351     while (iter != usermodes.constEnd()) {
352         users << network()->newIrcUser(iter.key());
353         modes << iter.value().toString();
354         ++iter;
355     }
356     // joinIrcUsers handles sorting modes
357     joinIrcUsers(users, modes);
358 }
359
360
361 QVariantMap IrcChannel::initChanModes() const
362 {
363     QVariantMap channelModes;
364
365     QVariantMap A_modes;
366     QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
367     while (A_iter != _A_channelModes.constEnd()) {
368         A_modes[A_iter.key()] = A_iter.value();
369         ++A_iter;
370     }
371     channelModes["A"] = A_modes;
372
373     QVariantMap B_modes;
374     QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
375     while (B_iter != _B_channelModes.constEnd()) {
376         B_modes[B_iter.key()] = B_iter.value();
377         ++B_iter;
378     }
379     channelModes["B"] = B_modes;
380
381     QVariantMap C_modes;
382     QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
383     while (C_iter != _C_channelModes.constEnd()) {
384         C_modes[C_iter.key()] = C_iter.value();
385         ++C_iter;
386     }
387     channelModes["C"] = C_modes;
388
389     QString D_modes;
390     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
391     while (D_iter != _D_channelModes.constEnd()) {
392         D_modes += *D_iter;
393         ++D_iter;
394     }
395     channelModes["D"] = D_modes;
396
397     return channelModes;
398 }
399
400
401 void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
402 {
403     QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
404     QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
405     while (iter != iterEnd) {
406         _A_channelModes[iter.key()[0]] = iter.value().toStringList();
407         ++iter;
408     }
409
410     iter = channelModes["B"].toMap().constBegin();
411     iterEnd = channelModes["B"].toMap().constEnd();
412     while (iter != iterEnd) {
413         _B_channelModes[iter.key()[0]] = iter.value().toString();
414         ++iter;
415     }
416
417     iter = channelModes["C"].toMap().constBegin();
418     iterEnd = channelModes["C"].toMap().constEnd();
419     while (iter != iterEnd) {
420         _C_channelModes[iter.key()[0]] = iter.value().toString();
421         ++iter;
422     }
423
424     QString D_modes = channelModes["D"].toString();
425     for (int i = 0; i < D_modes.count(); i++) {
426         _D_channelModes << D_modes[i];
427     }
428 }
429
430
431 void IrcChannel::ircUserDestroyed()
432 {
433     IrcUser *ircUser = static_cast<IrcUser *>(sender());
434     Q_ASSERT(ircUser);
435     _userModes.remove(ircUser);
436     // no further propagation.
437     // this leads only to fuck ups.
438 }
439
440
441 void IrcChannel::ircUserNickSet(QString nick)
442 {
443     IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
444     Q_ASSERT(ircUser);
445     emit ircUserNickSet(ircUser, nick);
446 }
447
448
449 /*******************************************************************************
450  *
451  * 3.3 CHANMODES
452  *
453  *    o  CHANMODES=A,B,C,D
454  *
455  *    The CHANMODES token specifies the modes that may be set on a channel.
456  *    These modes are split into four categories, as follows:
457  *
458  *    o  Type A: Modes that add or remove an address to or from a list.
459  *       These modes always take a parameter when sent by the server to a
460  *       client; when sent by a client, they may be specified without a
461  *       parameter, which requests the server to display the current
462  *       contents of the corresponding list on the channel to the client.
463  *    o  Type B: Modes that change a setting on the channel.  These modes
464  *       always take a parameter.
465  *    o  Type C: Modes that change a setting on the channel. These modes
466  *       take a parameter only when set; the parameter is absent when the
467  *       mode is removed both in the client's and server's MODE command.
468  *    o  Type D: Modes that change a setting on the channel. These modes
469  *       never take a parameter.
470  *
471  *    If the server sends any additional types after these 4, the client
472  *    MUST ignore them; this is intended to allow future extension of this
473  *    token.
474  *
475  *    The IRC server MUST NOT list modes in CHANMODES which are also
476  *    present in the PREFIX parameter; however, for completeness, modes
477  *    described in PREFIX may be treated as type B modes.
478  *
479  ******************************************************************************/
480
481 /*******************************************************************************
482  * Short Version:
483  * A --> add/remove from List
484  * B --> set value or remove
485  * C --> set value or remove
486  * D --> on/off
487  *
488  * B and C behave very similar... we store the data in different datastructures
489  * for future compatibility
490  ******************************************************************************/
491
492 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
493 // see list above for chanmode types
494 void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
495 {
496     Network::ChannelModeType modeType = network()->channelModeType(mode);
497
498     switch (modeType) {
499     case Network::NOT_A_CHANMODE:
500         return;
501     case Network::A_CHANMODE:
502         if (!_A_channelModes.contains(mode))
503             _A_channelModes[mode] = QStringList(value);
504         else if (!_A_channelModes[mode].contains(value))
505             _A_channelModes[mode] << value;
506         break;
507
508     case Network::B_CHANMODE:
509         _B_channelModes[mode] = value;
510         break;
511
512     case Network::C_CHANMODE:
513         _C_channelModes[mode] = value;
514         break;
515
516     case Network::D_CHANMODE:
517         _D_channelModes << mode;
518         break;
519     }
520     SYNC(ARG(mode), ARG(value))
521 }
522
523
524 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
525 {
526     Network::ChannelModeType modeType = network()->channelModeType(mode);
527
528     switch (modeType) {
529     case Network::NOT_A_CHANMODE:
530         return;
531     case Network::A_CHANMODE:
532         if (_A_channelModes.contains(mode))
533             _A_channelModes[mode].removeAll(value);
534         break;
535
536     case Network::B_CHANMODE:
537         _B_channelModes.remove(mode);
538         break;
539
540     case Network::C_CHANMODE:
541         _C_channelModes.remove(mode);
542         break;
543
544     case Network::D_CHANMODE:
545         _D_channelModes.remove(mode);
546         break;
547     }
548     SYNC(ARG(mode), ARG(value))
549 }
550
551
552 bool IrcChannel::hasMode(const QChar &mode) const
553 {
554     Network::ChannelModeType modeType = network()->channelModeType(mode);
555
556     switch (modeType) {
557     case Network::NOT_A_CHANMODE:
558         return false;
559     case Network::A_CHANMODE:
560         return _A_channelModes.contains(mode);
561     case Network::B_CHANMODE:
562         return _B_channelModes.contains(mode);
563     case Network::C_CHANMODE:
564         return _C_channelModes.contains(mode);
565     case Network::D_CHANMODE:
566         return _D_channelModes.contains(mode);
567     }
568     return false;
569 }
570
571
572 QString IrcChannel::modeValue(const QChar &mode) const
573 {
574     Network::ChannelModeType modeType = network()->channelModeType(mode);
575
576     switch (modeType) {
577     case Network::B_CHANMODE:
578         if (_B_channelModes.contains(mode))
579             return _B_channelModes[mode];
580         else
581             return QString();
582     case Network::C_CHANMODE:
583         if (_C_channelModes.contains(mode))
584             return _C_channelModes[mode];
585         else
586             return QString();
587     default:
588         return QString();
589     }
590 }
591
592
593 QStringList IrcChannel::modeValueList(const QChar &mode) const
594 {
595     Network::ChannelModeType modeType = network()->channelModeType(mode);
596
597     switch (modeType) {
598     case Network::A_CHANMODE:
599         if (_A_channelModes.contains(mode))
600             return _A_channelModes[mode];
601         break;
602     default:
603         ;
604     }
605     return {};
606 }
607
608
609 QString IrcChannel::channelModeString() const
610 {
611     QStringList params;
612     QString modeString;
613
614     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
615     while (D_iter != _D_channelModes.constEnd()) {
616         modeString += *D_iter;
617         ++D_iter;
618     }
619
620     QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
621     while (BC_iter != _C_channelModes.constEnd()) {
622         modeString += BC_iter.key();
623         params << BC_iter.value();
624         ++BC_iter;
625     }
626
627     BC_iter = _B_channelModes.constBegin();
628     while (BC_iter != _B_channelModes.constEnd()) {
629         modeString += BC_iter.key();
630         params << BC_iter.value();
631         ++BC_iter;
632     }
633     if (modeString.isEmpty())
634         return modeString;
635     else
636         return QString("+%1 %2").arg(modeString).arg(params.join(" "));
637 }