1 /***************************************************************************
2 * Copyright (C) 2005-09 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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);
50 IrcChannel::~IrcChannel() {
53 // ====================
55 // ====================
56 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();
70 bool IrcChannel::isValidChannelUserMode(const QString &mode) const {
73 qWarning() << "Channel" << name() << "received Channel User Mode which is longer then 1 Char:" << mode;
79 QString IrcChannel::userModes(IrcUser *ircuser) const {
80 if(_userModes.contains(ircuser))
81 return _userModes[ircuser];
86 QString IrcChannel::userModes(const QString &nick) const {
87 return userModes(network->ircUser(nick));
90 void IrcChannel::setCodecForEncoding(const QString &name) {
91 setCodecForEncoding(QTextCodec::codecForName(name.toAscii()));
94 void IrcChannel::setCodecForEncoding(QTextCodec *codec) {
95 _codecForEncoding = codec;
98 void IrcChannel::setCodecForDecoding(const QString &name) {
99 setCodecForDecoding(QTextCodec::codecForName(name.toAscii()));
102 void IrcChannel::setCodecForDecoding(QTextCodec *codec) {
103 _codecForDecoding = codec;
106 QString IrcChannel::decodeString(const QByteArray &text) const {
107 if(!codecForDecoding()) return network->decodeString(text);
108 return ::decodeString(text, _codecForDecoding);
111 QByteArray IrcChannel::encodeString(const QString &string) const {
112 if(codecForEncoding()) {
113 return _codecForEncoding->fromUnicode(string);
115 return network->encodeString(string);
118 // ====================
120 // ====================
121 void IrcChannel::setTopic(const QString &topic) {
124 emit topicSet(topic);
127 void IrcChannel::setPassword(const QString &password) {
128 _password = password;
132 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes) {
136 if(users.count() != modes.count()) {
137 qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
141 QStringList newNicks;
142 QStringList newModes;
143 QList<IrcUser *> newUsers;
146 for(int i = 0; i < users.count(); i++) {
148 if(!ircuser || _userModes.contains(ircuser)) {
149 addUserMode(ircuser, modes[i]);
153 _userModes[ircuser] = modes[i];
154 ircuser->joinChannel(this);
155 connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
157 // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
158 // if you wonder why there is no counterpart to ircUserJoined:
159 // the joines are propagted by the ircuser. the signal ircUserJoined is only for convenience
161 newNicks << ircuser->nick();
162 newModes << modes[i];
166 if(newNicks.isEmpty())
169 SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
170 emit ircUsersJoined(newUsers);
173 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes) {
174 QList<IrcUser *> users;
175 foreach(QString nick, nicks)
176 users << network->newIrcUser(nick);
177 joinIrcUsers(users, modes);
180 void IrcChannel::joinIrcUser(IrcUser *ircuser) {
181 QList <IrcUser *> users;
185 joinIrcUsers(users, modes);
188 void IrcChannel::part(IrcUser *ircuser) {
189 if(isKnownUser(ircuser)) {
190 _userModes.remove(ircuser);
191 ircuser->partChannel(this);
192 // if you wonder why there is no counterpart to ircUserParted:
193 // the joines are propagted by the ircuser. the signal ircUserParted is only for convenience
194 disconnect(ircuser, 0, this, 0);
195 emit ircUserParted(ircuser);
197 if(network->isMe(ircuser) || _userModes.isEmpty()) {
198 // in either case we're no longer in the channel
199 // -> clean up the channel and destroy it
200 QList<IrcUser *> users = _userModes.keys();
202 foreach(IrcUser *user, users) {
203 disconnect(user, 0, this, 0);
204 user->partChannel(this);
207 network->removeIrcChannel(this);
212 void IrcChannel::part(const QString &nick) {
213 part(network->ircUser(nick));
217 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes) {
218 if(isKnownUser(ircuser)) {
219 _userModes[ircuser] = modes;
220 QString nick = ircuser->nick();
221 SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
222 emit ircUserModesSet(ircuser, modes);
226 void IrcChannel::setUserModes(const QString &nick, const QString &modes) {
227 setUserModes(network->ircUser(nick), modes);
231 void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode) {
232 if(!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
235 if(!_userModes[ircuser].contains(mode)) {
236 _userModes[ircuser] += mode;
237 QString nick = ircuser->nick();
238 SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
239 emit ircUserModeAdded(ircuser, mode);
244 void IrcChannel::addUserMode(const QString &nick, const QString &mode) {
245 addUserMode(network->ircUser(nick), mode);
249 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode) {
250 if(!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
253 if(_userModes[ircuser].contains(mode)) {
254 _userModes[ircuser].remove(mode);
255 QString nick = ircuser->nick();
256 SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
257 emit ircUserModeRemoved(ircuser, mode);
261 void IrcChannel::removeUserMode(const QString &nick, const QString &mode) {
262 removeUserMode(network->ircUser(nick), mode);
265 // INIT SET USER MODES
266 QVariantMap IrcChannel::initUserModes() const {
267 QVariantMap usermodes;
268 QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
269 while(iter != _userModes.constEnd()) {
270 usermodes[iter.key()->nick()] = iter.value();
276 void IrcChannel::initSetUserModes(const QVariantMap &usermodes) {
277 QList<IrcUser *> users;
279 QVariantMap::const_iterator iter = usermodes.constBegin();
280 while(iter != usermodes.constEnd()) {
281 users << network->newIrcUser(iter.key());
282 modes << iter.value().toString();
285 joinIrcUsers(users, modes);
288 QVariantMap IrcChannel::initChanModes() const {
289 QVariantMap channelModes;
292 QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
293 while(A_iter != _A_channelModes.constEnd()) {
294 A_modes[A_iter.key()] = A_iter.value();
297 channelModes["A"] = A_modes;
300 QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
301 while(B_iter != _B_channelModes.constEnd()) {
302 B_modes[B_iter.key()] = B_iter.value();
305 channelModes["B"] = B_modes;
308 QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
309 while(C_iter != _C_channelModes.constEnd()) {
310 C_modes[C_iter.key()] = C_iter.value();
313 channelModes["C"] = C_modes;
316 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
317 while(D_iter != _D_channelModes.constEnd()) {
321 channelModes["D"] = D_modes;
326 void IrcChannel::initSetChanModes(const QVariantMap &channelModes) {
327 QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
328 QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
329 while(iter != iterEnd) {
330 _A_channelModes[iter.key()[0]] = iter.value().toStringList();
334 iter = channelModes["B"].toMap().constBegin();
335 iterEnd = channelModes["B"].toMap().constEnd();
336 while(iter != iterEnd) {
337 _B_channelModes[iter.key()[0]] = iter.value().toString();
341 iter = channelModes["C"].toMap().constBegin();
342 iterEnd = channelModes["C"].toMap().constEnd();
343 while(iter != iterEnd) {
344 _C_channelModes[iter.key()[0]] = iter.value().toString();
348 QString D_modes = channelModes["D"].toString();
349 for(int i = 0; i < D_modes.count(); i++) {
350 _D_channelModes << D_modes[i];
355 void IrcChannel::ircUserDestroyed() {
356 IrcUser *ircUser = static_cast<IrcUser *>(sender());
358 _userModes.remove(ircUser);
359 // no further propagation.
360 // this leads only to fuck ups.
363 void IrcChannel::ircUserNickSet(QString nick) {
364 IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
366 emit ircUserNickSet(ircUser, nick);
369 /*******************************************************************************
373 * o CHANMODES=A,B,C,D
375 * The CHANMODES token specifies the modes that may be set on a channel.
376 * These modes are split into four categories, as follows:
378 * o Type A: Modes that add or remove an address to or from a list.
379 * These modes always take a parameter when sent by the server to a
380 * client; when sent by a client, they may be specified without a
381 * parameter, which requests the server to display the current
382 * contents of the corresponding list on the channel to the client.
383 * o Type B: Modes that change a setting on the channel. These modes
384 * always take a parameter.
385 * o Type C: Modes that change a setting on the channel. These modes
386 * take a parameter only when set; the parameter is absent when the
387 * mode is removed both in the client's and server's MODE command.
388 * o Type D: Modes that change a setting on the channel. These modes
389 * never take a parameter.
391 * If the server sends any additional types after these 4, the client
392 * MUST ignore them; this is intended to allow future extension of this
395 * The IRC server MUST NOT list modes in CHANMODES which are also
396 * present in the PREFIX parameter; however, for completeness, modes
397 * described in PREFIX may be treated as type B modes.
399 ******************************************************************************/
402 /*******************************************************************************
404 * A --> add/remove from List
405 * B --> set value or remove
406 * C --> set value or remove
409 * B and C behave very similar... we store the data in different datastructes
410 * for future compatibility
411 ******************************************************************************/
413 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
414 // see list above for chanmode types
415 void IrcChannel::addChannelMode(const QChar &mode, const QString &value) {
416 Network::ChannelModeType modeType = network->channelModeType(mode);
419 case Network::NOT_A_CHANMODE:
421 case Network::A_CHANMODE:
422 if(!_A_channelModes.contains(mode))
423 _A_channelModes[mode] = QStringList(value);
424 else if(!_A_channelModes[mode].contains(value))
425 _A_channelModes[mode] << value;
428 case Network::B_CHANMODE:
429 _B_channelModes[mode] = value;
432 case Network::C_CHANMODE:
433 _C_channelModes[mode] = value;
436 case Network::D_CHANMODE:
437 _D_channelModes << mode;
440 SYNC(ARG(mode), ARG(value))
443 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value) {
444 Network::ChannelModeType modeType = network->channelModeType(mode);
447 case Network::NOT_A_CHANMODE:
449 case Network::A_CHANMODE:
450 if(_A_channelModes.contains(mode))
451 _A_channelModes[mode].removeAll(value);
454 case Network::B_CHANMODE:
455 _B_channelModes.remove(mode);
458 case Network::C_CHANMODE:
459 _C_channelModes.remove(mode);
462 case Network::D_CHANMODE:
463 _D_channelModes.remove(mode);
466 SYNC(ARG(mode), ARG(value))
469 bool IrcChannel::hasMode(const QChar &mode) const {
470 Network::ChannelModeType modeType = network->channelModeType(mode);
473 case Network::NOT_A_CHANMODE:
475 case Network::A_CHANMODE:
476 return _A_channelModes.contains(mode);
477 case Network::B_CHANMODE:
478 return _B_channelModes.contains(mode);
479 case Network::C_CHANMODE:
480 return _C_channelModes.contains(mode);
481 case Network::D_CHANMODE:
482 return _D_channelModes.contains(mode);
488 QString IrcChannel::modeValue(const QChar &mode) const {
489 Network::ChannelModeType modeType = network->channelModeType(mode);
492 case Network::B_CHANMODE:
493 if(_B_channelModes.contains(mode))
494 return _B_channelModes[mode];
497 case Network::C_CHANMODE:
498 if(_C_channelModes.contains(mode))
499 return _C_channelModes[mode];
508 QStringList IrcChannel::modeValueList(const QChar &mode) const {
509 Network::ChannelModeType modeType = network->channelModeType(mode);
512 case Network::A_CHANMODE:
513 if(_A_channelModes.contains(mode))
514 return _A_channelModes[mode];
516 return QStringList();
520 QString IrcChannel::channelModeString() const {
524 QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
525 while(D_iter != _D_channelModes.constEnd()) {
526 modeString += *D_iter;
530 QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
531 while(BC_iter != _C_channelModes.constEnd()) {
532 modeString += BC_iter.key();
533 params << BC_iter.value();
537 BC_iter = _B_channelModes.constBegin();
538 while(BC_iter != _B_channelModes.constEnd()) {
539 modeString += BC_iter.key();
540 params << BC_iter.value();
543 if(modeString.isEmpty())
546 return QString("+%1 %2").arg(modeString).arg(params.join(" "));
550 Cipher* IrcChannel::cipher() {
552 _cipher = new Cipher();
557 void IrcChannel::setEncrypted(bool e) {
560 if(topic().isEmpty())
563 QByteArray key = network->bufferKey(name());
567 if(!cipher()->setKey(key))
570 //only send encrypted text to decrypter
571 int index = topic().indexOf(":",topic().indexOf(":")+1);
573 QString backup = topic().mid(0,index+1);
574 QString decrypted = cipher()->decryptTopic(topic().mid(index+1).toAscii());;
575 decrypted.prepend(backup);
577 setTopic(decodeString(decrypted.toAscii()));