1 /***************************************************************************
2 * Copyright (C) 2005-2013 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),
43 setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname);
47 IrcChannel::~IrcChannel()
52 // ====================
54 // ====================
55 bool IrcChannel::isKnownUser(IrcUser *ircuser) const
58 qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
62 if (!_userModes.contains(ircuser)) {
63 qWarning() << "Channel" << name() << "received data for unknown User" << ircuser->nick();
71 bool IrcChannel::isValidChannelUserMode(const QString &mode) const
74 if (mode.size() > 1) {
75 qWarning() << "Channel" << name() << "received Channel User Mode which is longer then 1 Char:" << mode;
82 QString IrcChannel::userModes(IrcUser *ircuser) const
84 if (_userModes.contains(ircuser))
85 return _userModes[ircuser];
91 QString IrcChannel::userModes(const QString &nick) const
93 return userModes(network()->ircUser(nick));
97 void IrcChannel::setCodecForEncoding(const QString &name)
99 setCodecForEncoding(QTextCodec::codecForName(name.toAscii()));
103 void IrcChannel::setCodecForEncoding(QTextCodec *codec)
105 _codecForEncoding = codec;
109 void IrcChannel::setCodecForDecoding(const QString &name)
111 setCodecForDecoding(QTextCodec::codecForName(name.toAscii()));
115 void IrcChannel::setCodecForDecoding(QTextCodec *codec)
117 _codecForDecoding = codec;
121 QString IrcChannel::decodeString(const QByteArray &text) const
123 if (!codecForDecoding()) return network()->decodeString(text);
124 return ::decodeString(text, _codecForDecoding);
128 QByteArray IrcChannel::encodeString(const QString &string) const
130 if (codecForEncoding()) {
131 return _codecForEncoding->fromUnicode(string);
133 return network()->encodeString(string);
137 // ====================
139 // ====================
140 void IrcChannel::setTopic(const QString &topic)
144 emit topicSet(topic);
148 void IrcChannel::setPassword(const QString &password)
150 _password = password;
155 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
160 if (users.count() != modes.count()) {
161 qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
165 QStringList newNicks;
166 QStringList newModes;
167 QList<IrcUser *> newUsers;
170 for (int i = 0; i < users.count(); i++) {
172 if (!ircuser || _userModes.contains(ircuser)) {
173 addUserMode(ircuser, modes[i]);
177 _userModes[ircuser] = modes[i];
178 ircuser->joinChannel(this);
179 connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
181 // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
182 // if you wonder why there is no counterpart to ircUserJoined:
183 // the joines are propagted by the ircuser. the signal ircUserJoined is only for convenience
185 newNicks << ircuser->nick();
186 newModes << modes[i];
190 if (newNicks.isEmpty())
193 SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
194 emit ircUsersJoined(newUsers);
198 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
200 QList<IrcUser *> users;
201 foreach(QString nick, nicks)
202 users << network()->newIrcUser(nick);
203 joinIrcUsers(users, modes);
207 void IrcChannel::joinIrcUser(IrcUser *ircuser)
209 QList<IrcUser *> users;
213 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 joines are propagted by the ircuser. the signal ircUserParted is only for convenience
224 disconnect(ircuser, 0, this, 0);
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, 0, this, 0);
234 user->partChannel(this);
237 network()->removeIrcChannel(this);
243 void IrcChannel::part(const QString &nick)
245 part(network()->ircUser(nick));
250 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
252 if (isKnownUser(ircuser)) {
253 _userModes[ircuser] = modes;
254 QString nick = ircuser->nick();
255 SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
256 emit ircUserModesSet(ircuser, modes);
261 void IrcChannel::setUserModes(const QString &nick, const QString &modes)
263 setUserModes(network()->ircUser(nick), modes);
268 void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
270 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
273 if (!_userModes[ircuser].contains(mode)) {
274 _userModes[ircuser] += mode;
275 QString nick = ircuser->nick();
276 SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
277 emit ircUserModeAdded(ircuser, mode);
282 void IrcChannel::addUserMode(const QString &nick, const QString &mode)
284 addUserMode(network()->ircUser(nick), mode);
289 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
291 if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
294 if (_userModes[ircuser].contains(mode)) {
295 _userModes[ircuser].remove(mode);
296 QString nick = ircuser->nick();
297 SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
298 emit ircUserModeRemoved(ircuser, mode);
303 void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
305 removeUserMode(network()->ircUser(nick), mode);
309 // INIT SET USER MODES
310 QVariantMap IrcChannel::initUserModes() const
312 QVariantMap usermodes;
313 QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
314 while (iter != _userModes.constEnd()) {
315 usermodes[iter.key()->nick()] = iter.value();
322 void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
324 QList<IrcUser *> users;
326 QVariantMap::const_iterator iter = usermodes.constBegin();
327 while (iter != usermodes.constEnd()) {
328 users << network()->newIrcUser(iter.key());
329 modes << iter.value().toString();
332 joinIrcUsers(users, modes);
336 QVariantMap IrcChannel::initChanModes() const
338 QVariantMap channelModes;
341 QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
342 while (A_iter != _A_channelModes.constEnd()) {
343 A_modes[A_iter.key()] = A_iter.value();
346 channelModes["A"] = A_modes;
349 QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
350 while (B_iter != _B_channelModes.constEnd()) {
351 B_modes[B_iter.key()] = B_iter.value();
354 channelModes["B"] = B_modes;
357 QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
358 while (C_iter != _C_channelModes.constEnd()) {
359 C_modes[C_iter.key()] = C_iter.value();
362 channelModes["C"] = C_modes;
365 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
366 while (D_iter != _D_channelModes.constEnd()) {
370 channelModes["D"] = D_modes;
376 void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
378 QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
379 QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
380 while (iter != iterEnd) {
381 _A_channelModes[iter.key()[0]] = iter.value().toStringList();
385 iter = channelModes["B"].toMap().constBegin();
386 iterEnd = channelModes["B"].toMap().constEnd();
387 while (iter != iterEnd) {
388 _B_channelModes[iter.key()[0]] = iter.value().toString();
392 iter = channelModes["C"].toMap().constBegin();
393 iterEnd = channelModes["C"].toMap().constEnd();
394 while (iter != iterEnd) {
395 _C_channelModes[iter.key()[0]] = iter.value().toString();
399 QString D_modes = channelModes["D"].toString();
400 for (int i = 0; i < D_modes.count(); i++) {
401 _D_channelModes << D_modes[i];
406 void IrcChannel::ircUserDestroyed()
408 IrcUser *ircUser = static_cast<IrcUser *>(sender());
410 _userModes.remove(ircUser);
411 // no further propagation.
412 // this leads only to fuck ups.
416 void IrcChannel::ircUserNickSet(QString nick)
418 IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
420 emit ircUserNickSet(ircUser, nick);
424 /*******************************************************************************
428 * o CHANMODES=A,B,C,D
430 * The CHANMODES token specifies the modes that may be set on a channel.
431 * These modes are split into four categories, as follows:
433 * o Type A: Modes that add or remove an address to or from a list.
434 * These modes always take a parameter when sent by the server to a
435 * client; when sent by a client, they may be specified without a
436 * parameter, which requests the server to display the current
437 * contents of the corresponding list on the channel to the client.
438 * o Type B: Modes that change a setting on the channel. These modes
439 * always take a parameter.
440 * o Type C: Modes that change a setting on the channel. These modes
441 * take a parameter only when set; the parameter is absent when the
442 * mode is removed both in the client's and server's MODE command.
443 * o Type D: Modes that change a setting on the channel. These modes
444 * never take a parameter.
446 * If the server sends any additional types after these 4, the client
447 * MUST ignore them; this is intended to allow future extension of this
450 * The IRC server MUST NOT list modes in CHANMODES which are also
451 * present in the PREFIX parameter; however, for completeness, modes
452 * described in PREFIX may be treated as type B modes.
454 ******************************************************************************/
456 /*******************************************************************************
458 * A --> add/remove from List
459 * B --> set value or remove
460 * C --> set value or remove
463 * B and C behave very similar... we store the data in different datastructes
464 * for future compatibility
465 ******************************************************************************/
467 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
468 // see list above for chanmode types
469 void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
471 Network::ChannelModeType modeType = network()->channelModeType(mode);
474 case Network::NOT_A_CHANMODE:
476 case Network::A_CHANMODE:
477 if (!_A_channelModes.contains(mode))
478 _A_channelModes[mode] = QStringList(value);
479 else if (!_A_channelModes[mode].contains(value))
480 _A_channelModes[mode] << value;
483 case Network::B_CHANMODE:
484 _B_channelModes[mode] = value;
487 case Network::C_CHANMODE:
488 _C_channelModes[mode] = value;
491 case Network::D_CHANMODE:
492 _D_channelModes << mode;
495 SYNC(ARG(mode), ARG(value))
499 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
501 Network::ChannelModeType modeType = network()->channelModeType(mode);
504 case Network::NOT_A_CHANMODE:
506 case Network::A_CHANMODE:
507 if (_A_channelModes.contains(mode))
508 _A_channelModes[mode].removeAll(value);
511 case Network::B_CHANMODE:
512 _B_channelModes.remove(mode);
515 case Network::C_CHANMODE:
516 _C_channelModes.remove(mode);
519 case Network::D_CHANMODE:
520 _D_channelModes.remove(mode);
523 SYNC(ARG(mode), ARG(value))
527 bool IrcChannel::hasMode(const QChar &mode) const
529 Network::ChannelModeType modeType = network()->channelModeType(mode);
532 case Network::NOT_A_CHANMODE:
534 case Network::A_CHANMODE:
535 return _A_channelModes.contains(mode);
536 case Network::B_CHANMODE:
537 return _B_channelModes.contains(mode);
538 case Network::C_CHANMODE:
539 return _C_channelModes.contains(mode);
540 case Network::D_CHANMODE:
541 return _D_channelModes.contains(mode);
548 QString IrcChannel::modeValue(const QChar &mode) const
550 Network::ChannelModeType modeType = network()->channelModeType(mode);
553 case Network::B_CHANMODE:
554 if (_B_channelModes.contains(mode))
555 return _B_channelModes[mode];
558 case Network::C_CHANMODE:
559 if (_C_channelModes.contains(mode))
560 return _C_channelModes[mode];
569 QStringList IrcChannel::modeValueList(const QChar &mode) const
571 Network::ChannelModeType modeType = network()->channelModeType(mode);
574 case Network::A_CHANMODE:
575 if (_A_channelModes.contains(mode))
576 return _A_channelModes[mode];
578 return QStringList();
583 QString IrcChannel::channelModeString() const
588 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
589 while (D_iter != _D_channelModes.constEnd()) {
590 modeString += *D_iter;
594 QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
595 while (BC_iter != _C_channelModes.constEnd()) {
596 modeString += BC_iter.key();
597 params << BC_iter.value();
601 BC_iter = _B_channelModes.constBegin();
602 while (BC_iter != _B_channelModes.constEnd()) {
603 modeString += BC_iter.key();
604 params << BC_iter.value();
607 if (modeString.isEmpty())
610 return QString("+%1 %2").arg(modeString).arg(params.join(" "));