1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "ircchannel.h"
24 #include <QHashIterator>
25 #include <QMapIterator>
32 IrcChannel::IrcChannel(const QString& channelname, Network* network)
33 : SyncableObject(network)
39 , _codecForEncoding(nullptr)
40 , _codecForDecoding(nullptr)
42 setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname);
45 // ====================
47 // ====================
48 bool IrcChannel::isKnownUser(IrcUser* ircuser) const
50 if (ircuser == nullptr) {
51 qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
55 if (!_userModes.contains(ircuser)) {
56 // This can happen e.g. when disconnecting from a network, so don't log a warning
63 bool IrcChannel::isValidChannelUserMode(const QString& mode) const
66 if (mode.size() > 1) {
67 qWarning() << "Channel" << name() << "received Channel User Mode which is longer than 1 Char:" << mode;
73 QString IrcChannel::userModes(IrcUser* ircuser) const
75 if (_userModes.contains(ircuser))
76 return _userModes[ircuser];
81 QString IrcChannel::userModes(const QString& nick) const
83 return userModes(network()->ircUser(nick));
86 void IrcChannel::setCodecForEncoding(const QString& name)
88 setCodecForEncoding(QTextCodec::codecForName(name.toLatin1()));
91 void IrcChannel::setCodecForEncoding(QTextCodec* codec)
93 _codecForEncoding = codec;
96 void IrcChannel::setCodecForDecoding(const QString& name)
98 setCodecForDecoding(QTextCodec::codecForName(name.toLatin1()));
101 void IrcChannel::setCodecForDecoding(QTextCodec* codec)
103 _codecForDecoding = codec;
106 QString IrcChannel::decodeString(const QByteArray& text) const
108 if (!codecForDecoding())
109 return network()->decodeString(text);
110 return ::decodeString(text, _codecForDecoding);
113 QByteArray IrcChannel::encodeString(const QString& string) const
115 if (codecForEncoding()) {
116 return _codecForEncoding->fromUnicode(string);
118 return network()->encodeString(string);
121 // ====================
123 // ====================
124 void IrcChannel::setTopic(const QString& topic)
128 emit topicSet(topic);
131 void IrcChannel::setPassword(const QString& password)
133 _password = password;
137 void IrcChannel::setEncrypted(bool encrypted)
139 _encrypted = encrypted;
141 emit encryptedSet(encrypted);
144 void IrcChannel::joinIrcUsers(const QList<IrcUser*>& users, const QStringList& modes)
149 if (users.count() != modes.count()) {
150 qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
154 // Sort user modes first
155 const QStringList sortedModes = network()->sortPrefixModes(modes);
157 QStringList newNicks;
158 QStringList newModes;
159 QList<IrcUser*> newUsers;
162 for (int i = 0; i < users.count(); i++) {
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]);
175 addUserMode(ircuser, sortedModes[i]);
180 _userModes[ircuser] = sortedModes[i];
181 ircuser->joinChannel(this, true);
182 connect(ircuser, &IrcUser::nickSet, this, selectOverload<QString>(&IrcChannel::ircUserNickSet));
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
188 newNicks << ircuser->nick();
189 newModes << sortedModes[i];
193 if (newNicks.isEmpty())
196 SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
197 emit ircUsersJoined(newUsers);
200 void IrcChannel::joinIrcUsers(const QStringList& nicks, const QStringList& modes)
202 QList<IrcUser*> users;
203 foreach (QString nick, nicks)
204 users << network()->newIrcUser(nick);
205 joinIrcUsers(users, modes);
208 void IrcChannel::joinIrcUser(IrcUser* ircuser)
210 QList<IrcUser*> users;
214 joinIrcUsers(users, modes);
217 void IrcChannel::part(IrcUser* ircuser)
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);
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();
232 foreach (IrcUser* user, users) {
233 disconnect(user, nullptr, this, nullptr);
234 user->partChannelInternal(this, true);
237 network()->removeIrcChannel(this);
242 void IrcChannel::part(const QString& nick)
244 part(network()->ircUser(nick));
248 void IrcChannel::setUserModes(IrcUser* ircuser, const QString& modes)
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);
259 void IrcChannel::setUserModes(const QString& nick, const QString& modes)
261 setUserModes(network()->ircUser(nick), modes);
265 void IrcChannel::addUserMode(IrcUser* ircuser, const QString& mode)
267 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
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);
279 void IrcChannel::addUserMode(const QString& nick, const QString& mode)
281 addUserMode(network()->ircUser(nick), mode);
285 void IrcChannel::removeUserMode(IrcUser* ircuser, const QString& mode)
287 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
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);
299 void IrcChannel::removeUserMode(const QString& nick, const QString& mode)
301 removeUserMode(network()->ircUser(nick), mode);
304 // INIT SET USER MODES
305 QVariantMap IrcChannel::initUserModes() const
307 QVariantMap usermodes;
308 QHash<IrcUser*, QString>::const_iterator iter = _userModes.constBegin();
309 while (iter != _userModes.constEnd()) {
310 usermodes[iter.key()->nick()] = iter.value();
316 void IrcChannel::initSetUserModes(const QVariantMap& usermodes)
318 QList<IrcUser*> users;
320 QVariantMap::const_iterator iter = usermodes.constBegin();
321 while (iter != usermodes.constEnd()) {
322 users << network()->newIrcUser(iter.key());
323 modes << iter.value().toString();
326 // joinIrcUsers handles sorting modes
327 joinIrcUsers(users, modes);
330 QVariantMap IrcChannel::initChanModes() const
332 QVariantMap channelModes;
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();
340 channelModes["A"] = A_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();
348 channelModes["B"] = B_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();
356 channelModes["C"] = C_modes;
359 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
360 while (D_iter != _D_channelModes.constEnd()) {
364 channelModes["D"] = D_modes;
369 void IrcChannel::initSetChanModes(const QVariantMap& channelModes)
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();
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();
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();
392 QString D_modes = channelModes["D"].toString();
393 for (int i = 0; i < D_modes.count(); i++) {
394 _D_channelModes << D_modes[i];
398 void IrcChannel::ircUserDestroyed()
400 auto* ircUser = static_cast<IrcUser*>(sender());
402 _userModes.remove(ircUser);
403 // no further propagation.
404 // this leads only to fuck ups.
407 void IrcChannel::ircUserNickSet(QString nick)
409 auto* ircUser = qobject_cast<IrcUser*>(sender());
411 emit ircUserNickSet(ircUser, nick);
414 /*******************************************************************************
418 * o CHANMODES=A,B,C,D
420 * The CHANMODES token specifies the modes that may be set on a channel.
421 * These modes are split into four categories, as follows:
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.
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
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.
444 ******************************************************************************/
446 /*******************************************************************************
448 * A --> add/remove from List
449 * B --> set value or remove
450 * C --> set value or remove
453 * B and C behave very similar... we store the data in different datastructures
454 * for future compatibility
455 ******************************************************************************/
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)
461 Network::ChannelModeType modeType = network()->channelModeType(mode);
464 case Network::NOT_A_CHANMODE:
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;
473 case Network::B_CHANMODE:
474 _B_channelModes[mode] = value;
477 case Network::C_CHANMODE:
478 _C_channelModes[mode] = value;
481 case Network::D_CHANMODE:
482 _D_channelModes << mode;
485 SYNC(ARG(mode), ARG(value))
488 void IrcChannel::removeChannelMode(const QChar& mode, const QString& value)
490 Network::ChannelModeType modeType = network()->channelModeType(mode);
493 case Network::NOT_A_CHANMODE:
495 case Network::A_CHANMODE:
496 if (_A_channelModes.contains(mode))
497 _A_channelModes[mode].removeAll(value);
500 case Network::B_CHANMODE:
501 _B_channelModes.remove(mode);
504 case Network::C_CHANMODE:
505 _C_channelModes.remove(mode);
508 case Network::D_CHANMODE:
509 _D_channelModes.remove(mode);
512 SYNC(ARG(mode), ARG(value))
515 bool IrcChannel::hasMode(const QChar& mode) const
517 Network::ChannelModeType modeType = network()->channelModeType(mode);
520 case Network::NOT_A_CHANMODE:
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);
534 QString IrcChannel::modeValue(const QChar& mode) const
536 Network::ChannelModeType modeType = network()->channelModeType(mode);
539 case Network::B_CHANMODE:
540 if (_B_channelModes.contains(mode))
541 return _B_channelModes[mode];
544 case Network::C_CHANMODE:
545 if (_C_channelModes.contains(mode))
546 return _C_channelModes[mode];
554 QStringList IrcChannel::modeValueList(const QChar& mode) const
556 Network::ChannelModeType modeType = network()->channelModeType(mode);
559 case Network::A_CHANMODE:
560 if (_A_channelModes.contains(mode))
561 return _A_channelModes[mode];
569 QString IrcChannel::channelModeString() const
574 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
575 while (D_iter != _D_channelModes.constEnd()) {
576 modeString += *D_iter;
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();
587 BC_iter = _B_channelModes.constBegin();
588 while (BC_iter != _B_channelModes.constEnd()) {
589 modeString += BC_iter.key();
590 params << BC_iter.value();
593 if (modeString.isEmpty())
596 return QString("+%1 %2").arg(modeString).arg(params.join(" "));