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