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