1 /***************************************************************************
2 * Copyright (C) 2005-2016 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 INIT_SYNCABLE_OBJECT(IrcChannel)
34 IrcChannel::IrcChannel(const QString &channelname, Network *network)
35 : SyncableObject(network),
44 setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname);
48 IrcChannel::~IrcChannel()
53 // ====================
55 // ====================
56 bool IrcChannel::isKnownUser(IrcUser *ircuser) const
59 qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
63 if (!_userModes.contains(ircuser)) {
64 qWarning() << "Channel" << name() << "received data for unknown User" << ircuser->nick();
72 bool IrcChannel::isValidChannelUserMode(const QString &mode) const
75 if (mode.size() > 1) {
76 qWarning() << "Channel" << name() << "received Channel User Mode which is longer than 1 Char:" << mode;
83 QString IrcChannel::userModes(IrcUser *ircuser) const
85 if (_userModes.contains(ircuser))
86 return _userModes[ircuser];
92 QString IrcChannel::userModes(const QString &nick) const
94 return userModes(network()->ircUser(nick));
98 void IrcChannel::setCodecForEncoding(const QString &name)
100 setCodecForEncoding(QTextCodec::codecForName(name.toLatin1()));
104 void IrcChannel::setCodecForEncoding(QTextCodec *codec)
106 _codecForEncoding = codec;
110 void IrcChannel::setCodecForDecoding(const QString &name)
112 setCodecForDecoding(QTextCodec::codecForName(name.toLatin1()));
116 void IrcChannel::setCodecForDecoding(QTextCodec *codec)
118 _codecForDecoding = codec;
122 QString IrcChannel::decodeString(const QByteArray &text) const
124 if (!codecForDecoding()) return network()->decodeString(text);
125 return ::decodeString(text, _codecForDecoding);
129 QByteArray IrcChannel::encodeString(const QString &string) const
131 if (codecForEncoding()) {
132 return _codecForEncoding->fromUnicode(string);
134 return network()->encodeString(string);
138 // ====================
140 // ====================
141 void IrcChannel::setTopic(const QString &topic)
145 emit topicSet(topic);
149 void IrcChannel::setPassword(const QString &password)
151 _password = password;
155 void IrcChannel::setEncrypted(bool encrypted)
157 _encrypted = encrypted;
159 emit encryptedSet(encrypted);
163 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
168 if (users.count() != modes.count()) {
169 qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
173 QStringList newNicks;
174 QStringList newModes;
175 QList<IrcUser *> newUsers;
178 for (int i = 0; i < users.count(); i++) {
180 if (!ircuser || _userModes.contains(ircuser)) {
181 if (modes[i].count() > 1) {
182 // Multiple modes received, do it one at a time
183 // TODO Better way of syncing this without breaking protocol?
184 for (int i_m = 0; i_m < modes[i].count(); ++i_m) {
185 addUserMode(ircuser, modes[i][i_m]);
188 addUserMode(ircuser, modes[i]);
193 _userModes[ircuser] = modes[i];
194 ircuser->joinChannel(this, true);
195 connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
197 // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
198 // If you wonder why there is no counterpart to ircUserJoined:
199 // the joins are propagated by the ircuser. The signal ircUserJoined is only for convenience
201 newNicks << ircuser->nick();
202 newModes << modes[i];
206 if (newNicks.isEmpty())
209 SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
210 emit ircUsersJoined(newUsers);
214 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
216 QList<IrcUser *> users;
217 foreach(QString nick, nicks)
218 users << network()->newIrcUser(nick);
219 joinIrcUsers(users, modes);
223 void IrcChannel::joinIrcUser(IrcUser *ircuser)
225 QList<IrcUser *> users;
229 joinIrcUsers(users, modes);
233 void IrcChannel::part(IrcUser *ircuser)
235 if (isKnownUser(ircuser)) {
236 _userModes.remove(ircuser);
237 ircuser->partChannel(this);
238 // If you wonder why there is no counterpart to ircUserParted:
239 // the joins are propagted by the ircuser. The signal ircUserParted is only for convenience
240 disconnect(ircuser, 0, this, 0);
241 emit ircUserParted(ircuser);
243 if (network()->isMe(ircuser) || _userModes.isEmpty()) {
244 // in either case we're no longer in the channel
245 // -> clean up the channel and destroy it
246 QList<IrcUser *> users = _userModes.keys();
248 foreach(IrcUser *user, users) {
249 disconnect(user, 0, this, 0);
250 user->partChannel(this);
253 network()->removeIrcChannel(this);
259 void IrcChannel::part(const QString &nick)
261 part(network()->ircUser(nick));
266 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
268 if (isKnownUser(ircuser)) {
269 _userModes[ircuser] = 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 _userModes[ircuser] += mode;
291 QString nick = ircuser->nick();
292 SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
293 emit ircUserModeAdded(ircuser, mode);
298 void IrcChannel::addUserMode(const QString &nick, const QString &mode)
300 addUserMode(network()->ircUser(nick), mode);
305 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
307 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
310 if (_userModes[ircuser].contains(mode)) {
311 _userModes[ircuser].remove(mode);
312 QString nick = ircuser->nick();
313 SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
314 emit ircUserModeRemoved(ircuser, mode);
319 void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
321 removeUserMode(network()->ircUser(nick), mode);
325 // INIT SET USER MODES
326 QVariantMap IrcChannel::initUserModes() const
328 QVariantMap usermodes;
329 QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
330 while (iter != _userModes.constEnd()) {
331 usermodes[iter.key()->nick()] = iter.value();
338 void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
340 QList<IrcUser *> users;
342 QVariantMap::const_iterator iter = usermodes.constBegin();
343 while (iter != usermodes.constEnd()) {
344 users << network()->newIrcUser(iter.key());
345 modes << iter.value().toString();
348 joinIrcUsers(users, modes);
352 QVariantMap IrcChannel::initChanModes() const
354 QVariantMap channelModes;
357 QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
358 while (A_iter != _A_channelModes.constEnd()) {
359 A_modes[A_iter.key()] = A_iter.value();
362 channelModes["A"] = A_modes;
365 QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
366 while (B_iter != _B_channelModes.constEnd()) {
367 B_modes[B_iter.key()] = B_iter.value();
370 channelModes["B"] = B_modes;
373 QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
374 while (C_iter != _C_channelModes.constEnd()) {
375 C_modes[C_iter.key()] = C_iter.value();
378 channelModes["C"] = C_modes;
381 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
382 while (D_iter != _D_channelModes.constEnd()) {
386 channelModes["D"] = D_modes;
392 void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
394 QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
395 QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
396 while (iter != iterEnd) {
397 _A_channelModes[iter.key()[0]] = iter.value().toStringList();
401 iter = channelModes["B"].toMap().constBegin();
402 iterEnd = channelModes["B"].toMap().constEnd();
403 while (iter != iterEnd) {
404 _B_channelModes[iter.key()[0]] = iter.value().toString();
408 iter = channelModes["C"].toMap().constBegin();
409 iterEnd = channelModes["C"].toMap().constEnd();
410 while (iter != iterEnd) {
411 _C_channelModes[iter.key()[0]] = iter.value().toString();
415 QString D_modes = channelModes["D"].toString();
416 for (int i = 0; i < D_modes.count(); i++) {
417 _D_channelModes << D_modes[i];
422 void IrcChannel::ircUserDestroyed()
424 IrcUser *ircUser = static_cast<IrcUser *>(sender());
426 _userModes.remove(ircUser);
427 // no further propagation.
428 // this leads only to fuck ups.
432 void IrcChannel::ircUserNickSet(QString nick)
434 IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
436 emit ircUserNickSet(ircUser, nick);
440 /*******************************************************************************
444 * o CHANMODES=A,B,C,D
446 * The CHANMODES token specifies the modes that may be set on a channel.
447 * These modes are split into four categories, as follows:
449 * o Type A: Modes that add or remove an address to or from a list.
450 * These modes always take a parameter when sent by the server to a
451 * client; when sent by a client, they may be specified without a
452 * parameter, which requests the server to display the current
453 * contents of the corresponding list on the channel to the client.
454 * o Type B: Modes that change a setting on the channel. These modes
455 * always take a parameter.
456 * o Type C: Modes that change a setting on the channel. These modes
457 * take a parameter only when set; the parameter is absent when the
458 * mode is removed both in the client's and server's MODE command.
459 * o Type D: Modes that change a setting on the channel. These modes
460 * never take a parameter.
462 * If the server sends any additional types after these 4, the client
463 * MUST ignore them; this is intended to allow future extension of this
466 * The IRC server MUST NOT list modes in CHANMODES which are also
467 * present in the PREFIX parameter; however, for completeness, modes
468 * described in PREFIX may be treated as type B modes.
470 ******************************************************************************/
472 /*******************************************************************************
474 * A --> add/remove from List
475 * B --> set value or remove
476 * C --> set value or remove
479 * B and C behave very similar... we store the data in different datastructures
480 * for future compatibility
481 ******************************************************************************/
483 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
484 // see list above for chanmode types
485 void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
487 Network::ChannelModeType modeType = network()->channelModeType(mode);
490 case Network::NOT_A_CHANMODE:
492 case Network::A_CHANMODE:
493 if (!_A_channelModes.contains(mode))
494 _A_channelModes[mode] = QStringList(value);
495 else if (!_A_channelModes[mode].contains(value))
496 _A_channelModes[mode] << value;
499 case Network::B_CHANMODE:
500 _B_channelModes[mode] = value;
503 case Network::C_CHANMODE:
504 _C_channelModes[mode] = value;
507 case Network::D_CHANMODE:
508 _D_channelModes << mode;
511 SYNC(ARG(mode), ARG(value))
515 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
517 Network::ChannelModeType modeType = network()->channelModeType(mode);
520 case Network::NOT_A_CHANMODE:
522 case Network::A_CHANMODE:
523 if (_A_channelModes.contains(mode))
524 _A_channelModes[mode].removeAll(value);
527 case Network::B_CHANMODE:
528 _B_channelModes.remove(mode);
531 case Network::C_CHANMODE:
532 _C_channelModes.remove(mode);
535 case Network::D_CHANMODE:
536 _D_channelModes.remove(mode);
539 SYNC(ARG(mode), ARG(value))
543 bool IrcChannel::hasMode(const QChar &mode) const
545 Network::ChannelModeType modeType = network()->channelModeType(mode);
548 case Network::NOT_A_CHANMODE:
550 case Network::A_CHANMODE:
551 return _A_channelModes.contains(mode);
552 case Network::B_CHANMODE:
553 return _B_channelModes.contains(mode);
554 case Network::C_CHANMODE:
555 return _C_channelModes.contains(mode);
556 case Network::D_CHANMODE:
557 return _D_channelModes.contains(mode);
564 QString IrcChannel::modeValue(const QChar &mode) const
566 Network::ChannelModeType modeType = network()->channelModeType(mode);
569 case Network::B_CHANMODE:
570 if (_B_channelModes.contains(mode))
571 return _B_channelModes[mode];
574 case Network::C_CHANMODE:
575 if (_C_channelModes.contains(mode))
576 return _C_channelModes[mode];
585 QStringList IrcChannel::modeValueList(const QChar &mode) const
587 Network::ChannelModeType modeType = network()->channelModeType(mode);
590 case Network::A_CHANMODE:
591 if (_A_channelModes.contains(mode))
592 return _A_channelModes[mode];
594 return QStringList();
599 QString IrcChannel::channelModeString() const
604 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
605 while (D_iter != _D_channelModes.constEnd()) {
606 modeString += *D_iter;
610 QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
611 while (BC_iter != _C_channelModes.constEnd()) {
612 modeString += BC_iter.key();
613 params << BC_iter.value();
617 BC_iter = _B_channelModes.constBegin();
618 while (BC_iter != _B_channelModes.constEnd()) {
619 modeString += BC_iter.key();
620 params << BC_iter.value();
623 if (modeString.isEmpty())
626 return QString("+%1 %2").arg(modeString).arg(params.join(" "));