1 /***************************************************************************
2 * Copyright (C) 2005-2018 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"
27 #include <QMapIterator>
28 #include <QHashIterator>
33 IrcChannel::IrcChannel(const QString &channelname, Network *network)
34 : SyncableObject(network),
40 _codecForEncoding(nullptr),
41 _codecForDecoding(nullptr)
43 setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname);
47 // ====================
49 // ====================
50 bool IrcChannel::isKnownUser(IrcUser *ircuser) const
52 if (ircuser == nullptr) {
53 qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
57 if (!_userModes.contains(ircuser)) {
58 // This can happen e.g. when disconnecting from a network, so don't log a warning
66 bool IrcChannel::isValidChannelUserMode(const QString &mode) const
69 if (mode.size() > 1) {
70 qWarning() << "Channel" << name() << "received Channel User Mode which is longer than 1 Char:" << mode;
77 QString IrcChannel::userModes(IrcUser *ircuser) const
79 if (_userModes.contains(ircuser))
80 return _userModes[ircuser];
86 QString IrcChannel::userModes(const QString &nick) const
88 return userModes(network()->ircUser(nick));
92 void IrcChannel::setCodecForEncoding(const QString &name)
94 setCodecForEncoding(QTextCodec::codecForName(name.toLatin1()));
98 void IrcChannel::setCodecForEncoding(QTextCodec *codec)
100 _codecForEncoding = codec;
104 void IrcChannel::setCodecForDecoding(const QString &name)
106 setCodecForDecoding(QTextCodec::codecForName(name.toLatin1()));
110 void IrcChannel::setCodecForDecoding(QTextCodec *codec)
112 _codecForDecoding = codec;
116 QString IrcChannel::decodeString(const QByteArray &text) const
118 if (!codecForDecoding()) return network()->decodeString(text);
119 return ::decodeString(text, _codecForDecoding);
123 QByteArray IrcChannel::encodeString(const QString &string) const
125 if (codecForEncoding()) {
126 return _codecForEncoding->fromUnicode(string);
128 return network()->encodeString(string);
132 // ====================
134 // ====================
135 void IrcChannel::setTopic(const QString &topic)
139 emit topicSet(topic);
143 void IrcChannel::setPassword(const QString &password)
145 _password = password;
149 void IrcChannel::setEncrypted(bool encrypted)
151 _encrypted = encrypted;
153 emit encryptedSet(encrypted);
157 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
162 if (users.count() != modes.count()) {
163 qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
167 // Sort user modes first
168 const QStringList sortedModes = network()->sortPrefixModes(modes);
170 QStringList newNicks;
171 QStringList newModes;
172 QList<IrcUser *> newUsers;
175 for (int i = 0; i < users.count(); i++) {
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]);
187 addUserMode(ircuser, sortedModes[i]);
192 _userModes[ircuser] = sortedModes[i];
193 ircuser->joinChannel(this, true);
194 connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
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
200 newNicks << ircuser->nick();
201 newModes << sortedModes[i];
205 if (newNicks.isEmpty())
208 SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
209 emit ircUsersJoined(newUsers);
213 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
215 QList<IrcUser *> users;
216 foreach(QString nick, nicks)
217 users << network()->newIrcUser(nick);
218 joinIrcUsers(users, modes);
222 void IrcChannel::joinIrcUser(IrcUser *ircuser)
224 QList<IrcUser *> users;
228 joinIrcUsers(users, modes);
232 void IrcChannel::part(IrcUser *ircuser)
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);
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();
247 foreach(IrcUser *user, users) {
248 disconnect(user, nullptr, this, nullptr);
249 user->partChannel(this);
252 network()->removeIrcChannel(this);
258 void IrcChannel::part(const QString &nick)
260 part(network()->ircUser(nick));
265 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
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);
277 void IrcChannel::setUserModes(const QString &nick, const QString &modes)
279 setUserModes(network()->ircUser(nick), modes);
284 void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
286 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
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);
299 void IrcChannel::addUserMode(const QString &nick, const QString &mode)
301 addUserMode(network()->ircUser(nick), mode);
306 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
308 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
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);
321 void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
323 removeUserMode(network()->ircUser(nick), mode);
327 // INIT SET USER MODES
328 QVariantMap IrcChannel::initUserModes() const
330 QVariantMap usermodes;
331 QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
332 while (iter != _userModes.constEnd()) {
333 usermodes[iter.key()->nick()] = iter.value();
340 void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
342 QList<IrcUser *> users;
344 QVariantMap::const_iterator iter = usermodes.constBegin();
345 while (iter != usermodes.constEnd()) {
346 users << network()->newIrcUser(iter.key());
347 modes << iter.value().toString();
350 // joinIrcUsers handles sorting modes
351 joinIrcUsers(users, modes);
355 QVariantMap IrcChannel::initChanModes() const
357 QVariantMap channelModes;
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();
365 channelModes["A"] = A_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();
373 channelModes["B"] = B_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();
381 channelModes["C"] = C_modes;
384 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
385 while (D_iter != _D_channelModes.constEnd()) {
389 channelModes["D"] = D_modes;
395 void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
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();
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();
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();
418 QString D_modes = channelModes["D"].toString();
419 for (int i = 0; i < D_modes.count(); i++) {
420 _D_channelModes << D_modes[i];
425 void IrcChannel::ircUserDestroyed()
427 auto *ircUser = static_cast<IrcUser *>(sender());
429 _userModes.remove(ircUser);
430 // no further propagation.
431 // this leads only to fuck ups.
435 void IrcChannel::ircUserNickSet(QString nick)
437 auto *ircUser = qobject_cast<IrcUser *>(sender());
439 emit ircUserNickSet(ircUser, nick);
443 /*******************************************************************************
447 * o CHANMODES=A,B,C,D
449 * The CHANMODES token specifies the modes that may be set on a channel.
450 * These modes are split into four categories, as follows:
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.
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
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.
473 ******************************************************************************/
475 /*******************************************************************************
477 * A --> add/remove from List
478 * B --> set value or remove
479 * C --> set value or remove
482 * B and C behave very similar... we store the data in different datastructures
483 * for future compatibility
484 ******************************************************************************/
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)
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] = QStringList(value);
498 else if (!_A_channelModes[mode].contains(value))
499 _A_channelModes[mode] << value;
502 case Network::B_CHANMODE:
503 _B_channelModes[mode] = value;
506 case Network::C_CHANMODE:
507 _C_channelModes[mode] = value;
510 case Network::D_CHANMODE:
511 _D_channelModes << mode;
514 SYNC(ARG(mode), ARG(value))
518 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
520 Network::ChannelModeType modeType = network()->channelModeType(mode);
523 case Network::NOT_A_CHANMODE:
525 case Network::A_CHANMODE:
526 if (_A_channelModes.contains(mode))
527 _A_channelModes[mode].removeAll(value);
530 case Network::B_CHANMODE:
531 _B_channelModes.remove(mode);
534 case Network::C_CHANMODE:
535 _C_channelModes.remove(mode);
538 case Network::D_CHANMODE:
539 _D_channelModes.remove(mode);
542 SYNC(ARG(mode), ARG(value))
546 bool IrcChannel::hasMode(const QChar &mode) const
548 Network::ChannelModeType modeType = network()->channelModeType(mode);
551 case Network::NOT_A_CHANMODE:
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);
566 QString IrcChannel::modeValue(const QChar &mode) const
568 Network::ChannelModeType modeType = network()->channelModeType(mode);
571 case Network::B_CHANMODE:
572 if (_B_channelModes.contains(mode))
573 return _B_channelModes[mode];
576 case Network::C_CHANMODE:
577 if (_C_channelModes.contains(mode))
578 return _C_channelModes[mode];
587 QStringList IrcChannel::modeValueList(const QChar &mode) const
589 Network::ChannelModeType modeType = network()->channelModeType(mode);
592 case Network::A_CHANMODE:
593 if (_A_channelModes.contains(mode))
594 return _A_channelModes[mode];
603 QString IrcChannel::channelModeString() const
608 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
609 while (D_iter != _D_channelModes.constEnd()) {
610 modeString += *D_iter;
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();
621 BC_iter = _B_channelModes.constBegin();
622 while (BC_iter != _B_channelModes.constEnd()) {
623 modeString += BC_iter.key();
624 params << BC_iter.value();
627 if (modeString.isEmpty())
630 return QString("+%1 %2").arg(modeString).arg(params.join(" "));