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