diff --git a/src/irc/inputfilter.cpp b/src/irc/inputfilter.cpp index a24a7801..ed810080 100644 --- a/src/irc/inputfilter.cpp +++ b/src/irc/inputfilter.cpp @@ -1,2528 +1,2540 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004, 2016 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #include "inputfilter.h" #include "server.h" #include "replycodes.h" #include "application.h" #include "version.h" #include "commit.h" #include "query.h" #include "channel.h" #include "statuspanel.h" #include "common.h" #include "notificationhandler.h" #include #include #include #include #include InputFilter::InputFilter() : m_server(0), m_lagMeasuring(false) { m_connecting = false; } InputFilter::~InputFilter() { } void InputFilter::setServer(Server* newServer) { m_server = newServer; } template int posOrLen(T chr, const QString& str, int from=0) { int p=str.indexOf(QLatin1Char(chr), from); if (p<0) return str.size(); return p; } /// "[22:08] >> :thiago!n=thiago@kde/thiago QUIT :Read error: 110 (Connection timed out)" /// "[21:47] >> :Zarin!n=x365@kde/developer/lmurray PRIVMSG #plasma :If the decoration doesn't have paint( QPixmap ) it falls back to the old one" /// "[21:49] >> :niven.freenode.net 352 argonel #kde-forum i=beezle konversation/developer/argonel irc.freenode.net argonel H :0 Konversation User " void InputFilter::parseLine(const QString& line) { int start=0; QHash messageTags; if (line[0] == QLatin1Char('@')) { messageTags = parseMessageTags(line, &start); } QString prefix; int end(posOrLen(' ', line, start)); if (line[start]==QLatin1Char(':')) { prefix = line.mid(start + 1, end - start - 1); //skips the colon and does not include the trailing space start = end + 1; end = posOrLen(' ', line, start); } //even though the standard is UPPER CASE, someone when through a great deal of trouble to make this lower case... QString command = QString(line.mid(start, end-start)).toLower(); start=end+1; int trailing=line.indexOf(QStringLiteral(" :"), end); if (trailing >= 0) end=trailing; else end=line.size(); QStringList parameterList; while (start < end) { if (line[start]==QLatin1Char(' ')) start++; else { int p=line.indexOf(QLatin1Char(' '), start); //easier to have Qt loop for me :) if (p<0) p=end; parameterList << line.mid(start, p-start); start=p+1; } }; /* Quote: "The final colon is specified as a "last argument" designator, and * is always valid before the final argument." * Quote: "The last parameter may be an empty string." * Quote: "After extracting the parameter list, all parameters are equal * whether matched by or . is just a * syntactic trick to allow SPACE within the parameter." */ if (trailing >= 0 ) //<-- trailing == ":" - per above we need the empty string parameterList << line.mid( qMin(trailing+2, line.size()) ); Q_ASSERT(m_server); //how could we have gotten a line without a server? // Server command, if no "!" was found in prefix if ((!prefix.contains(QLatin1Char('!'))) && (prefix != m_server->getNickname())) { parseServerCommand(prefix, command, parameterList, messageTags); } else { parseClientCommand(prefix, command, parameterList, messageTags); } } #define trailing (parameterList.isEmpty() ? QString() : parameterList.last()) #define plHas(x) _plHas(parameterList.count(), (x)) bool _plHad=false; int _plWanted = 0; bool _plHas(int count, int x) { _plHad=(count >= x); _plWanted = x; if (!_plHad) qDebug() << "plhad" << count << "wanted" << x; return _plHad; } void InputFilter::parseClientCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags) { Application* konv_app = Application::instance(); Q_ASSERT(konv_app); Q_ASSERT(m_server); // Extract nickname from prefix int pos = prefix.indexOf(QLatin1Char('!')); QString sourceNick = prefix.left(pos); QString sourceHostmask = prefix.mid(pos + 1); // remember hostmask for this nick, it could have changed m_server->addHostmaskToNick(sourceNick, sourceHostmask); + bool isNumeric = false; + int numeric = command.toInt(&isNumeric); + + if(isNumeric) + { + parseNumeric(prefix, numeric, parameterList, messageTags); + } //PRIVMSG #channel :message - if (command == QStringLiteral("privmsg") && plHas(2)) + else if (command == QStringLiteral("privmsg") && plHas(2)) { bool isChan = isAChannel(parameterList.value(0)); // CTCP message? if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { trailing = trailing.mid(1); } if (!trailing.isEmpty() && trailing.at(0)==QChar(0x01)) { // cut out the CTCP command QString ctcp = trailing.mid(1,trailing.indexOf(QChar(0x01),1)-1); //QString::left(-1) returns the entire string QString ctcpCommand = ctcp.left(ctcp.indexOf(QLatin1Char(' '))).toLower(); bool hasArg = ctcp.indexOf(QLatin1Char(' ')) > 0; //QString::mid(-1)+1 = 0, which returns the entire string if there is no space, resulting in command==arg QString ctcpArgument = hasArg ? ctcp.mid(ctcp.indexOf(QLatin1Char(' '))+1) : QString(); hasArg = !ctcpArgument.isEmpty(); if (hasArg) ctcpArgument = konv_app->doAutoreplace(ctcpArgument, false).first; // If it was a ctcp action, build an action string if (ctcpCommand == QStringLiteral("action") && isChan) { if (!isIgnore(prefix, Ignore::Channel)) { Channel* channel = m_server->getChannelByName( parameterList.value(0) ); if (!channel) { qCritical() << "Didn't find the channel " << parameterList.value(0) << endl; return; } channel->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname()) { if (hasArg && ctcpArgument.toLower().contains(QRegExp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")))) { konv_app->notificationHandler()->nick(channel, sourceNick, ctcpArgument); } else { konv_app->notificationHandler()->message(channel, sourceNick, ctcpArgument); } } } } // If it was a ctcp action, build an action string else if (ctcpCommand == QStringLiteral("action") && !isChan) { // Check if we ignore queries from this nick if (!isIgnore(prefix, Ignore::Query)) { NickInfoPtr nickinfo = m_server->obtainNickInfo(sourceNick); nickinfo->setHostmask(sourceHostmask); // create new query (server will check for dupes) Query* query = m_server->addQuery(nickinfo, false /* we didn't initiate this*/ ); // send action to query query->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname() && query) konv_app->notificationHandler()->queryMessage(query, sourceNick, ctcpArgument); } } // Answer ping requests else if (ctcpCommand == QStringLiteral("ping") && hasArg) { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING request from %1 to channel %2, sending answer.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QStringLiteral("PING"), sourceNick), messageTags ); } m_server->ctcpReply(sourceNick, QString(QStringLiteral("PING %1")).arg(ctcpArgument)); } } // Maybe it was a version request, so act appropriately else if (ctcpCommand == QStringLiteral("version")) { if(!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1 to channel %2.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1.", sourceNick), messageTags ); } QString reply; if (Preferences::self()->customVersionReplyEnabled()) { reply = Preferences::self()->customVersionReply().trimmed(); } else { // Do not internationalize the below version string reply = QString(QStringLiteral("Konversation %1 Build %2 (C) 2002-2018 by the Konversation team")) .arg(QStringLiteral(KONVI_VERSION)) .arg(QString::number(COMMIT)); } if (!reply.isEmpty()) m_server->ctcpReply(sourceNick,QStringLiteral("VERSION ")+reply); } } // DCC request? else if (ctcpCommand==QStringLiteral("dcc") && !isChan && hasArg) { if (!isIgnore(prefix,Ignore::DCC)) { // Extract DCC type and argument list QString dccType=ctcpArgument.toLower().section(QLatin1Char(' '),0,0); // Support file names with spaces QString dccArguments = ctcpArgument.mid(ctcpArgument.indexOf(QLatin1Char(' '))+1); QStringList dccArgumentList; if ((dccArguments.count(QLatin1Char('\"')) >= 2) && (dccArguments.startsWith(QLatin1Char('\"')))) { int lastQuotePos = dccArguments.lastIndexOf(QLatin1Char('\"')); if (dccArguments[lastQuotePos+1] == QLatin1Char(' ')) { QString fileName = dccArguments.mid(1, lastQuotePos-1); dccArguments = dccArguments.mid(lastQuotePos+2); dccArgumentList.append(fileName); } } dccArgumentList += dccArguments.split(QLatin1Char(' '), QString::SkipEmptyParts); if (dccType==QStringLiteral("send")) { if (dccArgumentList.count()==4) { // incoming file konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else if (dccArgumentList.count() >= 5) { if (dccArgumentList[dccArgumentList.size() - 3] == QStringLiteral("0")) { // incoming file (Reverse DCC) konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC emit startReverseDccSendTransfer(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC SEND request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("accept")) { // resume request was accepted if (dccArgumentList.count() >= 3) { emit resumeDccGetTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC ACCEPT request from %1.", sourceNick), messageTags ); } } // Remote client wants our sent file resumed else if (dccType==QStringLiteral("resume")) { if (dccArgumentList.count() >= 3) { emit resumeDccSendTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC RESUME request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("chat")) { if (dccArgumentList.count() == 3) { // incoming chat emit addDccChat(sourceNick,dccArgumentList); } else if (dccArgumentList.count() == 4) { if (dccArgumentList[dccArgumentList.size() - 2] == QStringLiteral("0")) { // incoming chat (Reverse DCC) emit addDccChat(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC chat emit startReverseDccChat(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC CHAT request from %1.", sourceNick), messageTags ); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Unknown DCC command %1 received from %2.", ctcpArgument, sourceNick), messageTags ); } } } else if (ctcpCommand==QStringLiteral("clientinfo") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QString::fromLatin1("CLIENTINFO"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("CLIENTINFO ACTION CLIENTINFO DCC PING TIME VERSION")); } } else if (ctcpCommand==QStringLiteral("time") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QString::fromLatin1("TIME"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("TIME ")+QDateTime::currentDateTime().toString()); } } // No known CTCP request, give a general message else { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) m_server->appendServerMessageToChannel( parameterList.value(0), QStringLiteral("CTCP"), i18n("Received unknown CTCP-%1 request from %2 to Channel %3.", ctcp, sourceNick, parameterList.value(0)), messageTags ); else m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received unknown CTCP-%1 request from %2.", ctcp, sourceNick), messageTags ); } } } // No CTCP, so it's an ordinary channel or query message else { parsePrivMsg(prefix, parameterList, messageTags); } } else if (command==QStringLiteral("notice") && plHas(2)) { if (!isIgnore(prefix,Ignore::Notice)) { // Channel notice? if(isAChannel(parameterList.value(0))) { if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { trailing = trailing.mid(1); } m_server->appendServerMessageToChannel(parameterList.value(0), i18n("Notice"), i18n("-%1 to %2- %3", sourceNick, parameterList.value(0), trailing), messageTags ); } // Private notice else { // Was this a CTCP reply? if (!trailing.isEmpty() && trailing.at(0) == QChar(0x01)) { // cut 0x01 bytes from trailing string QString ctcp(trailing.mid(1,trailing.length()-2)); QString replyReason(ctcp.section(QLatin1Char(' '),0,0)); QString reply(ctcp.section(QLatin1Char(' '),1)); // pong reply, calculate turnaround time if (replyReason.toLower()==QStringLiteral("ping")) { int dateArrived=QDateTime::currentDateTime().toTime_t(); int dateSent=reply.toInt(); int time = dateArrived-dateSent; QString unit = i18np("second", "seconds", time); m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING reply from %1: %2 %3.", sourceNick, time, unit), messageTags ); } else if (replyReason.toLower() == QStringLiteral("dcc")) { qDebug() << reply; QStringList dccList = reply.split(QLatin1Char(' ')); //all dcc notices we receive are rejects if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("reject")) { dccList.removeFirst(); if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("send")) { dccList.removeFirst(); emit rejectDccSendTransfer(sourceNick,dccList); } else if (dccList.first().toLower() == QStringLiteral("chat")) { emit rejectDccChat(sourceNick); } } } // all other ctcp replies get a general message else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 reply from %2: %3.", replyReason, sourceNick, reply), messageTags ); } } // No, so it was a normal notice else { #ifdef HAVE_QCA2 //Key exchange if (trailing.startsWith(QLatin1String("DH1080_INIT "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_INIT from %1", sourceNick), messageTags); m_server->parseInitKeyX(sourceNick, trailing.mid(12)); } else if (trailing.startsWith(QLatin1String("DH1080_FINISH "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_FINISH from %1", sourceNick), messageTags); m_server->parseFinishKeyX(sourceNick, trailing.mid(14)); } else { #endif m_server->appendMessageToFrontmost(i18n("Notice"), i18n("-%1- %2", sourceNick, m_server->identifyMsg() ? trailing.mid(1) : trailing), messageTags); #ifdef HAVE_QCA2 } #endif } } } } else if (command==QStringLiteral("join") && plHas(1)) { QString channelName; QString account; QString realName; channelName = parameterList[0]; if (m_server->capabilities() & Server::ExtendedJoin && plHas(3)) { if (parameterList[1] != "*") account = parameterList[1]; realName = parameterList[2]; } // Did we join the channel, or was it someone else? if (m_server->isNickname(sourceNick)) { /* QString key; // TODO: Try to remember channel keys for autojoins and manual joins, so // we can get %k to work if(channelName.contains(' ')) { key=channelName.section(' ',1,1); channelName=channelName.section(' ',0,0); } */ // Join the channel Channel* channel = m_server->joinChannel(channelName, sourceHostmask, messageTags); // Upon JOIN we're going to receive some NAMES input from the server which // we need to be able to tell apart from manual invocations of /names setAutomaticRequest(QStringLiteral("NAMES"),channelName,true); channel->clearModeList(); // Request modes for the channel m_server->queue(QStringLiteral("MODE ")+channelName, Server::LowPriority); // Initiate channel ban list channel->clearBanList(); setAutomaticRequest(QStringLiteral("BANLIST"),channelName,true); m_server->queue(QStringLiteral("MODE ")+channelName+QStringLiteral(" +b"), Server::LowPriority); } else { Channel* channel = m_server->nickJoinsChannel(channelName, sourceNick, sourceHostmask, account, realName, messageTags); konv_app->notificationHandler()->join(channel, sourceNick); } } else if (command==QStringLiteral("kick") && plHas(2)) { m_server->nickWasKickedFromChannel(parameterList.value(0), parameterList.value(1), sourceNick, trailing, messageTags); } else if (command==QStringLiteral("part") && plHas(1)) { // A version of the PART line encountered on ircu: ":Nick!user@host PART :#channel" QString channel(parameterList.value(0)); QString reason(parameterList.value(1)); Channel* channelPtr = m_server->removeNickFromChannel(channel, sourceNick, reason, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->part(channelPtr, sourceNick); } } else if (command==QStringLiteral("quit") && plHas(1)) { m_server->removeNickFromServer(sourceNick, trailing, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->quit(m_server->getStatusView(), sourceNick); } } else if (command==QStringLiteral("nick") && plHas(1)) { QString newNick(parameterList.value(0)); // Message may not include ":" in front of the new nickname m_server->renameNick(sourceNick, newNick, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->nickChange(m_server->getStatusView(), sourceNick, newNick); } } else if (command==QStringLiteral("topic") && plHas(2)) { m_server->setChannelTopic(sourceNick, parameterList.value(0), trailing, messageTags); } else if (command==QStringLiteral("mode") && plHas(2)) // mode #channel -/+ mmm params { parseModes(sourceNick, parameterList, messageTags); Channel* channel = m_server->getChannelByName(parameterList.value(0)); konv_app->notificationHandler()->mode(channel, sourceNick, parameterList.value(0), QStringList(parameterList.mid(1)).join (QStringLiteral(" "))); } else if (command==QStringLiteral("invite") && plHas(2)) //:ejm!i=beezle@bas5-oshawa95-1176455927.dsl.bell.ca INVITE argnl :#sug4 { if (!isIgnore(prefix, Ignore::Invite)) { QString channel(trailing); m_server->appendMessageToFrontmost(i18n("Invite"), i18n("%1 invited you to channel %2.", sourceNick, channel), messageTags ); emit invitation(sourceNick, channel); } } else if (command == QStringLiteral("away")) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); if (nickInfo) { if (!parameterList.isEmpty()) { nickInfo->setAway(true); nickInfo->setAwayMessage(parameterList.first()); } else { nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } } else { qDebug() << "Received away message for unknown nick," << sourceNick; } } else if (command == QStringLiteral("account") && plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); QString account = parameterList.first(); if (account == "*") { nickInfo->setAccount(QString()); } else { nickInfo->setAccount(account); } } else { qDebug() << "unknown client command" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); } } void InputFilter::parseServerCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags) { bool isNumeric; int numeric = command.toInt(&isNumeric); Q_ASSERT(m_server); if (!m_server) return; if (!isNumeric) { if (command == QStringLiteral("ping")) { QString text; text = (!trailing.isEmpty()) ? trailing : parameterList.join(QStringLiteral(" ")); if (!trailing.isEmpty()) { text = prefix + QStringLiteral(" :") + text; } if (!text.startsWith(QLatin1Char(' '))) { text.prepend(QLatin1Char(' ')); } // queue the reply to send it as soon as possible m_server->queue(QStringLiteral("PONG")+text, Server::HighPriority); } else if (command == QStringLiteral("error :closing link:")) { qDebug() << "link closed"; } else if (command == QStringLiteral("pong")) { // double check if we are in lag measuring mode since some servers fail to send // the LAG cookie back in PONG if (trailing.startsWith(QLatin1String("LAG")) || getLagMeasuring()) { m_server->pongReceived(); } } else if (command == QStringLiteral("mode")) { parseModes(prefix, parameterList, messageTags); } else if (command == QStringLiteral("notice")) { m_server->appendStatusMessage(i18n("Notice"), i18n("-%1- %2", prefix, trailing), messageTags); } else if (command == QStringLiteral("kick") && plHas(3)) { m_server->nickWasKickedFromChannel(parameterList.value(1), parameterList.value(2), prefix, trailing, messageTags); } else if (command == QStringLiteral("privmsg")) { parsePrivMsg(prefix, parameterList, messageTags); } else if (command==QStringLiteral("cap") && plHas(3)) { QString command = parameterList.value(1).toLower(); if (command == QStringLiteral("ack") || command == QStringLiteral("nak")) { m_server->capReply(); QStringList capabilities = parameterList.value(2).split(QLatin1Char(' '), QString::SkipEmptyParts); foreach(const QString& capability, capabilities) { int nameStart = capability.indexOf(QRegExp(QStringLiteral("[a-z0-9"), Qt::CaseInsensitive)); QString modifierString = capability.left(nameStart); QString name = capability.mid(nameStart); Server::CapModifiers modifiers = Server::NoModifiers; if (modifierString.contains(QLatin1Char('-'))) { modifiers = modifiers | Server::DisMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('='))) { modifiers = modifiers | Server::StickyMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('~'))) { modifiers = modifiers | Server::AckMod; modifiers = modifiers ^ Server::NoModifiers; } if (command == QStringLiteral("ack")) m_server->capAcknowledged(name, modifiers); else m_server->capDenied(name); } if(!m_server->capEndDelayed()) { m_server->capEndNegotiation(); } } else if (command == "ls" || command == "list") { if (getAutomaticRequest(QStringLiteral("CAP LS"), QString()) == 0) { m_server->appendStatusMessage(i18n("Capabilities"), parameterList.mid(2).join(QStringLiteral(" ")), messageTags); } else { setAutomaticRequest (QStringLiteral("CAP LS"), QString (), false); m_server->capInitiateNegotiation (parameterList.mid(2).join(QStringLiteral(" "))); } } } else if (command == QStringLiteral("authenticate") && plHas(1)) { if ((m_server->getLastAuthenticateCommand() == QStringLiteral("PLAIN") || m_server->getLastAuthenticateCommand() == QStringLiteral("EXTERNAL")) && parameterList.value(0) == QStringLiteral("+")) m_server->registerWithServices(); } // All yet unknown messages go into the frontmost window unaltered else { qDebug() << "unknown server command" << command; m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); } } else if (plHas(2)) //[0]==ourNick, [1] needs to be *something* { - //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 - //QString m_serverAssignedNick(parameterList.takeFirst()); - QString m_serverAssignedNick(parameterList.first()); + parseNumeric(prefix, numeric, parameterList, messageTags); + } // end of numeric elseif + else + { + qDebug() << "unknown message format" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); + } +} // end of server + +void InputFilter::parseModes(const QString &sourceNick, const QStringList ¶meterList, const QHash &messageTags) +{ + const QString modestring=parameterList.value(1); - switch (numeric) + if (!isAChannel(parameterList.value(0))) + { + QString message; + if (parameterList.value(0) == m_server->getNickname()) { - case RPL_WELCOME: - case RPL_YOURHOST: - case RPL_CREATED: - { - if (plHas(0)) //make the script happy - { - if (numeric == RPL_WELCOME) - { - QString host; + if (sourceNick == m_server->getNickname()) + { //XXX someone might care about the potentially unnecessary plural here + message = i18n("You have set personal modes: %1", modestring); + } + else + { //XXX someone might care about the potentially unnecessary plural here + message = i18n("%1 has changed your personal modes: %2", sourceNick, modestring); + } + } + if (!message.isEmpty()) + m_server->appendStatusMessage(i18n("Mode"), message, messageTags); + return; + } - if (trailing.contains(QStringLiteral("@"))) - host = trailing.section(QLatin1Char('@'), 1); + bool plus=false; + int parameterIndex=0; + // List of modes that need a parameter (note exception with -k and -l) + // Mode q is quiet on freenode and acts like b... if this is a channel mode on other + // networks then more logic is needed here. --MrGrim + //FIXME: The assumptions being made here about which modes have parameters and + // which don't strike me as very wrong, as some of these mode chars are used + // both as user and channel modes (e.g. A and O) and whether they use a para- + // meter can vary between those cases. This would seem to lead to trouble with + // the list indices down there. --hein, Thu Aug 6 19:48:02 CEST 2009 / r1008015 + QString parameterModes = QStringLiteral("aAoOvhkbleIq"); + QString message = i18n("%1 sets mode: %2", sourceNick, modestring); - // re-set nickname, since the server may have truncated it - if (m_serverAssignedNick != m_server->getNickname()) - { - m_server->renameNick(m_server->getNickname(), m_serverAssignedNick, messageTags); - } + for (int index=0;indexappendStatusMessage(i18n("Welcome"), trailing, messageTags); - } - break; - } - case RPL_MYINFO: + // Check if this is a mode or a +/- qualifier + if (mode=='+' || mode=='-') + { + plus=(mode=='+'); + } + else + { + // Check if this was a parameter mode + if (parameterModes.contains(QLatin1Char(mode))) { - if (plHas(5)) + // Check if the mode actually wants a parameter. -k and -l do not! + if (plus || (!plus && (mode!='k') && (mode!='l'))) { - m_server->appendStatusMessage(i18n("Welcome"), - i18n("Server %1 (Version %2), User modes: %3, Channel modes: %4", - parameterList.value(1), - parameterList.value(2), - parameterList.value(3), - parameterList.value(4)), - messageTags - ); - - QString allowed = m_server->allowedChannelModes(); - QString newModes = parameterList.value(4); - if(!allowed.isEmpty()) //attempt to merge the two - { - for(int i=0; i < allowed.length(); i++) - { - if(!newModes.contains(allowed.at(i))) - newModes.append(allowed.at(i)); - } - } - m_server->setAllowedChannelModes(newModes); + // Remember the mode parameter + parameter = parameterList.value(2+parameterIndex); + message += QLatin1Char(' ') + parameter; + // Switch to next parameter + ++parameterIndex; } - break; } - //case RPL_BOUNCE: // RFC 1459 name, now seems to be obsoleted by ... - case RPL_ISUPPORT: // ... DALnet RPL_ISUPPORT + // Let the channel update its modes + if (parameter.isEmpty()) // XXX Check this to ensure the braces are in the correct place { - if (plHas(0)) //make the script happy - { - m_server->appendStatusMessage(i18n("Support"), parameterList.join(QStringLiteral(" ")), messageTags); + qDebug() << "in updateChannelMode. sourceNick: '" << sourceNick << "' parameterlist: '" + << parameterList.join(QStringLiteral(", ")) << "'"; + } + m_server->updateChannelMode(sourceNick, parameterList.value(0), mode, plus, parameter, messageTags); + } + } // endfor - // The following behaviour is neither documented in RFC 1459 nor in 2810-2813 - // Nowadays, most ircds send server capabilities out via 005 (BOUNCE). - // refer to http://www.irc.org/tech_docs/005.html for a kind of documentation. - // More on http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt + if (Preferences::self()->useLiteralModes()) + { + m_server->appendCommandMessageToChannel(parameterList.value(0), i18n("Mode"), message, messageTags); + } +} - QStringList::const_iterator it = parameterList.constBegin(); - // don't want the user name - ++it; - for (; it != parameterList.constEnd(); ++it ) - { - QString property, value; - int pos; - if ((pos=(*it).indexOf( QLatin1Char('=') )) !=-1) - { - property = (*it).left(pos); - value = (*it).mid(pos+1); - } - else - { - property = *it; - } - if (property==QStringLiteral("PREFIX")) - { - pos = value.indexOf(QLatin1Char(')'),1); - if(pos==-1) - { - m_server->setPrefixes(QString(), value); - // XXX if ) isn't in the string, NOTHING should be there. anyone got a server - if (value.length() || property.length()) - m_server->appendStatusMessage(QString(), QStringLiteral("XXX Server sent bad PREFIX in RPL_ISUPPORT, please report."), messageTags); - } - else - { - m_server->setPrefixes(value.mid(1, pos-1), value.mid(pos+1)); - } - } - else if (property==QStringLiteral("CHANTYPES")) - { - m_server->setChannelTypes(value); - } - else if (property==QStringLiteral("MODES")) - { - if (!value.isEmpty()) - { - bool ok = false; - // If a value is given, it must be numeric. - int modesCount = value.toInt(&ok, 10); - if(ok) m_server->setModesCount(modesCount); - } - } - else if (property == QStringLiteral("CAPAB")) - { - // Disable as we don't use this for anything yet - //server->queue("CAPAB IDENTIFY-MSG"); - } - else if (property == QStringLiteral("CHANMODES")) - { - if(!value.isEmpty()) - { - m_server->setChanModes(value); - QString allowed = m_server->allowedChannelModes(); - QString newModes = value.remove(QLatin1Char(',')); - if(!allowed.isEmpty()) //attempt to merge the two - { - for(int i=0; i < allowed.length(); i++) - { - if(!newModes.contains(allowed.at(i))) - newModes.append(allowed.at(i)); - } - } - m_server->setAllowedChannelModes(newModes); - } - } - else if (property == QStringLiteral("TOPICLEN")) - { - if (!value.isEmpty()) - { - bool ok = false; - int topicLength = value.toInt(&ok); +// # & + and ! are *often*, but not necessarily, Channel identifiers. + and ! are non-RFC, +// so if a server doesn't offer 005 and supports + and ! channels, I think thats broken behaviour +// on their part - not ours. --Argonel +bool InputFilter::isAChannel(const QString &check) +{ + if (check.isEmpty()) + return false; + Q_ASSERT(m_server); + // if we ever see the assert, we need the ternary + return m_server? m_server->isAChannel(check) : bool(QString(QStringLiteral("#&")).contains(check.at(0))); +} - if (ok) - m_server->setTopicLength(topicLength); - } - } - else if (property == QStringLiteral("WHOX")) - { - m_server->setHasWHOX(true); - } - else - { - //qDebug() << "Ignored server-capability: " << property << " with value '" << value << "'"; - } - } // endfor - } - break; - } - case RPL_UMODEIS: +bool InputFilter::isIgnore(const QString &sender, Ignore::Type type) +{ + bool doIgnore = false; + + foreach (Ignore* item, Preferences::ignoreList()) + { + QRegExp ignoreItem(QRegExp::escape(item->getName()).replace(QStringLiteral("\\*"), QStringLiteral("(.*)")), Qt::CaseInsensitive); + if (ignoreItem.exactMatch(sender) && (item->getFlags() & type)) + doIgnore = true; + if (ignoreItem.exactMatch(sender) && (item->getFlags() & Ignore::Exception)) + return false; + } + + return doIgnore; +} + +void InputFilter::reset() +{ + m_automaticRequest.clear(); + m_whoRequestList.clear(); +} + +void InputFilter::setAutomaticRequest(const QString& command, const QString& name, bool yes) +{ + m_automaticRequest[command][name.toLower()] += (yes) ? 1 : -1; + if(m_automaticRequest[command][name.toLower()]<0) + { + qDebug() << "( " << command << ", " << name + << " ) was negative! Resetting!"; + m_automaticRequest[command][name.toLower()]=0; + } +} + +int InputFilter::getAutomaticRequest(const QString& command, const QString& name) +{ + return m_automaticRequest[command][name.toLower()]; +} + +void InputFilter::addWhoRequest(const QString& name) { m_whoRequestList << name.toLower(); } + +bool InputFilter::isWhoRequestUnderProcess(const QString& name) { return (m_whoRequestList.contains(name.toLower())>0); } + +void InputFilter::setLagMeasuring(bool state) { m_lagMeasuring=state; } + +bool InputFilter::getLagMeasuring() { return m_lagMeasuring; } + +void InputFilter::parsePrivMsg(const QString& prefix, QStringList& parameterList, const QHash &messageTags) +{ + int pos = prefix.indexOf(QLatin1Char('!')); + QString source; + QString sourceHostmask; + QString message(trailing); + + if(pos > 0) + { + source = prefix.left(pos); + sourceHostmask = prefix.mid(pos + 1); + } + else + { + source = prefix; + } + + Application* konv_app = Application::instance(); + message = konv_app->doAutoreplace(message, false).first; + + if(isAChannel(parameterList.value(0))) + { + if(!isIgnore(prefix, Ignore::Channel)) + { + Channel* channel = m_server->getChannelByName(parameterList.value(0)); + if(channel) { - if (plHas(0)) + QString label; + + if (m_server->getServerNickPrefixes().contains(parameterList.value(0).at(0))) { - // TODO check this one... I amputated + ' '+trailing - QString message=QString(QStringLiteral("%1 %2")).arg(i18n("Your personal modes are:")).arg(parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1)); - m_server->appendMessageToFrontmost(QStringLiteral("Info"), message, messageTags); + label = parameterList.value(0); } - break; - } - case RPL_CHANNELMODEIS: - { - if (plHas(2)) - { - const QString modeString=parameterList.value(2); // TEST this one was a 2 - // This is the string the user will see - QString modesAre; - QString message = i18n("Channel modes: ") + modeString; - int parameterCount=3; - QHash channelModesHash = Konversation::getChannelModesHash(); - for (int index=0;indexupdateChannelModeWidgets(parameterList.value(1), mode, parameter); - } - } // endfor - if (!modesAre.isEmpty() && Preferences::self()->useLiteralModes()) + channel->append(source, message, messageTags, label); + + if(source != m_server->getNickname()) + { + QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + + QRegExp::escape(m_server->loweredNickname()) + + QStringLiteral("([^\\d\\w]|$)")); + regexp.setCaseSensitivity(Qt::CaseInsensitive); + if(message.contains(regexp)) { - m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), message, messageTags); + konv_app->notificationHandler()->nick(channel, + source, message); } else { - m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), - i18n("Channel modes: ") + modesAre, messageTags); + konv_app->notificationHandler()->message(channel, + source, message); } } - break; - } - case RPL_CHANNELURLIS: - {// :niven.freenode.net 328 argonel #channel :http://www.buggeroff.com/ - if (plHas(3)) - { - m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("URL"), - i18n("Channel URL: %1", trailing), messageTags); - } - break; - } - case RPL_CHANNELCREATED: - { - if (plHas(3)) - { - QDateTime when; - when.setTime_t(parameterList.value(2).toUInt()); - m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Created"), - i18n("This channel was created on %1.", - QLocale().toString(when, QLocale::ShortFormat)), - messageTags - ); - } - break; } - case RPL_WHOISACCOUNT: - { - if (plHas(2)) - { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - if (nickInfo) - { - nickInfo->setIdentified(true); - } - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is logged in as %2.", parameterList.value(1), parameterList.value(2)), messageTags); - } + } + } + else + { + if(!isIgnore(prefix,Ignore::Query)) + { + QString queryName = source; + + // Handle znc.in/self-message correctly + if (source == m_server->getNickname()) + queryName = parameterList[0]; + + NickInfoPtr nickinfo = m_server->obtainNickInfo(queryName); + + if (queryName == source) + nickinfo->setHostmask(sourceHostmask); + + // Create a new query (server will check for dupes) + Query* query = m_server->addQuery(nickinfo, false /*we didn't initiate this*/ ); + + // send action to query + query->appendQuery(source, message, messageTags); + + if(source != m_server->getNickname() && query) + { + QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + + QRegExp::escape(m_server->loweredNickname()) + + QStringLiteral("([^\\d\\w]|$)")); + regexp.setCaseSensitivity(Qt::CaseInsensitive); + if(message.contains(regexp)) + { + konv_app->notificationHandler()->nick(query, + source, message); + } + else + { + konv_app->notificationHandler()->queryMessage(query, + source, message); } - break; } - //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 - case RPL_NAMREPLY: + } + } +} + +QHash InputFilter::parseMessageTags(const QString &line, int *startOfMessage) +{ + int index = line.indexOf(QLatin1Char(' ')); + *startOfMessage = index + 1; + QStringList tags = line.mid(1, index - 1).split(QLatin1Char(';')); + QHash tagHash; + + foreach(const QString &tag, tags) + { + QStringList tagList = tag.split(QLatin1Char('=')); + tagHash.insert(tagList.first(), tagList.last()); + } + + return tagHash; +} + +void InputFilter::parseNumeric(const QString &prefix, int command, QStringList ¶meterList, const QHash &messageTags) +{ + //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 + //QString m_serverAssignedNick(parameterList.takeFirst()); + QString m_serverAssignedNick(parameterList.first()); + + switch (command) + { + case RPL_WELCOME: + case RPL_YOURHOST: + case RPL_CREATED: + { + if (plHas(0)) //make the script happy { - if (plHas(4)) + if (command == RPL_WELCOME) { - QStringList nickList; + QString host; - if (!trailing.isEmpty()) - { - nickList = trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); - } - else if (parameterList.count() > 3) - { - for(int i = 3; i < parameterList.count(); i++) { - nickList.append(parameterList.value(i)); - } - } - else + if (trailing.contains(QStringLiteral("@"))) + host = trailing.section(QLatin1Char('@'), 1); + + // re-set nickname, since the server may have truncated it + if (m_serverAssignedNick != m_server->getNickname()) { - qDebug() << "Hmm seems something is broken... can't get to the names!"; + m_server->renameNick(m_server->getNickname(), m_serverAssignedNick, messageTags); } - // send list to channel - m_server->queueNicks(parameterList.value(2), nickList); // TEST this was a 2 + // Send the welcome signal, so the server class knows we are connected properly + emit welcome(host); + m_connecting = true; + } + m_server->appendStatusMessage(i18n("Welcome"), trailing, messageTags); + } + break; + } + case RPL_MYINFO: + { + if (plHas(5)) + { + m_server->appendStatusMessage(i18n("Welcome"), + i18n("Server %1 (Version %2), User modes: %3, Channel modes: %4", + parameterList.value(1), + parameterList.value(2), + parameterList.value(3), + parameterList.value(4)), + messageTags + ); - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(2)) == 0) + QString allowed = m_server->allowedChannelModes(); + QString newModes = parameterList.value(4); + if(!allowed.isEmpty()) //attempt to merge the two + { + for(int i=0; i < allowed.length(); i++) { - m_server->appendMessageToFrontmost(i18n("Names"), trailing, messageTags); + if(!newModes.contains(allowed.at(i))) + newModes.append(allowed.at(i)); } } - break; + m_server->setAllowedChannelModes(newModes); } - case RPL_ENDOFNAMES: + break; + } + //case RPL_BOUNCE: // RFC 1459 name, now seems to be obsoleted by ... + case RPL_ISUPPORT: // ... DALnet RPL_ISUPPORT + { + if (plHas(0)) //make the script happy { - if (plHas(2)) + m_server->appendStatusMessage(i18n("Support"), parameterList.join(QStringLiteral(" ")), messageTags); + + // The following behaviour is neither documented in RFC 1459 nor in 2810-2813 + // Nowadays, most ircds send server capabilities out via 005 (BOUNCE). + // refer to http://www.irc.org/tech_docs/005.html for a kind of documentation. + // More on http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt + + QStringList::const_iterator it = parameterList.constBegin(); + // don't want the user name + ++it; + for (; it != parameterList.constEnd(); ++it ) { - if (getAutomaticRequest(QStringLiteral("NAMES"),parameterList.value(1)) != 0) + QString property, value; + int pos; + if ((pos=(*it).indexOf( QLatin1Char('=') )) !=-1) { - // This code path was taken for the automatic NAMES input on JOIN, upcoming - // NAMES input for this channel will be manual invocations of /names - setAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(1), false); + property = (*it).left(pos); + value = (*it).mid(pos+1); } else { - m_server->appendMessageToFrontmost(i18n("Names"), i18n("End of NAMES list."), messageTags); + property = *it; } - - emit endOfNames(parameterList.value(1)); - } - break; - } - // Topic set messages - case RPL_NOTOPIC: - { - if (plHas(2)) - { - //this really has 3, but [2] is "No topic has been set" - m_server->appendMessageToFrontmost(i18n("TOPIC"), i18n("The channel %1 has no topic set.", parameterList.value(1)), messageTags); - } - break; - } - case RPL_TOPIC: - { - if (plHas(3)) - { - QString topic(trailing); - - // FIXME: This is an abuse of the automaticRequest system: We're - // using it in an inverted manner, i.e. the automaticRequest is - // set to true by a manual invocation of /topic. Bad bad bad - - // needs rethinking of automaticRequest. - if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) + if (property==QStringLiteral("PREFIX")) { - // Update channel window - m_server->setChannelTopic(parameterList.value(1), topic, messageTags); + pos = value.indexOf(QLatin1Char(')'),1); + if(pos==-1) + { + m_server->setPrefixes(QString(), value); + // XXX if ) isn't in the string, NOTHING should be there. anyone got a server + if (value.length() || property.length()) + m_server->appendStatusMessage(QString(), QStringLiteral("XXX Server sent bad PREFIX in RPL_ISUPPORT, please report."), messageTags); + } + else + { + m_server->setPrefixes(value.mid(1, pos-1), value.mid(pos+1)); + } } - else + else if (property==QStringLiteral("CHANTYPES")) { - m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The channel topic for %1 is: \"%2\"", parameterList.value(1), topic), messageTags); + m_server->setChannelTypes(value); } - } - break; - } - case RPL_TOPICSETBY: - { - if (plHas(4)) - { - // Inform user who set the topic and when - QDateTime when; - when.setTime_t(parameterList.value(3).toUInt()); - - // See FIXME in RPL_TOPIC - if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) + else if (property==QStringLiteral("MODES")) { - m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Topic"), - i18n("The topic was set by %1 on %2.", - parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), - messageTags, - false, - false); + if (!value.isEmpty()) + { + bool ok = false; + // If a value is given, it must be numeric. + int modesCount = value.toInt(&ok, 10); + if(ok) m_server->setModesCount(modesCount); + } } - else + else if (property == QStringLiteral("CAPAB")) { - m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The topic for %1 was set by %2 on %3.", - parameterList.value(1), - parameterList.value(2), - QLocale().toString(when, QLocale::ShortFormat)), messageTags, - false); - setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); + // Disable as we don't use this for anything yet + //server->queue("CAPAB IDENTIFY-MSG"); } - emit topicAuthor(parameterList.value(1), parameterList.value(2), when); - } - break; - } - case RPL_WHOISACTUALLY: - { - if (plHas(3)) - { - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"),parameterList.value(1)) == 0) + else if (property == QStringLiteral("CHANMODES")) { - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is actually using the host %2.", parameterList.value(1), parameterList.value(2)), messageTags); + if(!value.isEmpty()) + { + m_server->setChanModes(value); + QString allowed = m_server->allowedChannelModes(); + QString newModes = value.remove(QLatin1Char(',')); + if(!allowed.isEmpty()) //attempt to merge the two + { + for(int i=0; i < allowed.length(); i++) + { + if(!newModes.contains(allowed.at(i))) + newModes.append(allowed.at(i)); + } + } + m_server->setAllowedChannelModes(newModes); + } } - } - break; - } - case ERR_NOSUCHNICK: - { - if (plHas(2)) - { - // Display slightly different error message in case we performed a WHOIS for - // IP resolve purposes, and clear it from the automaticRequest list - if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) == 0) + else if (property == QStringLiteral("TOPICLEN")) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such nick/channel.", parameterList.value(1)), messageTags); + if (!value.isEmpty()) + { + bool ok = false; + int topicLength = value.toInt(&ok); + + if (ok) + m_server->setTopicLength(topicLength); + } } - else if(getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) //Display message only if this was not an automatic request. + else if (property == QStringLiteral("WHOX")) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such nick: %1.", parameterList.value(1)), messageTags); - setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); + m_server->setHasWHOX(true); + } + else + { + //qDebug() << "Ignored server-capability: " << property << " with value '" << value << "'"; } - } - break; + } // endfor } - case ERR_NOSUCHCHANNEL: + break; + } + case RPL_UMODEIS: + { + if (plHas(0)) { - if (plHas(2)) - { - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such channel.", parameterList.value(1)), messageTags); - } - } - break; + // TODO check this one... I amputated + ' '+trailing + QString message=QString(QStringLiteral("%1 %2")).arg(i18n("Your personal modes are:")).arg(parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1)); + m_server->appendMessageToFrontmost(QStringLiteral("Info"), message, messageTags); } - // Nick already on the server, so try another one - case ERR_NICKNAMEINUSE: + break; + } + case RPL_CHANNELMODEIS: + { + if (plHas(2)) { - if (plHas(1)) + const QString modeString=parameterList.value(2); // TEST this one was a 2 + // This is the string the user will see + QString modesAre; + QString message = i18n("Channel modes: ") + modeString; + int parameterCount=3; + QHash channelModesHash = Konversation::getChannelModesHash(); + for (int index=0;indexisConnected()) // Show message + QString parameter; + char mode(modeString[index].toLatin1()); + if(mode!='+') { - m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use, try a different one."), messageTags); - } - else // not connected yet, so try to find a nick that's not in use - { - // Get the next nick from the list or ask for a new one - QString newNick = m_server->getNextNickname(); + if(!modesAre.isEmpty()) + modesAre+=QStringLiteral(", "); - // The user chose to disconnect... - if (newNick.isNull()) + if(mode=='k') { - if (m_server->isConnecting()) // ... or did they? - m_server->disconnectServer(); - else // No they didn't! - m_server->appendMessageToFrontmost(i18n("Info"), i18n("The nickname %1 was already in use, but the connection failed before you responded.", m_server->getNickname()), messageTags); + parameter=parameterList.value(parameterCount++); + message += QLatin1Char(' ') + parameter; + modesAre+=i18n("password protected"); } - else + else if(mode=='l') { - // Update Server window - m_server->obtainNickInfo(m_server->getNickname()) ; - m_server->renameNick(m_server->getNickname(), newNick, messageTags); - // Show message - m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use. Trying %1.", newNick), messageTags); - // Send nickchange request to the server - m_server->queue(QStringLiteral("NICK ")+newNick); + parameter=parameterList.value(parameterCount++); + message += QLatin1Char(' ') + parameter; + modesAre+=i18np("limited to %1 user", "limited to %1 users", parameter.toInt()); } - } - } - break; - } - case ERR_ERRONEUSNICKNAME: - { - if (plHas(1)) - { - if (m_server->isConnected()) - { // We are already connected. Just print the error message - m_server->appendMessageToFrontmost(i18n("Nick"), trailing, messageTags); - } - else // Find a new nick as in ERR_NICKNAMEINUSE - { - QString newNick = m_server->getNextNickname(); - - // The user chose to disconnect - if (newNick.isNull()) + else if(channelModesHash.contains(QLatin1Char(mode))) { - m_server->disconnectServer(); + modesAre+=channelModesHash.value(QLatin1Char(mode)); } else { - m_server->obtainNickInfo(m_server->getNickname()) ; - m_server->renameNick(m_server->getNickname(), newNick, messageTags); - m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Erroneous nickname. Changing nick to %1.", newNick), messageTags); - m_server->queue(QStringLiteral("NICK ")+newNick); + modesAre+=QLatin1Char(mode); } + m_server->updateChannelModeWidgets(parameterList.value(1), mode, parameter); } + } // endfor + if (!modesAre.isEmpty() && Preferences::self()->useLiteralModes()) + { + m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), message, messageTags); } - break; - } - case ERR_NOTONCHANNEL: - { - if (plHas(2)) + else { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("You are not on %1.", parameterList.value(1)), messageTags); - setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); - + m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), + i18n("Channel modes: ") + modesAre, messageTags); } - break; } - case RPL_MOTDSTART: + break; + } + case RPL_CHANNELURLIS: + {// :niven.freenode.net 328 argonel #channel :http://www.buggeroff.com/ + if (plHas(3)) { - if (plHas(1)) - { - if (!m_connecting || !Preferences::self()->skipMOTD()) - m_server->appendStatusMessage(i18n("MOTD"), i18n("Message of the day:"), messageTags); - } - break; + m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("URL"), + i18n("Channel URL: %1", trailing), messageTags); + } + break; + } + case RPL_CHANNELCREATED: + { + if (plHas(3)) + { + QDateTime when; + when.setTime_t(parameterList.value(2).toUInt()); + m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Created"), + i18n("This channel was created on %1.", + QLocale().toString(when, QLocale::ShortFormat)), + messageTags + ); } - case RPL_MOTD: + break; + } + case RPL_WHOISACCOUNT: + { + if (plHas(2)) { - if (plHas(2)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + if (nickInfo) + { + nickInfo->setIdentified(true); + } + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - if (!m_connecting || !Preferences::self()->skipMOTD()) - m_server->appendStatusMessage(i18n("MOTD"), trailing, messageTags); + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is logged in as %2.", parameterList.value(1), parameterList.value(2)), messageTags); } - break; } - case RPL_ENDOFMOTD: + break; + } + //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 + case RPL_NAMREPLY: + { + if (plHas(4)) { - if (plHas(1)) + QStringList nickList; + + if (!trailing.isEmpty()) + { + nickList = trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); + } + else if (parameterList.count() > 3) + { + for(int i = 3; i < parameterList.count(); i++) { + nickList.append(parameterList.value(i)); + } + } + else { - if (!m_connecting || !Preferences::self()->skipMOTD()) - m_server->appendStatusMessage(i18n("MOTD"), i18n("End of message of the day"), messageTags); + qDebug() << "Hmm seems something is broken... can't get to the names!"; + } - if (m_connecting) - m_server->autoCommandsAndChannels(); + // send list to channel + m_server->queueNicks(parameterList.value(2), nickList); // TEST this was a 2 - m_connecting = false; + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(2)) == 0) + { + m_server->appendMessageToFrontmost(i18n("Names"), trailing, messageTags); } - break; } - case ERR_NOMOTD: + break; + } + case RPL_ENDOFNAMES: + { + if (plHas(2)) { - if (plHas(1)) + if (getAutomaticRequest(QStringLiteral("NAMES"),parameterList.value(1)) != 0) { - if (m_connecting) - m_server->autoCommandsAndChannels(); - - m_connecting = false; + // This code path was taken for the automatic NAMES input on JOIN, upcoming + // NAMES input for this channel will be manual invocations of /names + setAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(1), false); } - break; - } - case ERR_CHANOPRIVSNEEDED: - { - if (plHas(2)) + else { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("You need to be a channel operator in %1 to do that.", parameterList.value(1)), messageTags); + m_server->appendMessageToFrontmost(i18n("Names"), i18n("End of NAMES list."), messageTags); } - break; + + emit endOfNames(parameterList.value(1)); } - case RPL_YOUREOPER: + break; + } + // Topic set messages + case RPL_NOTOPIC: + { + if (plHas(2)) { - if (plHas(1)) - { - m_server->appendMessageToFrontmost(i18n("Notice"), i18n("You are now an IRC operator on this server."), messageTags); - } - break; + //this really has 3, but [2] is "No topic has been set" + m_server->appendMessageToFrontmost(i18n("TOPIC"), i18n("The channel %1 has no topic set.", parameterList.value(1)), messageTags); } - case RPL_HOSTHIDDEN: + break; + } + case RPL_TOPIC: + { + if (plHas(3)) { - if (plHas(2)) + QString topic(trailing); + + // FIXME: This is an abuse of the automaticRequest system: We're + // using it in an inverted manner, i.e. the automaticRequest is + // set to true by a manual invocation of /topic. Bad bad bad - + // needs rethinking of automaticRequest. + if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) { - m_server->appendStatusMessage(i18n("Info"), i18n("'%1' is now your hidden host (set by services).", parameterList.value(1)), messageTags); + // Update channel window + m_server->setChannelTopic(parameterList.value(1), topic, messageTags); } - break; - } - case RPL_GLOBALUSERS: // Current global users: 589 Max: 845 - { - if (plHas(2)) + else { - QString current(trailing.section(QLatin1Char(' '),3)); - //QString max(trailing.section(' ',5,5)); - m_server->appendStatusMessage(i18n("Users"), i18n("Current users on the network: %1", current), messageTags); + m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The channel topic for %1 is: \"%2\"", parameterList.value(1), topic), messageTags); } - break; } - case RPL_LOCALUSERS: // Current local users: 589 Max: 845 + break; + } + case RPL_TOPICSETBY: + { + if (plHas(4)) { - if (plHas(2)) + // Inform user who set the topic and when + QDateTime when; + when.setTime_t(parameterList.value(3).toUInt()); + + // See FIXME in RPL_TOPIC + if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) + { + m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Topic"), + i18n("The topic was set by %1 on %2.", + parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), + messageTags, + false, + false); + } + else { - QString current(trailing.section(QLatin1Char(' '), 3)); - //QString max(trailing.section(' ',5,5)); - m_server->appendStatusMessage(i18n("Users"), i18n("Current users on %1: %2.", prefix, current), messageTags); + m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The topic for %1 was set by %2 on %3.", + parameterList.value(1), + parameterList.value(2), + QLocale().toString(when, QLocale::ShortFormat)), messageTags, + false); + setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); } - break; + emit topicAuthor(parameterList.value(1), parameterList.value(2), when); } - case RPL_ISON: + break; + } + case RPL_WHOISACTUALLY: + { + if (plHas(3)) { - if (plHas(2)) + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"),parameterList.value(1)) == 0) { - // Tell server to start the next notify timer round - emit notifyResponse(trailing); + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is actually using the host %2.", parameterList.value(1), parameterList.value(2)), messageTags); } - break; } - case RPL_AWAY: + break; + } + case ERR_NOSUCHNICK: + { + if (plHas(2)) { - if (plHas(3)) + // Display slightly different error message in case we performed a WHOIS for + // IP resolve purposes, and clear it from the automaticRequest list + if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) == 0) { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - if (nickInfo) - { - nickInfo->setAway(true); - if (nickInfo->getAwayMessage() != trailing) // FIXME i think this check should be in the setAwayMessage method - { - nickInfo->setAwayMessage(trailing); - // TEST this used to skip the automatic request handler below - } - } - - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Away"), - i18n("%1 is away: %2", parameterList.value(1), trailing), messageTags - ); - } + m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such nick/channel.", parameterList.value(1)), messageTags); + } + else if(getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) //Display message only if this was not an automatic request. + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such nick: %1.", parameterList.value(1)), messageTags); + setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } - break; } - case RPL_INVITING: + break; + } + case ERR_NOSUCHCHANNEL: + { + if (plHas(2)) { - if (plHas(3)) + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - m_server->appendMessageToFrontmost(i18n("Invite"), - i18n("You invited %1 to channel %2.", - parameterList.value(1), parameterList.value(2)), messageTags - ); + m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such channel.", parameterList.value(1)), messageTags); } - break; } - //Sample WHOIS response - //"/WHOIS psn" - //[19:11] :zahn.freenode.net 311 PhantomsDad psn ~psn h106n2fls23o1068.bredband.comhem.se * :Peter Simonsson - //[19:11] :zahn.freenode.net 319 PhantomsDad psn :#kde-devel #koffice - //[19:11] :zahn.freenode.net 312 PhantomsDad psn irc.freenode.net :http://freenode.net/ - //[19:11] :zahn.freenode.net 301 PhantomsDad psn :away - //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user - //[19:11] :zahn.freenode.net 317 PhantomsDad psn 4921 1074973024 :seconds idle, signon time - //[19:11] :zahn.freenode.net 318 PhantomsDad psn :End of /WHOIS list. - case RPL_WHOISUSER: + break; + } + // Nick already on the server, so try another one + case ERR_NICKNAMEINUSE: + { + if (plHas(1)) { - if (plHas(4)) + // if we are already connected, don't try tro find another nick ourselves + if (m_server->isConnected()) // Show message { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - if (nickInfo) - { - nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); - nickInfo->setRealName(trailing); - } - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - // escape html tags - QString escapedRealName(trailing); + m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use, try a different one."), messageTags); + } + else // not connected yet, so try to find a nick that's not in use + { + // Get the next nick from the list or ask for a new one + QString newNick = m_server->getNextNickname(); - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is %2@%3 (%4)", - parameterList.value(1), - parameterList.value(2), - parameterList.value(3), - escapedRealName), messageTags, false); // Don't parse any urls + // The user chose to disconnect... + if (newNick.isNull()) + { + if (m_server->isConnecting()) // ... or did they? + m_server->disconnectServer(); + else // No they didn't! + m_server->appendMessageToFrontmost(i18n("Info"), i18n("The nickname %1 was already in use, but the connection failed before you responded.", m_server->getNickname()), messageTags); } else { - // This WHOIS was requested by Server for DNS resolve purposes; try to resolve the host - if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) != 0) - { - QHostInfo resolved = QHostInfo::fromName(parameterList.value(3)); - if (resolved.error() == QHostInfo::NoError && !resolved.addresses().isEmpty()) - { - QString ip = resolved.addresses().first().toString(); - m_server->appendMessageToFrontmost(i18n("DNS"), - i18n("Resolved %1 (%2) to address: %3", - parameterList.value(1), - parameterList.value(3), - ip), messageTags - ); - } - else - { - m_server->appendMessageToFrontmost(i18n("Error"), - i18n("Unable to resolve address for %1 (%2)", - parameterList.value(1), - parameterList.value(3)), messageTags - ); - } - - // Clear this from the automaticRequest list so it works repeatedly - setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); - } + // Update Server window + m_server->obtainNickInfo(m_server->getNickname()) ; + m_server->renameNick(m_server->getNickname(), newNick, messageTags); + // Show message + m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use. Trying %1.", newNick), messageTags); + // Send nickchange request to the server + m_server->queue(QStringLiteral("NICK ")+newNick); } } - break; } - // From a WHOIS. - //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user - case RPL_WHOISIDENTIFY: - case RPL_IDENTIFIED: + break; + } + case ERR_ERRONEUSNICKNAME: + { + if (plHas(1)) { - if (plHas(2)) + if (m_server->isConnected()) + { // We are already connected. Just print the error message + m_server->appendMessageToFrontmost(i18n("Nick"), trailing, messageTags); + } + else // Find a new nick as in ERR_NICKNAMEINUSE { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - if (nickInfo) + QString newNick = m_server->getNextNickname(); + + // The user chose to disconnect + if (newNick.isNull()) { - nickInfo->setIdentified(true); + m_server->disconnectServer(); } - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + else { - // Prints "psn is an identified user" - //server->appendStatusMessage(i18n("Whois"),parameterList.join(" ").section(' ',1)+' '+trailing); - // The above line works fine, but can't be i18n'ised. So use the below instead.. I hope this is okay. - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an identified user.", parameterList.value(1)), messageTags); + m_server->obtainNickInfo(m_server->getNickname()) ; + m_server->renameNick(m_server->getNickname(), newNick, messageTags); + m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Erroneous nickname. Changing nick to %1.", newNick), messageTags); + m_server->queue(QStringLiteral("NICK ")+newNick); } } - break; } - case RPL_WHOISSECURE: + break; + } + case ERR_NOTONCHANNEL: + { + if (plHas(2)) { - if (plHas(2)) - { - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is using a secure connection.", parameterList.value(1)), messageTags); - } - break; + m_server->appendMessageToFrontmost(i18n("Error"), i18n("You are not on %1.", parameterList.value(1)), messageTags); + setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); + } - // Sample WHO response - //"/WHO #lounge" - //[21:39] [352] #lounge jasmine bots.worldforge.org irc.worldforge.org jasmine H 0 jasmine - //[21:39] [352] #lounge ~Nottingha worldforge.org irc.worldforge.org SherwoodSpirit H 0 Arboreal Entity - case RPL_WHOSPCRPL: - case RPL_WHOREPLY: + break; + } + case RPL_MOTDSTART: + { + if (plHas(1)) { - if (plHas(6)) - { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(5)); - // G=away G@=away,op G+=away,voice - bool bAway = parameterList.value(6).toUpper().startsWith(QLatin1Char('G')); - QString realName = trailing; + if (!m_connecting || !Preferences::self()->skipMOTD()) + m_server->appendStatusMessage(i18n("MOTD"), i18n("Message of the day:"), messageTags); + } + break; + } + case RPL_MOTD: + { + if (plHas(2)) + { + if (!m_connecting || !Preferences::self()->skipMOTD()) + m_server->appendStatusMessage(i18n("MOTD"), trailing, messageTags); + } + break; + } + case RPL_ENDOFMOTD: + { + if (plHas(1)) + { + if (!m_connecting || !Preferences::self()->skipMOTD()) + m_server->appendStatusMessage(i18n("MOTD"), i18n("End of message of the day"), messageTags); - if (realName.indexOf (QRegExp(QStringLiteral("\\d\\s"))) == 0) - realName = realName.mid (2); + if (m_connecting) + m_server->autoCommandsAndChannels(); - if (nickInfo) - { - nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); - nickInfo->setRealName(realName); - nickInfo->setAway(bAway); - if(!bAway) - { - nickInfo->setAwayMessage(QString()); - } + m_connecting = false; + } + break; + } + case ERR_NOMOTD: + { + if (plHas(1)) + { + if (m_connecting) + m_server->autoCommandsAndChannels(); - if(m_server->capabilities() & Server::WHOX && m_server->capabilities() & Server::ExtendedJoin) - { - nickInfo->setAccount(parameterList.value(8)); - } - } - // Display message only if this was not an automatic request. - if (!m_whoRequestList.isEmpty()) // for safe - { - if (getAutomaticRequest(QStringLiteral("WHO"),m_whoRequestList.front())==0) - { - m_server->appendMessageToFrontmost(i18n("Who"), - i18n("%1 is %2@%3 (%4)%5", parameterList.value(5), - parameterList.value(2), - parameterList.value(3), - realName, - bAway?i18n(" (Away)"):QString()), messageTags, - false); // Don't parse as url - } - } - } - break; + m_connecting = false; } - case RPL_ENDOFWHO: + break; + } + case ERR_CHANOPRIVSNEEDED: + { + if (plHas(2)) + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("You need to be a channel operator in %1 to do that.", parameterList.value(1)), messageTags); + } + break; + } + case RPL_YOUREOPER: + { + if (plHas(1)) + { + m_server->appendMessageToFrontmost(i18n("Notice"), i18n("You are now an IRC operator on this server."), messageTags); + } + break; + } + case RPL_HOSTHIDDEN: + { + if (plHas(2)) + { + m_server->appendStatusMessage(i18n("Info"), i18n("'%1' is now your hidden host (set by services).", parameterList.value(1)), messageTags); + } + break; + } + case RPL_GLOBALUSERS: // Current global users: 589 Max: 845 + { + if (plHas(2)) + { + QString current(trailing.section(QLatin1Char(' '),3)); + //QString max(trailing.section(' ',5,5)); + m_server->appendStatusMessage(i18n("Users"), i18n("Current users on the network: %1", current), messageTags); + } + break; + } + case RPL_LOCALUSERS: // Current local users: 589 Max: 845 + { + if (plHas(2)) + { + QString current(trailing.section(QLatin1Char(' '), 3)); + //QString max(trailing.section(' ',5,5)); + m_server->appendStatusMessage(i18n("Users"), i18n("Current users on %1: %2.", prefix, current), messageTags); + } + break; + } + case RPL_ISON: + { + if (plHas(2)) + { + // Tell server to start the next notify timer round + emit notifyResponse(trailing); + } + break; + } + case RPL_AWAY: + { + if (plHas(3)) { - if (plHas(2)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + if (nickInfo) { - if (!m_whoRequestList.isEmpty()) - { - const QString param = parameterList.value(1).toLower(); - // for safety - const int idx = m_whoRequestList.indexOf(param); - if (idx > -1) - { - if (getAutomaticRequest(QStringLiteral("WHO"), param) == 0) - { - m_server->appendMessageToFrontmost(i18n("Who"), - i18n("End of /WHO list for %1", - parameterList.value(1)), messageTags); - } - else - { - setAutomaticRequest(QStringLiteral("WHO"), param, false); - } - - m_whoRequestList.removeAt(idx); - } - else - { - // whoRequestList seems to be broken. - qDebug() << "RPL_ENDOFWHO: malformed ENDOFWHO. retrieved: " - << parameterList.value(1) << " expected: " << m_whoRequestList.front(); - m_whoRequestList.clear(); - } - } - else + nickInfo->setAway(true); + if (nickInfo->getAwayMessage() != trailing) // FIXME i think this check should be in the setAwayMessage method { - qDebug() << "RPL_ENDOFWHO: unexpected ENDOFWHO. retrieved: " - << parameterList.value(1); + nickInfo->setAwayMessage(trailing); + // TEST this used to skip the automatic request handler below } + } - emit endOfWho(parameterList.value(1)); + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + { + m_server->appendMessageToFrontmost(i18n("Away"), + i18n("%1 is away: %2", parameterList.value(1), trailing), messageTags + ); } - break; } - case RPL_WHOISCHANNELS: + break; + } + case RPL_INVITING: + { + if (plHas(3)) + { + m_server->appendMessageToFrontmost(i18n("Invite"), + i18n("You invited %1 to channel %2.", + parameterList.value(1), parameterList.value(2)), messageTags + ); + } + break; + } + //Sample WHOIS response + //"/WHOIS psn" + //[19:11] :zahn.freenode.net 311 PhantomsDad psn ~psn h106n2fls23o1068.bredband.comhem.se * :Peter Simonsson + //[19:11] :zahn.freenode.net 319 PhantomsDad psn :#kde-devel #koffice + //[19:11] :zahn.freenode.net 312 PhantomsDad psn irc.freenode.net :http://freenode.net/ + //[19:11] :zahn.freenode.net 301 PhantomsDad psn :away + //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user + //[19:11] :zahn.freenode.net 317 PhantomsDad psn 4921 1074973024 :seconds idle, signon time + //[19:11] :zahn.freenode.net 318 PhantomsDad psn :End of /WHOIS list. + case RPL_WHOISUSER: + { + if (plHas(4)) { - if (plHas(3)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + if (nickInfo) { - QStringList userChannels,voiceChannels,opChannels,halfopChannels,ownerChannels,adminChannels; - - // get a list of all channels the user is in - QStringList channelList=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); - channelList.sort(); + nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); + nickInfo->setRealName(trailing); + } + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + { + // escape html tags + QString escapedRealName(trailing); - // split up the list in channels where they are operator / user / voice - for (int index=0; index < channelList.count(); index++) - { - QString lookChannel=channelList[index]; - if (lookChannel.startsWith(QLatin1Char('*')) || lookChannel.startsWith(QLatin1Char('&'))) - { - adminChannels.append(lookChannel.mid(1)); - m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 16); - } - // See bug #97354 part 2 - else if((lookChannel.startsWith(QLatin1Char('!')) || lookChannel.startsWith(QLatin1Char('~'))) && m_server->isAChannel(lookChannel.mid(1))) - { - ownerChannels.append(lookChannel.mid(1)); - m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 8); - } - // See bug #97354 part 1 - else if (lookChannel.startsWith(QStringLiteral("@+"))) - { - opChannels.append(lookChannel.mid(2)); - m_server->setChannelNick(lookChannel.mid(2), parameterList.value(1), 4); - } - else if (lookChannel.startsWith(QLatin1Char('@'))) - { - opChannels.append(lookChannel.mid(1)); - m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 4); - } - else if (lookChannel.startsWith(QLatin1Char('%'))) - { - halfopChannels.append(lookChannel.mid(1)); - m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 2); - } - else if (lookChannel.startsWith(QLatin1Char('+'))) - { - voiceChannels.append(lookChannel.mid(1)); - m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 1); - } - else - { - userChannels.append(lookChannel); - m_server->setChannelNick(lookChannel, parameterList.value(1), 0); - } - } // endfor - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is %2@%3 (%4)", + parameterList.value(1), + parameterList.value(2), + parameterList.value(3), + escapedRealName), messageTags, false); // Don't parse any urls + } + else + { + // This WHOIS was requested by Server for DNS resolve purposes; try to resolve the host + if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) != 0) { - if (userChannels.count()) + QHostInfo resolved = QHostInfo::fromName(parameterList.value(3)); + if (resolved.error() == QHostInfo::NoError && !resolved.addresses().isEmpty()) { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is a user on channels: %2", + QString ip = resolved.addresses().first().toString(); + m_server->appendMessageToFrontmost(i18n("DNS"), + i18n("Resolved %1 (%2) to address: %3", parameterList.value(1), - userChannels.join(QStringLiteral(" "))), messageTags - ); - } - if (voiceChannels.count()) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 has voice on channels: %2", - parameterList.value(1), voiceChannels.join(QStringLiteral(" "))), messageTags - ); - } - if (halfopChannels.count()) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is a halfop on channels: %2", - parameterList.value(1), halfopChannels.join(QStringLiteral(" "))), messageTags - ); - } - if (opChannels.count()) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is an operator on channels: %2", - parameterList.value(1), opChannels.join(QStringLiteral(" "))), messageTags - ); - } - if (ownerChannels.count()) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is owner of channels: %2", - parameterList.value(1), ownerChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(3), + ip), messageTags ); } - if (adminChannels.count()) + else { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is admin on channels: %2", - parameterList.value(1), adminChannels.join(QStringLiteral(" "))), messageTags + m_server->appendMessageToFrontmost(i18n("Error"), + i18n("Unable to resolve address for %1 (%2)", + parameterList.value(1), + parameterList.value(3)), messageTags ); } + + // Clear this from the automaticRequest list so it works repeatedly + setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } } - break; } - case RPL_WHOISSERVER: + break; + } + // From a WHOIS. + //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user + case RPL_WHOISIDENTIFY: + case RPL_IDENTIFIED: + { + if (plHas(2)) { - if (plHas(4)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + if (nickInfo) { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - if (nickInfo) - { - nickInfo->setNetServer(parameterList.value(2)); - nickInfo->setNetServerInfo(trailing); - // Clear the away state on assumption that if nick is away, this message will be followed - // by a 301 RPL_AWAY message. Not necessary a invalid assumption, but what can we do? - nickInfo->setAway(false); - nickInfo->setAwayMessage(QString()); - } - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is online via %2 (%3).", parameterList.value(1), - parameterList.value(2), trailing), messageTags - ); - } + nickInfo->setIdentified(true); } - break; - } - case RPL_WHOISHELPER: - { - if (plHas(2)) + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 is available for help.", - parameterList.value(1)), messageTags - ); - } + // Prints "psn is an identified user" + //server->appendStatusMessage(i18n("Whois"),parameterList.join(" ").section(' ',1)+' '+trailing); + // The above line works fine, but can't be i18n'ised. So use the below instead.. I hope this is okay. + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an identified user.", parameterList.value(1)), messageTags); } - break; } - case RPL_WHOISOPERATOR: + break; + } + case RPL_WHOISSECURE: + { + if (plHas(2)) { - if (plHas(2)) - { - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - if (trailing.toLower().simplified().startsWith(QLatin1String("is an irc operator"))) - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an IRC Operator.", parameterList.value(1)), messageTags); - else - m_server->appendMessageToFrontmost(i18n("Whois"), QString(QStringLiteral("%1 %2")).arg(parameterList.value(1)).arg(trailing), messageTags); - } - } - break; + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is using a secure connection.", parameterList.value(1)), messageTags); } - case RPL_WHOISIDLE: + break; + } + // Sample WHO response + //"/WHO #lounge" + //[21:39] [352] #lounge jasmine bots.worldforge.org irc.worldforge.org jasmine H 0 jasmine + //[21:39] [352] #lounge ~Nottingha worldforge.org irc.worldforge.org SherwoodSpirit H 0 Arboreal Entity + case RPL_WHOSPCRPL: + case RPL_WHOREPLY: + { + if (plHas(6)) { - if (plHas(3)) - { - // get idle time in seconds - bool ok = false; - long seconds = parameterList.value(2).toLong(&ok); - if (!ok) break; - - long minutes = seconds/60; - long hours = minutes/60; - long days = hours/24; - - QDateTime signonTime; - uint signonTimestamp = parameterList.value(3).toUInt(&ok); + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(5)); + // G=away G@=away,op G+=away,voice + bool bAway = parameterList.value(6).toUpper().startsWith(QLatin1Char('G')); + QString realName = trailing; - if (ok && parameterList.count() == 5) - signonTime.setTime_t(signonTimestamp); + if (realName.indexOf (QRegExp(QStringLiteral("\\d\\s"))) == 0) + realName = realName.mid (2); - if (!signonTime.isNull()) + if (nickInfo) + { + nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); + nickInfo->setRealName(realName); + nickInfo->setAway(bAway); + if(!bAway) { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - - if (nickInfo) - nickInfo->setOnlineSince(signonTime); + nickInfo->setAwayMessage(QString()); } - // if idle time is longer than a day - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + if(m_server->capabilities() & Server::WHOX && m_server->capabilities() & Server::ExtendedJoin) { - if (days) - { - const QString daysString = i18np("1 day", "%1 days", days); - const QString hoursString = i18np("1 hour", "%1 hours", (hours % 24)); - const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); - const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); - - m_server->appendMessageToFrontmost(i18n("Whois"), - i18nc("%1 = name of person, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)", - "%1 has been idle for %2, %3, %4, and %5.", - parameterList.value(1), - daysString, hoursString, minutesString, secondsString), messageTags); - // or longer than an hour - } - else if (hours) - { - const QString hoursString = i18np("1 hour", "%1 hours", hours); - const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); - const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); - m_server->appendMessageToFrontmost(i18n("Whois"), - i18nc("%1 = name of person, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)", - "%1 has been idle for %2, %3, and %4.", parameterList.value(1), hoursString, - minutesString, secondsString), messageTags); - // or longer than a minute - } - else if (minutes) - { - const QString minutesString = i18np("1 minute", "%1 minutes", minutes); - const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); - m_server->appendMessageToFrontmost(i18n("Whois"), - i18nc("%1 = name of person, %2 = (x minutes), %3 = (x seconds)", - "%1 has been idle for %2 and %3.", parameterList.value(1), minutesString, secondsString), messageTags); - // or just some seconds - } - else - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18np("%2 has been idle for 1 second.", "%2 has been idle for %1 seconds.", seconds, parameterList.value(1)), messageTags); - } - - if (!signonTime.isNull()) - { - m_server->appendMessageToFrontmost(i18n("Whois"), - i18n("%1 has been online since %2.", - parameterList.value(1), QLocale().toString(signonTime, QLocale::ShortFormat)), messageTags); - } + nickInfo->setAccount(parameterList.value(8)); } } - break; - } - case RPL_ENDOFWHOIS: - { - if (plHas(2)) + // Display message only if this was not an automatic request. + if (!m_whoRequestList.isEmpty()) // for safe { - /*FIXME why is the nickinfo line below commented out? - //NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); - */ - // Display message only if this was not an automatic request. - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) - { - m_server->appendMessageToFrontmost(i18n("Whois"), i18n("End of WHOIS list."), messageTags); - } - // was this an automatic request? - if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) + if (getAutomaticRequest(QStringLiteral("WHO"),m_whoRequestList.front())==0) { - setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); + m_server->appendMessageToFrontmost(i18n("Who"), + i18n("%1 is %2@%3 (%4)%5", parameterList.value(5), + parameterList.value(2), + parameterList.value(3), + realName, + bAway?i18n(" (Away)"):QString()), messageTags, + false); // Don't parse as url } } - break; } - case RPL_USERHOST: + break; + } + case RPL_ENDOFWHO: + { + if (plHas(2)) { - if (plHas(2)) + if (!m_whoRequestList.isEmpty()) { - // iterate over all nick/masks in reply - QStringList uhosts=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); - - for (int index=0;index -1) { - // extract nickname and hostmask from reply - QString nick(uhosts[index].section(QLatin1Char('='),0,0)); - QString mask(uhosts[index].section(QLatin1Char('='),1)); - - // get away and IRC operator flags - bool away=(mask[0]==QLatin1Char('-')); - bool ircOp=(nick[nick.length()-1]==QLatin1Char('*')); - - // cut flags from nick/hostmask - mask=mask.mid(1); - if (ircOp) + if (getAutomaticRequest(QStringLiteral("WHO"), param) == 0) { - nick=nick.left(nick.length()-1); + m_server->appendMessageToFrontmost(i18n("Who"), + i18n("End of /WHO list for %1", + parameterList.value(1)), messageTags); } - - // inform server of this user's data - emit userhost(nick,mask,away,ircOp); - - // display message only if this was no automatic request - if (getAutomaticRequest(QStringLiteral("USERHOST"),nick)==0) + else { - m_server->appendMessageToFrontmost(i18n("Userhost"), - i18nc("%1 = nick, %2 = shows if nick is op, %3 = hostmask, %4 = shows away", "%1%2 is %3%4.", - nick, - (ircOp) ? i18n(" (IRC Operator)") : QString() - ,mask, - (away) ? i18n(" (away)") : QString()), messageTags); + setAutomaticRequest(QStringLiteral("WHO"), param, false); } - // was this an automatic request? - if (getAutomaticRequest(QStringLiteral("USERHOST"),nick)!=0) - { - setAutomaticRequest(QStringLiteral("USERHOST"),nick,false); - } - } // for - } - break; - } - case RPL_LISTSTART: //FIXME This reply is obsolete!!! - { - if (plHas(0)) - { - if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) + m_whoRequestList.removeAt(idx); + } + else { - m_server->appendMessageToFrontmost(i18n("List"), i18n("List of channels:"), messageTags); + // whoRequestList seems to be broken. + qDebug() << "RPL_ENDOFWHO: malformed ENDOFWHO. retrieved: " + << parameterList.value(1) << " expected: " << m_whoRequestList.front(); + m_whoRequestList.clear(); } } - break; + else + { + qDebug() << "RPL_ENDOFWHO: unexpected ENDOFWHO. retrieved: " + << parameterList.value(1); + } + + emit endOfWho(parameterList.value(1)); } - case RPL_LIST: + break; + } + case RPL_WHOISCHANNELS: + { + if (plHas(3)) { - if (plHas(3)) + QStringList userChannels,voiceChannels,opChannels,halfopChannels,ownerChannels,adminChannels; + + // get a list of all channels the user is in + QStringList channelList=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); + channelList.sort(); + + // split up the list in channels where they are operator / user / voice + for (int index=0; index < channelList.count(); index++) { - if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) + QString lookChannel=channelList[index]; + if (lookChannel.startsWith(QLatin1Char('*')) || lookChannel.startsWith(QLatin1Char('&'))) { - QString message; - message=i18np("%2 (%1 user): %3", "%2 (%1 users): %3", parameterList.value(2).toInt(), parameterList.value(1), trailing); - m_server->appendMessageToFrontmost(i18n("List"), message, messageTags); + adminChannels.append(lookChannel.mid(1)); + m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 16); } - else // send them to /LIST window + // See bug #97354 part 2 + else if((lookChannel.startsWith(QLatin1Char('!')) || lookChannel.startsWith(QLatin1Char('~'))) && m_server->isAChannel(lookChannel.mid(1))) { - emit addToChannelList(parameterList.value(1), parameterList.value(2).toInt(), trailing); + ownerChannels.append(lookChannel.mid(1)); + m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 8); } - } - break; - } - case RPL_LISTEND: - { - if (plHas(0)) - { - // was this an automatic request? - if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) + // See bug #97354 part 1 + else if (lookChannel.startsWith(QStringLiteral("@+"))) { - m_server->appendMessageToFrontmost(i18n("List"), i18n("End of channel list."), messageTags); + opChannels.append(lookChannel.mid(2)); + m_server->setChannelNick(lookChannel.mid(2), parameterList.value(1), 4); } - else + else if (lookChannel.startsWith(QLatin1Char('@'))) { - emit endOfChannelList(); - setAutomaticRequest(QStringLiteral("LIST"),QString(),false); + opChannels.append(lookChannel.mid(1)); + m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 4); } - } - break; - } - case RPL_NOWAWAY: - { - if (plHas(1)) - { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); - if (nickInfo) + else if (lookChannel.startsWith(QLatin1Char('%'))) { - nickInfo->setAway(true); + halfopChannels.append(lookChannel.mid(1)); + m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 2); } - - m_server->setAway(true, messageTags); - } - break; - } - case RPL_UNAWAY: - { - if (plHas(1)) - { - NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); - - if (nickInfo) + else if (lookChannel.startsWith(QLatin1Char('+'))) { - nickInfo->setAway(false); - nickInfo->setAwayMessage(QString()); + voiceChannels.append(lookChannel.mid(1)); + m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 1); } - - m_server->setAway(false, messageTags); - } - break; - } - case RPL_BANLIST: - { - //:calvino.freenode.net 367 argonel #konversation fooish!~a@example.com argonel!argkde4@konversation/developer/argonel 1269464382 - if (plHas(3)) + else + { + userChannels.append(lookChannel); + m_server->setChannelNick(lookChannel, parameterList.value(1), 0); + } + } // endfor + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) + if (userChannels.count()) { - m_server->addBan(parameterList.value(1), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '), 2, 4)); //<-- QString::Section handles out of bounds end parameter + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is a user on channels: %2", + parameterList.value(1), + userChannels.join(QStringLiteral(" "))), messageTags + ); } - else + if (voiceChannels.count()) { - QDateTime when; - if (plHas(5)) - when.setTime_t(parameterList.value(4).toUInt()); - else - when = QDateTime::currentDateTime(); //use todays date instead of Jan 1 1970 - - QString setter(parameterList.value(3, i18nc("The server didn't respond with the identity of the ban creator, so we say unknown (in brackets to avoid confusion with a real nickname)", "(unknown)")).section(QLatin1Char('!'), 0, 0)); - - m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), - i18nc("BanList message: e.g. *!*@aol.com set by MrGrim on ", "%1 set by %2 on %3", - parameterList.value(2), setter, QLocale().toString(when, QLocale::ShortFormat)), messageTags - ); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 has voice on channels: %2", + parameterList.value(1), voiceChannels.join(QStringLiteral(" "))), messageTags + ); } - } - break; - } - case RPL_ENDOFBANLIST: - { - if (plHas(2)) - { - if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) + if (halfopChannels.count()) { - setAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1), false); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is a halfop on channels: %2", + parameterList.value(1), halfopChannels.join(QStringLiteral(" "))), messageTags + ); } - else + if (opChannels.count()) { - m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18n("End of Ban List."), messageTags); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is an operator on channels: %2", + parameterList.value(1), opChannels.join(QStringLiteral(" "))), messageTags + ); } - } - break; - } - case ERR_NOCHANMODES: - { - if (plHas(3)) - { - ChatWindow *chatwindow = m_server->getChannelByName(parameterList.value(1)); - if (chatwindow) + if (ownerChannels.count()) { - chatwindow->appendServerMessage(i18n("Channel"), trailing, messageTags); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is owner of channels: %2", + parameterList.value(1), ownerChannels.join(QStringLiteral(" "))), messageTags + ); } - else // We couldn't join the channel , so print the error. with [#channel] : + if (adminChannels.count()) { - m_server->appendMessageToFrontmost(i18n("Channel"), trailing, messageTags); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is admin on channels: %2", + parameterList.value(1), adminChannels.join(QStringLiteral(" "))), messageTags + ); } } - break; } - case ERR_NOSUCHSERVER: + break; + } + case RPL_WHOISSERVER: + { + if (plHas(4)) { - if (plHas(2)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + if (nickInfo) { - //Some servers don't know their name, so they return an error instead of the PING data - if (getLagMeasuring() && trailing.startsWith(prefix)) - { - m_server->pongReceived(); - } - else if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) //Inhibit message if this was an automatic request - { - setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); - } - else - { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such server: %1.", parameterList.value(1)), messageTags); - } + nickInfo->setNetServer(parameterList.value(2)); + nickInfo->setNetServerInfo(trailing); + // Clear the away state on assumption that if nick is away, this message will be followed + // by a 301 RPL_AWAY message. Not necessary a invalid assumption, but what can we do? + nickInfo->setAway(false); + nickInfo->setAwayMessage(QString()); } - break; - } - case ERR_UNAVAILRESOURCE: - { - if (plHas(2)) + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - if (m_server->isConnected()) - m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1 is currently unavailable.", parameterList.value(1)), messageTags); - else - { - QString newNick = m_server->getNextNickname(); - - // The user chose to disconnect - if (newNick.isNull()) - m_server->disconnectServer(); - else - { - m_server->obtainNickInfo(m_server->getNickname()) ; - m_server->renameNick(m_server->getNickname(), newNick, messageTags); - m_server->appendMessageToFrontmost(i18n("Nick"), - i18n("Nickname %1 is unavailable. Trying %2.", parameterList.value(1), newNick), messageTags); - m_server->queue(QStringLiteral("NICK ")+newNick); - } - } + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is online via %2 (%3).", parameterList.value(1), + parameterList.value(2), trailing), messageTags + ); } - break; } - case RPL_HIGHCONNECTCOUNT: - case RPL_LUSERCLIENT: - case RPL_LUSEROP: - case RPL_LUSERUNKNOWN: - case RPL_LUSERCHANNELS: - case RPL_LUSERME: - { // TODO make sure this works, i amputated the "+ ' '+trailing" - if (plHas(0)) + break; + } + case RPL_WHOISHELPER: + { + if (plHas(2)) + { + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - m_server->appendStatusMessage(i18n("Users"), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1), messageTags); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 is available for help.", + parameterList.value(1)), messageTags + ); } - break; } - case ERR_UNKNOWNCOMMAND: + break; + } + case RPL_WHOISOPERATOR: + { + if (plHas(2)) { - if (plHas(2)) + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: Unknown command.", parameterList.value(1)), messageTags); + if (trailing.toLower().simplified().startsWith(QLatin1String("is an irc operator"))) + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an IRC Operator.", parameterList.value(1)), messageTags); + else + m_server->appendMessageToFrontmost(i18n("Whois"), QString(QStringLiteral("%1 %2")).arg(parameterList.value(1)).arg(trailing), messageTags); } - break; } - case ERR_NOTREGISTERED: + break; + } + case RPL_WHOISIDLE: + { + if (plHas(3)) { - if (plHas(0)) + // get idle time in seconds + bool ok = false; + long seconds = parameterList.value(2).toLong(&ok); + if (!ok) break; + + long minutes = seconds/60; + long hours = minutes/60; + long days = hours/24; + + QDateTime signonTime; + uint signonTimestamp = parameterList.value(3).toUInt(&ok); + + if (ok && parameterList.count() == 5) + signonTime.setTime_t(signonTimestamp); + + if (!signonTime.isNull()) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("Not registered."), messageTags); + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + + if (nickInfo) + nickInfo->setOnlineSince(signonTime); + } + + // if idle time is longer than a day + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + { + if (days) + { + const QString daysString = i18np("1 day", "%1 days", days); + const QString hoursString = i18np("1 hour", "%1 hours", (hours % 24)); + const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); + const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); + + m_server->appendMessageToFrontmost(i18n("Whois"), + i18nc("%1 = name of person, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)", + "%1 has been idle for %2, %3, %4, and %5.", + parameterList.value(1), + daysString, hoursString, minutesString, secondsString), messageTags); + // or longer than an hour + } + else if (hours) + { + const QString hoursString = i18np("1 hour", "%1 hours", hours); + const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); + const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18nc("%1 = name of person, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)", + "%1 has been idle for %2, %3, and %4.", parameterList.value(1), hoursString, + minutesString, secondsString), messageTags); + // or longer than a minute + } + else if (minutes) + { + const QString minutesString = i18np("1 minute", "%1 minutes", minutes); + const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); + m_server->appendMessageToFrontmost(i18n("Whois"), + i18nc("%1 = name of person, %2 = (x minutes), %3 = (x seconds)", + "%1 has been idle for %2 and %3.", parameterList.value(1), minutesString, secondsString), messageTags); + // or just some seconds + } + else + { + m_server->appendMessageToFrontmost(i18n("Whois"), + i18np("%2 has been idle for 1 second.", "%2 has been idle for %1 seconds.", seconds, parameterList.value(1)), messageTags); + } + + if (!signonTime.isNull()) + { + m_server->appendMessageToFrontmost(i18n("Whois"), + i18n("%1 has been online since %2.", + parameterList.value(1), QLocale().toString(signonTime, QLocale::ShortFormat)), messageTags); + } } - break; } - case ERR_NEEDMOREPARAMS: + break; + } + case RPL_ENDOFWHOIS: + { + if (plHas(2)) { - if (plHas(2)) + /*FIXME why is the nickinfo line below commented out? + //NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); + */ + // Display message only if this was not an automatic request. + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) + { + m_server->appendMessageToFrontmost(i18n("Whois"), i18n("End of WHOIS list."), messageTags); + } + // was this an automatic request? + if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: This command requires more parameters.", parameterList.value(1)), messageTags); + setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); } - break; } - case RPL_CAPAB: // Special freenode reply afaik + break; + } + case RPL_USERHOST: + { + if (plHas(2)) { - if (plHas(2)) + // iterate over all nick/masks in reply + QStringList uhosts=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); + + for (int index=0;indexenableIdentifyMsg(true); + m_server->appendMessageToFrontmost(i18n("Userhost"), + i18nc("%1 = nick, %2 = shows if nick is op, %3 = hostmask, %4 = shows away", "%1%2 is %3%4.", + nick, + (ircOp) ? i18n(" (IRC Operator)") : QString() + ,mask, + (away) ? i18n(" (away)") : QString()), messageTags); } - else // TEST is this right? split the logic up in prep for slotization + + // was this an automatic request? + if (getAutomaticRequest(QStringLiteral("USERHOST"),nick)!=0) { - m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1) + QLatin1Char(' ')+trailing, messageTags); + setAutomaticRequest(QStringLiteral("USERHOST"),nick,false); } - } - break; + } // for } - case ERR_BADCHANNELKEY: + break; + } + case RPL_LISTSTART: //FIXME This reply is obsolete!!! + { + if (plHas(0)) { - if (plHas(2)) + if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { - m_server->appendMessageToFrontmost(i18n("Error"), i18n("Cannot join %1: The channel is password-protected and either a wrong or no password was given.", parameterList.value(1)), messageTags); + m_server->appendMessageToFrontmost(i18n("List"), i18n("List of channels:"), messageTags); } - break; } - case RPL_LOGGEDIN: + break; + } + case RPL_LIST: + { + if (plHas(3)) { - if (plHas(3)) - m_server->appendStatusMessage(i18n("Info"), i18n("You are now logged in as %1.", parameterList.value(2)), messageTags); - - break; + if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) + { + QString message; + message=i18np("%2 (%1 user): %3", "%2 (%1 users): %3", parameterList.value(2).toInt(), parameterList.value(1), trailing); + m_server->appendMessageToFrontmost(i18n("List"), message, messageTags); + } + else // send them to /LIST window + { + emit addToChannelList(parameterList.value(1), parameterList.value(2).toInt(), trailing); + } } - case RPL_SASLSUCCESS: + break; + } + case RPL_LISTEND: + { + if (plHas(0)) { - if (plHas(2)) + // was this an automatic request? + if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { - m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication successful."), messageTags); - m_server->capEndNegotiation(); - - NickInfoPtr nickInfo = m_server->getNickInfo(m_server->getNickname()); - if (nickInfo) nickInfo->setIdentified(true); + m_server->appendMessageToFrontmost(i18n("List"), i18n("End of channel list."), messageTags); + } + else + { + emit endOfChannelList(); + setAutomaticRequest(QStringLiteral("LIST"),QString(),false); } - - break; } - case ERR_SASLFAIL: + break; + } + case RPL_NOWAWAY: + { + if (plHas(1)) { - if (plHas(2)) + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); + if (nickInfo) { - m_server->appendStatusMessage(i18n("Error"), i18n("SASL authentication attempt failed."), messageTags); - m_server->capEndNegotiation(); + nickInfo->setAway(true); } - break; + m_server->setAway(true, messageTags); } - case ERR_SASLABORTED: + break; + } + case RPL_UNAWAY: + { + if (plHas(1)) { - if (plHas(2)) - m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication aborted."), messageTags); + NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); + + if (nickInfo) + { + nickInfo->setAway(false); + nickInfo->setAwayMessage(QString()); + } - break; + m_server->setAway(false, messageTags); } - default: + break; + } + case RPL_BANLIST: + { + //:calvino.freenode.net 367 argonel #konversation fooish!~a@example.com argonel!argkde4@konversation/developer/argonel 1269464382 + if (plHas(3)) { - // All yet unknown messages go into the frontmost window without the - // preceding nickname - qDebug() << "unknown numeric" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); - m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); - } - } // end of numeric switch - if (!_plHad) - qDebug() << "numeric format error" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); - } // end of numeric elseif - else - { - qDebug() << "unknown message format" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); - } -} // end of server + if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) + { + m_server->addBan(parameterList.value(1), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '), 2, 4)); //<-- QString::Section handles out of bounds end parameter + } + else + { + QDateTime when; + if (plHas(5)) + when.setTime_t(parameterList.value(4).toUInt()); + else + when = QDateTime::currentDateTime(); //use todays date instead of Jan 1 1970 -void InputFilter::parseModes(const QString &sourceNick, const QStringList ¶meterList, const QHash &messageTags) -{ - const QString modestring=parameterList.value(1); + QString setter(parameterList.value(3, i18nc("The server didn't respond with the identity of the ban creator, so we say unknown (in brackets to avoid confusion with a real nickname)", "(unknown)")).section(QLatin1Char('!'), 0, 0)); - if (!isAChannel(parameterList.value(0))) - { - QString message; - if (parameterList.value(0) == m_server->getNickname()) - { - if (sourceNick == m_server->getNickname()) - { //XXX someone might care about the potentially unnecessary plural here - message = i18n("You have set personal modes: %1", modestring); - } - else - { //XXX someone might care about the potentially unnecessary plural here - message = i18n("%1 has changed your personal modes: %2", sourceNick, modestring); + m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), + i18nc("BanList message: e.g. *!*@aol.com set by MrGrim on ", "%1 set by %2 on %3", + parameterList.value(2), setter, QLocale().toString(when, QLocale::ShortFormat)), messageTags + ); + } } + break; } - if (!message.isEmpty()) - m_server->appendStatusMessage(i18n("Mode"), message, messageTags); - return; - } - - bool plus=false; - int parameterIndex=0; - // List of modes that need a parameter (note exception with -k and -l) - // Mode q is quiet on freenode and acts like b... if this is a channel mode on other - // networks then more logic is needed here. --MrGrim - //FIXME: The assumptions being made here about which modes have parameters and - // which don't strike me as very wrong, as some of these mode chars are used - // both as user and channel modes (e.g. A and O) and whether they use a para- - // meter can vary between those cases. This would seem to lead to trouble with - // the list indices down there. --hein, Thu Aug 6 19:48:02 CEST 2009 / r1008015 - QString parameterModes = QStringLiteral("aAoOvhkbleIq"); - QString message = i18n("%1 sets mode: %2", sourceNick, modestring); - - for (int index=0;indexappendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18n("End of Ban List."), messageTags); + } + } + break; } - else + case ERR_NOCHANMODES: { - // Check if this was a parameter mode - if (parameterModes.contains(QLatin1Char(mode))) + if (plHas(3)) { - // Check if the mode actually wants a parameter. -k and -l do not! - if (plus || (!plus && (mode!='k') && (mode!='l'))) + ChatWindow *chatwindow = m_server->getChannelByName(parameterList.value(1)); + if (chatwindow) { - // Remember the mode parameter - parameter = parameterList.value(2+parameterIndex); - message += QLatin1Char(' ') + parameter; - // Switch to next parameter - ++parameterIndex; + chatwindow->appendServerMessage(i18n("Channel"), trailing, messageTags); + } + else // We couldn't join the channel , so print the error. with [#channel] : + { + m_server->appendMessageToFrontmost(i18n("Channel"), trailing, messageTags); } } - // Let the channel update its modes - if (parameter.isEmpty()) // XXX Check this to ensure the braces are in the correct place + break; + } + case ERR_NOSUCHSERVER: + { + if (plHas(2)) { - qDebug() << "in updateChannelMode. sourceNick: '" << sourceNick << "' parameterlist: '" - << parameterList.join(QStringLiteral(", ")) << "'"; + //Some servers don't know their name, so they return an error instead of the PING data + if (getLagMeasuring() && trailing.startsWith(prefix)) + { + m_server->pongReceived(); + } + else if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) //Inhibit message if this was an automatic request + { + setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); + } + else + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such server: %1.", parameterList.value(1)), messageTags); + } } - m_server->updateChannelMode(sourceNick, parameterList.value(0), mode, plus, parameter, messageTags); + break; } - } // endfor - - if (Preferences::self()->useLiteralModes()) - { - m_server->appendCommandMessageToChannel(parameterList.value(0), i18n("Mode"), message, messageTags); - } -} - -// # & + and ! are *often*, but not necessarily, Channel identifiers. + and ! are non-RFC, -// so if a server doesn't offer 005 and supports + and ! channels, I think thats broken behaviour -// on their part - not ours. --Argonel -bool InputFilter::isAChannel(const QString &check) -{ - if (check.isEmpty()) - return false; - Q_ASSERT(m_server); - // if we ever see the assert, we need the ternary - return m_server? m_server->isAChannel(check) : bool(QString(QStringLiteral("#&")).contains(check.at(0))); -} - -bool InputFilter::isIgnore(const QString &sender, Ignore::Type type) -{ - bool doIgnore = false; - - foreach (Ignore* item, Preferences::ignoreList()) - { - QRegExp ignoreItem(QRegExp::escape(item->getName()).replace(QStringLiteral("\\*"), QStringLiteral("(.*)")), Qt::CaseInsensitive); - if (ignoreItem.exactMatch(sender) && (item->getFlags() & type)) - doIgnore = true; - if (ignoreItem.exactMatch(sender) && (item->getFlags() & Ignore::Exception)) - return false; - } - - return doIgnore; -} - -void InputFilter::reset() -{ - m_automaticRequest.clear(); - m_whoRequestList.clear(); -} - -void InputFilter::setAutomaticRequest(const QString& command, const QString& name, bool yes) -{ - m_automaticRequest[command][name.toLower()] += (yes) ? 1 : -1; - if(m_automaticRequest[command][name.toLower()]<0) - { - qDebug() << "( " << command << ", " << name - << " ) was negative! Resetting!"; - m_automaticRequest[command][name.toLower()]=0; - } -} - -int InputFilter::getAutomaticRequest(const QString& command, const QString& name) -{ - return m_automaticRequest[command][name.toLower()]; -} - -void InputFilter::addWhoRequest(const QString& name) { m_whoRequestList << name.toLower(); } - -bool InputFilter::isWhoRequestUnderProcess(const QString& name) { return (m_whoRequestList.contains(name.toLower())>0); } - -void InputFilter::setLagMeasuring(bool state) { m_lagMeasuring=state; } - -bool InputFilter::getLagMeasuring() { return m_lagMeasuring; } - -void InputFilter::parsePrivMsg(const QString& prefix, QStringList& parameterList, const QHash &messageTags) -{ - int pos = prefix.indexOf(QLatin1Char('!')); - QString source; - QString sourceHostmask; - QString message(trailing); - - if(pos > 0) - { - source = prefix.left(pos); - sourceHostmask = prefix.mid(pos + 1); - } - else - { - source = prefix; - } - - Application* konv_app = Application::instance(); - message = konv_app->doAutoreplace(message, false).first; - - if(isAChannel(parameterList.value(0))) - { - if(!isIgnore(prefix, Ignore::Channel)) + case ERR_UNAVAILRESOURCE: { - Channel* channel = m_server->getChannelByName(parameterList.value(0)); - if(channel) + if (plHas(2)) { - QString label; - - if (m_server->getServerNickPrefixes().contains(parameterList.value(0).at(0))) + if (m_server->isConnected()) + m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1 is currently unavailable.", parameterList.value(1)), messageTags); + else { - label = parameterList.value(0); - } + QString newNick = m_server->getNextNickname(); - channel->append(source, message, messageTags, label); - - if(source != m_server->getNickname()) - { - QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + - QRegExp::escape(m_server->loweredNickname()) + - QStringLiteral("([^\\d\\w]|$)")); - regexp.setCaseSensitivity(Qt::CaseInsensitive); - if(message.contains(regexp)) - { - konv_app->notificationHandler()->nick(channel, - source, message); - } + // The user chose to disconnect + if (newNick.isNull()) + m_server->disconnectServer(); else { - konv_app->notificationHandler()->message(channel, - source, message); + m_server->obtainNickInfo(m_server->getNickname()) ; + m_server->renameNick(m_server->getNickname(), newNick, messageTags); + m_server->appendMessageToFrontmost(i18n("Nick"), + i18n("Nickname %1 is unavailable. Trying %2.", parameterList.value(1), newNick), messageTags); + m_server->queue(QStringLiteral("NICK ")+newNick); } } } + break; } - } - else - { - if(!isIgnore(prefix,Ignore::Query)) + case RPL_HIGHCONNECTCOUNT: + case RPL_LUSERCLIENT: + case RPL_LUSEROP: + case RPL_LUSERUNKNOWN: + case RPL_LUSERCHANNELS: + case RPL_LUSERME: + { // TODO make sure this works, i amputated the "+ ' '+trailing" + if (plHas(0)) + { + m_server->appendStatusMessage(i18n("Users"), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1), messageTags); + } + break; + } + case ERR_UNKNOWNCOMMAND: { - QString queryName = source; - - // Handle znc.in/self-message correctly - if (source == m_server->getNickname()) - queryName = parameterList[0]; - - NickInfoPtr nickinfo = m_server->obtainNickInfo(queryName); - - if (queryName == source) - nickinfo->setHostmask(sourceHostmask); - - // Create a new query (server will check for dupes) - Query* query = m_server->addQuery(nickinfo, false /*we didn't initiate this*/ ); - - // send action to query - query->appendQuery(source, message, messageTags); - - if(source != m_server->getNickname() && query) + if (plHas(2)) { - QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + - QRegExp::escape(m_server->loweredNickname()) + - QStringLiteral("([^\\d\\w]|$)")); - regexp.setCaseSensitivity(Qt::CaseInsensitive); - if(message.contains(regexp)) + m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: Unknown command.", parameterList.value(1)), messageTags); + } + break; + } + case ERR_NOTREGISTERED: + { + if (plHas(0)) + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("Not registered."), messageTags); + } + break; + } + case ERR_NEEDMOREPARAMS: + { + if (plHas(2)) + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: This command requires more parameters.", parameterList.value(1)), messageTags); + } + break; + } + case RPL_CAPAB: // Special freenode reply afaik + { + if (plHas(2)) + { + // Disable as we don't use this for anything yet + if (trailing.contains(QStringLiteral("IDENTIFY-MSG"))) { - konv_app->notificationHandler()->nick(query, - source, message); + m_server->enableIdentifyMsg(true); } - else + else // TEST is this right? split the logic up in prep for slotization { - konv_app->notificationHandler()->queryMessage(query, - source, message); + m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1) + QLatin1Char(' ')+trailing, messageTags); } } + break; } - } -} + case ERR_BADCHANNELKEY: + { + if (plHas(2)) + { + m_server->appendMessageToFrontmost(i18n("Error"), i18n("Cannot join %1: The channel is password-protected and either a wrong or no password was given.", parameterList.value(1)), messageTags); + } + break; + } + case RPL_LOGGEDIN: + { + if (plHas(3)) + m_server->appendStatusMessage(i18n("Info"), i18n("You are now logged in as %1.", parameterList.value(2)), messageTags); -QHash InputFilter::parseMessageTags(const QString &line, int *startOfMessage) -{ - int index = line.indexOf(QLatin1Char(' ')); - *startOfMessage = index + 1; - QStringList tags = line.mid(1, index - 1).split(QLatin1Char(';')); - QHash tagHash; + break; + } + case RPL_SASLSUCCESS: + { + if (plHas(2)) + { + m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication successful."), messageTags); + m_server->capEndNegotiation(); - foreach(const QString &tag, tags) - { - QStringList tagList = tag.split(QLatin1Char('=')); - tagHash.insert(tagList.first(), tagList.last()); - } + NickInfoPtr nickInfo = m_server->getNickInfo(m_server->getNickname()); + if (nickInfo) nickInfo->setIdentified(true); + } - return tagHash; + break; + } + case ERR_SASLFAIL: + { + if (plHas(2)) + { + m_server->appendStatusMessage(i18n("Error"), i18n("SASL authentication attempt failed."), messageTags); + m_server->capEndNegotiation(); + } + + break; + } + case ERR_SASLABORTED: + { + if (plHas(2)) + m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication aborted."), messageTags); + + break; + } + default: + { + // All yet unknown messages go into the frontmost window without the + // preceding nickname + qDebug() << "unknown numeric" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); + m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")), messageTags); + } + } // end of numeric switch + if (!_plHad) + qDebug() << "numeric format error" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/inputfilter.h b/src/irc/inputfilter.h index d02af1b7..8460a1fe 100644 --- a/src/irc/inputfilter.h +++ b/src/irc/inputfilter.h @@ -1,99 +1,100 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #ifndef INPUTFILTER_H #define INPUTFILTER_H #include "ignore.h" #include #include #include class Server; class QDateTime; class InputFilter : public QObject { Q_OBJECT public: InputFilter(); ~InputFilter(); void setServer(Server* newServer); void parseLine(const QString &line); void reset(); // reset AutomaticRequest, WhoRequestList // use this when the client does automatics, like userhost for finding hostmasks void setAutomaticRequest(const QString& command, const QString& name, bool yes); int getAutomaticRequest(const QString& command, const QString& name); void addWhoRequest(const QString& name); // called from Server::send() // to avoid duplicate requests bool isWhoRequestUnderProcess(const QString& name); void setLagMeasuring(bool yes); bool getLagMeasuring(); Q_SIGNALS: void welcome(const QString& ownHost); void notifyResponse(const QString &nicksOnline); // will be connected to Server::startReverseDccSendTransfer() void startReverseDccSendTransfer(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::startReverseDccChat() void startReverseDccChat(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::addDccGet() void addDccGet(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::resumeDccGetTransfer() void resumeDccGetTransfer(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::resumeDccSendTransfer() void resumeDccSendTransfer(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::rejectDccSendTransfer() void rejectDccSendTransfer(const QString &sourceNick, const QStringList &dccArgument); // will be connected to Server::rejectDccChat() void rejectDccChat(const QString &sourceNick); // will be connected to Server::userhost() void userhost(const QString& nick,const QString& hostmask,bool away,bool ircOp); // will be connected to Server::setTopicAuthor() void topicAuthor(const QString& channel, const QString& author, QDateTime t); void endOfWho(const QString& target); void endOfNames(const QString& target); void addChannelListPanel(); void addToChannelList(const QString& channel,int users,const QString& topic); void endOfChannelList(); void invitation(const QString& nick,const QString& channel); void addDccChat(const QString& nick,const QStringList& arguments); protected: void parseClientCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags); void parseServerCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags); void parseModes(const QString &sourceNick, const QStringList ¶meterList, const QHash &messageTags); void parsePrivMsg(const QString& prefix, QStringList& parameterList, const QHash &messageTags); + void parseNumeric(const QString &prefix, int command, QStringList ¶meterList, const QHash &messageTags); QHash parseMessageTags(const QString &line, int *startOfMessage); bool isAChannel(const QString &check); bool isIgnore(const QString &pattern, Ignore::Type type); Server* m_server; // automaticRequest[command][channel or nick]=count QMap< QString, QMap< QString, int > > m_automaticRequest; QStringList m_whoRequestList; bool m_lagMeasuring; /// Used when handling MOTD bool m_connecting; }; #endif