X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fircserverhandler.cpp;h=445ca7c6f962662e7709f06e2926868a51307482;hp=563e6f073f57b28bbf47be8ab422dc35b3fb5b2d;hb=e4972a0ebce0d555d752ab2f34ffd6a6dcdef1dd;hpb=026199305d921c1ddbe706cd0da86ae058f48647 diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index 563e6f07..445ca7c6 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-09 by the Quassel Project * + * Copyright (C) 2005-10 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -33,12 +33,14 @@ #include IrcServerHandler::IrcServerHandler(CoreNetwork *parent) - : BasicHandler(parent), + : CoreBasicHandler(parent), _whois(false) { + connect(parent, SIGNAL(disconnected(NetworkId)), this, SLOT(destroyNetsplits())); } IrcServerHandler::~IrcServerHandler() { + destroyNetsplits(); } /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */ @@ -57,11 +59,23 @@ void IrcServerHandler::handleServerMsg(QByteArray msg) { // NOTE: This assumes that this is true in raw encoding, but well, hopefully there are no servers running in japanese on protocol level... int idx = msg.indexOf(" :"); if(idx >= 0) { - if(msg.length() > idx + 2) trailing = msg.mid(idx + 2); + if(msg.length() > idx + 2) + trailing = msg.mid(idx + 2); msg = msg.left(idx); } // OK, now it is safe to split... QList params = msg.split(' '); + + // This could still contain empty elements due to (faulty?) ircds sending multiple spaces in a row + // Also, QByteArray is not nearly as convenient to work with as QString for such things :) + QList::iterator iter = params.begin(); + while(iter != params.end()) { + if(iter->isEmpty()) + iter = params.erase(iter); + else + ++iter; + } + if(!trailing.isEmpty()) params << trailing; if(params.count() < 1) { qWarning() << "Received invalid string from server!"; @@ -70,6 +84,12 @@ void IrcServerHandler::handleServerMsg(QByteArray msg) { QString foo = serverDecode(params.takeFirst()); + // with SASL, the command is 'AUTHENTICATE +' and we should check for this here. + if(foo == QString("AUTHENTICATE +")) { + handleAuthenticate(); + return; + } + // a colon as the first chars indicates the existence of a prefix if(foo[0] == ':') { foo.remove(0, 1); @@ -146,6 +166,11 @@ void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const case 321: case 366: case 376: break; + case 903: case 904: case 905: case 906: case 907: + { + network()->putRawLine("CAP END"); + emit displayMsg(Message::Info, BufferInfo::StatusBuffer, "", "CAP: " + params.join("")); + } // Everything else will be marked in red, so we can add them somewhere. default: if(_whois) { @@ -174,9 +199,23 @@ void IrcServerHandler::handleJoin(const QString &prefix, const QList QString channel = serverDecode(params[0]); IrcUser *ircuser = network()->updateNickFromMask(prefix); - emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix); + + bool handledByNetsplit = false; + if(!_netsplits.empty()) { + foreach(Netsplit* n, _netsplits) { + handledByNetsplit = n->userJoined(prefix, channel); + if(handledByNetsplit) + break; + } + } + + // normal join + if(!handledByNetsplit) { + emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, prefix); + ircuser->joinChannel(channel); + } //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params; - ircuser->joinChannel(channel); + if(network()->isMe(ircuser)) { network()->setChannelJoined(channel); putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely @@ -237,10 +276,26 @@ void IrcServerHandler::handleMode(const QString &prefix, const QList // user channel modes (op, voice, etc...) if(paramOffset < params.count()) { IrcUser *ircUser = network()->ircUser(params[paramOffset]); - if(add) - channel->addUserMode(ircUser, QString(modes[c])); - else - channel->removeUserMode(ircUser, QString(modes[c])); + if(!ircUser) { + qWarning() << Q_FUNC_INFO << "Unknown IrcUser:" << params[paramOffset]; + } else { + if(add) { + bool handledByNetsplit = false; + if(!_netsplits.empty()) { + foreach(Netsplit* n, _netsplits) { + handledByNetsplit = n->userAlreadyJoined(ircUser->hostmask(), channel->name()); + if(handledByNetsplit) { + n->addMode(ircUser->hostmask(), channel->name(), QString(modes[c])); + break; + } + } + } + if(!handledByNetsplit) + channel->addUserMode(ircUser, QString(modes[c])); + } + else + channel->removeUserMode(ircUser, QString(modes[c])); + } } else { qWarning() << "Received MODE with too few parameters:" << serverDecode(params); } @@ -327,35 +382,41 @@ void IrcServerHandler::handleNotice(const QString &prefix, const QListisChannelName(target)) { - QString msg = serverDecode(params[1]); - QRegExp welcomeRegExp("^\\[([^\\]]+)\\] "); - if(welcomeRegExp.indexIn(msg) != -1) { - QString channelname = welcomeRegExp.cap(1); - msg = msg.mid(welcomeRegExp.matchedLength()); - CoreIrcChannel *chan = static_cast(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe - if(chan && !chan->receivedWelcomeMsg()) { - chan->setReceivedWelcomeMsg(); - emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix); - return; + + QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts); + QStringList::const_iterator targetIter; + for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) { + QString target = *targetIter; + + // special treatment for welcome messages like: + // :ChanServ!ChanServ@services. NOTICE egst :[#apache] Welcome, this is #apache. Please read the in-channel topic message. This channel is being logged by IRSeekBot. If you have any question please see http://blog.freenode.net/?p=68 + if(!network()->isChannelName(target)) { + QString msg = serverDecode(params[1]); + QRegExp welcomeRegExp("^\\[([^\\]]+)\\] "); + if(welcomeRegExp.indexIn(msg) != -1) { + QString channelname = welcomeRegExp.cap(1); + msg = msg.mid(welcomeRegExp.matchedLength()); + CoreIrcChannel *chan = static_cast(network()->ircChannel(channelname)); // we only have CoreIrcChannels in the core, so this cast is safe + if(chan && !chan->receivedWelcomeMsg()) { + chan->setReceivedWelcomeMsg(); + emit displayMsg(Message::Notice, BufferInfo::ChannelBuffer, channelname, msg, prefix); + continue; + } } } - } - if(prefix.isEmpty() || target == "AUTH") { - target = ""; - } else { - if(!target.isEmpty() && network()->prefixes().contains(target[0])) - target = target.mid(1); - if(!network()->isChannelName(target)) - target = nickFromMask(prefix); + if(prefix.isEmpty() || target == "AUTH") { + target = ""; + } else { + if(!target.isEmpty() && network()->prefixes().contains(target[0])) + target = target.mid(1); + if(!network()->isChannelName(target)) + target = nickFromMask(prefix); + } + + network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]); } - network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]); } void IrcServerHandler::handlePart(const QString &prefix, const QList ¶ms) { @@ -416,18 +477,23 @@ void IrcServerHandler::handlePrivmsg(const QString &prefix, const QListisChannelName(target)) - target = nickFromMask(prefix); + QStringList targets = serverDecode(params[0]).split(',', QString::SkipEmptyParts); + QStringList::const_iterator targetIter; + for(targetIter = targets.constBegin(); targetIter != targets.constEnd(); targetIter++) { + const QString &target = network()->isChannelName(*targetIter) + ? *targetIter + : senderNick; - // it's possible to pack multiple privmsgs into one param using ctcp - // - > we let the ctcpHandler do the work - network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg); + // it's possible to pack multiple privmsgs into one param using ctcp + // - > we let the ctcpHandler do the work + network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg); + } } void IrcServerHandler::handleQuit(const QString &prefix, const QList ¶ms) { @@ -438,10 +504,32 @@ void IrcServerHandler::handleQuit(const QString &prefix, const QList if(params.count() > 0) msg = userDecode(ircuser->nick(), params[0]); - foreach(QString channel, ircuser->channels()) - emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix); - - ircuser->quit(); + // check if netsplit + if(Netsplit::isNetsplit(msg)) { + Netsplit *n; + if(!_netsplits.contains(msg)) { + n = new Netsplit(); + connect(n, SIGNAL(finished()), this, SLOT(handleNetsplitFinished())); + connect(n, SIGNAL(netsplitJoin(const QString&, const QStringList&, const QStringList&, const QString&)), + this, SLOT(handleNetsplitJoin(const QString&, const QStringList&, const QStringList&, const QString&))); + connect(n, SIGNAL(netsplitQuit(const QString&, const QStringList&, const QString&)), + this, SLOT(handleNetsplitQuit(const QString&, const QStringList&, const QString&))); + connect(n, SIGNAL(earlyJoin(const QString&, const QStringList&, const QStringList&)), + this, SLOT(handleEarlyNetsplitJoin(const QString&, const QStringList&, const QStringList&))); + _netsplits.insert(msg, n); + } + else { + n = _netsplits[msg]; + } + // add this user to the netsplit + n->userQuit(prefix, ircuser->channels(),msg); + } + // normal quit + else { + foreach(QString channel, ircuser->channels()) + emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, msg, prefix); + ircuser->quit(); + } } void IrcServerHandler::handleTopic(const QString &prefix, const QList ¶ms) { @@ -462,7 +550,32 @@ void IrcServerHandler::handleTopic(const QString &prefix, const QListsetTopic(topic); - emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel->name(), tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel->name()).arg(topic)); + emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel->name(), tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel->name()).arg(topic)); +} + +void IrcServerHandler::handleCap(const QString &prefix, const QList ¶ms) { + // for SASL, there will only be a single param of 'sasl', however you can check here for + // additional CAP messages (ls, multi-prefix, et cetera). + + Q_UNUSED(prefix); + + if(params.size() == 3) { + QString param = serverDecode(params[2]); + if(param == QString("sasl")) { // SASL Ready + network()->putRawLine(serverEncode("AUTHENTICATE PLAIN")); // Only working with PLAIN atm, blowfish later + } + } +} + +void IrcServerHandler::handleAuthenticate() { + QString construct = network()->saslAccount(); + construct.append(QChar(QChar::Null)); + construct.append(network()->saslAccount()); + construct.append(QChar(QChar::Null)); + construct.append(network()->saslPassword()); + QByteArray saslData = QByteArray(construct.toAscii().toBase64()); + saslData.prepend(QString("AUTHENTICATE ").toAscii()); + network()->putRawLine(saslData); } /* RPL_WELCOME */ @@ -820,6 +933,19 @@ void IrcServerHandler::handle324(const QString &prefix, const QList handleMode(prefix, params); } +/* RPL_??? - " */ +void IrcServerHandler::handle328(const QString &prefix, const QList ¶ms) { + Q_UNUSED(prefix); + if(!checkParamCount("IrcServerHandler::handle328()", params, 2)) + return; + + QString channel = serverDecode(params[0]); + QString homepage = serverDecode(params[1]); + + emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Homepage for %1 is %2").arg(channel, homepage)); +} + + /* RPL_??? - " " */ void IrcServerHandler::handle329(const QString &prefix, const QList ¶ms) { Q_UNUSED(prefix); @@ -848,7 +974,7 @@ void IrcServerHandler::handle331(const QString &prefix, const QList if(chan) chan->setTopic(QString()); - emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("No topic is set for %1.").arg(channel)); + emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel, tr("No topic is set for %1.").arg(channel)); } /* RPL_TOPIC */ @@ -863,7 +989,7 @@ void IrcServerHandler::handle332(const QString &prefix, const QList if(chan) chan->setTopic(topic); - emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic)); + emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic)); } /* Topic set by... */ @@ -873,7 +999,7 @@ void IrcServerHandler::handle333(const QString &prefix, const QList return; QString channel = serverDecode(params[0]); - emit displayMsg(Message::Server, BufferInfo::ChannelBuffer, channel, + emit displayMsg(Message::Topic, BufferInfo::ChannelBuffer, channel, tr("Topic set by %1 on %2") .arg(serverDecode(params[1]), QDateTime::fromTime_t(channelDecode(channel, params[2]).toUInt()).toString())); } @@ -977,6 +1103,73 @@ void IrcServerHandler::handle433(const QString &prefix, const QList tryNextNick(errnick); } +/* Handle signals from Netsplit objects */ + +void IrcServerHandler::handleNetsplitJoin(const QString &channel, const QStringList &users, const QStringList &modes, const QString& quitMessage) +{ + IrcChannel *ircChannel = network()->ircChannel(channel); + if(!ircChannel) { + return; + } + QList ircUsers; + QStringList newModes = modes; + QStringList newUsers = users; + + foreach(QString user, users) { + IrcUser *iu = network()->ircUser(nickFromMask(user)); + if(iu) + ircUsers.append(iu); + else { // the user already quit + int idx = users.indexOf(user); + newUsers.removeAt(idx); + newModes.removeAt(idx); + } + } + + QString msg = newUsers.join("#:#").append("#:#").append(quitMessage); + emit displayMsg(Message::NetsplitJoin, BufferInfo::ChannelBuffer, channel, msg); + ircChannel->joinIrcUsers(ircUsers, newModes); +} + +void IrcServerHandler::handleNetsplitQuit(const QString &channel, const QStringList &users, const QString& quitMessage) +{ + QString msg = users.join("#:#").append("#:#").append(quitMessage); + emit displayMsg(Message::NetsplitQuit, BufferInfo::ChannelBuffer, channel, msg); + foreach(QString user, users) { + IrcUser *iu = network()->ircUser(nickFromMask(user)); + if(iu) + iu->quit(); + } +} + +void IrcServerHandler::handleEarlyNetsplitJoin(const QString &channel, const QStringList &users, const QStringList &modes) { + IrcChannel *ircChannel = network()->ircChannel(channel); + if(!ircChannel) { + qDebug() << "handleEarlyNetsplitJoin(): channel " << channel << " invalid"; + return; + } + QList ircUsers; + QStringList newModes = modes; + + foreach(QString user, users) { + IrcUser *iu = network()->updateNickFromMask(user); + if(iu) { + ircUsers.append(iu); + emit displayMsg(Message::Join, BufferInfo::ChannelBuffer, channel, channel, user); + } + else { + newModes.removeAt(users.indexOf(user)); + } + } + ircChannel->joinIrcUsers(ircUsers, newModes); +} +void IrcServerHandler::handleNetsplitFinished() +{ + Netsplit* n = qobject_cast(sender()); + _netsplits.remove(_netsplits.key(n)); + n->deleteLater(); +} + /* */ // FIXME networkConnection()->setChannelKey("") for all ERR replies indicating that a JOIN went wrong @@ -1010,6 +1203,10 @@ bool IrcServerHandler::checkParamCount(const QString &methodName, const QList