X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=core%2Fserver.cpp;h=74f9c7ad743c1d30eefdf0b6b6733e745d1e7800;hp=45c9da1acddfafde051f07012fd1f1716c76831c;hb=c66cfafe5dfa8ccdb830e1ae412e7b51cbcdd184;hpb=60bce4937bf006ceae27d012908b644a87865302 diff --git a/core/server.cpp b/core/server.cpp index 45c9da1a..74f9c7ad 100644 --- a/core/server.cpp +++ b/core/server.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005/06 by The Quassel Team * + * Copyright (C) 2005-07 by The Quassel Team * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -27,8 +27,16 @@ #include Server::Server(QString net) : network(net) { + QString MQUOTE = QString('\020'); + ctcpMDequoteHash[MQUOTE + '0'] = QString('\000'); + ctcpMDequoteHash[MQUOTE + 'n'] = QString('\n'); + ctcpMDequoteHash[MQUOTE + 'r'] = QString('\r'); + ctcpMDequoteHash[MQUOTE + MQUOTE] = MQUOTE; - + XDELIM = QString('\001'); + QString XQUOTE = QString('\134'); + ctcpXDelimDequoteHash[XQUOTE + XQUOTE] = XQUOTE; + ctcpXDelimDequoteHash[XQUOTE + QString('a')] = XDELIM; } Server::~Server() { @@ -159,7 +167,7 @@ void Server::handleServerMsg(QString msg) { return; } // OK, first we split the raw message into its various parts... - QString prefix; + QString prefix = ""; QString cmd; QStringList params; @@ -218,19 +226,21 @@ void Server::defaultServerHandler(QString cmd, QString prefix, QStringList param emit displayMsg(Message::Server, "", params.join(" "), prefix); break; // Server error messages without param, just display them - case 409: case 411: case 412: case 422: case 424: case 431: case 445: case 446: case 451: case 462: + case 409: case 411: case 412: case 422: case 424: case 445: case 446: case 451: case 462: case 463: case 464: case 465: case 466: case 472: case 481: case 483: case 485: case 491: case 501: case 502: + case 431: // ERR_NONICKNAMEGIVEN emit displayMsg(Message::Error, "", params.join(" "), prefix); break; // Server error messages, display them in red. First param will be appended. - case 401: case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 432: case 442: + case 401: case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 442: { QString p = params.takeFirst(); emit displayMsg(Message::Error, "", params.join(" ") + " " + p, prefix); break; } // Server error messages which will be displayed with a colon between the first param and the rest - case 413: case 414: case 423: case 436: case 441: case 444: case 461: + case 413: case 414: case 423: case 441: case 444: case 461: case 467: case 471: case 473: case 474: case 475: case 476: case 477: case 478: case 482: + case 436: // ERR_NICKCOLLISION { QString p = params.takeFirst(); emit displayMsg(Message::Error, "", p + ": " + params.join(" ")); break; @@ -291,6 +301,96 @@ QStringList Server::providesUserHandlers() { return userHandlers; } +QString Server::ctcpDequote(QString message) { + QString dequotedMessage; + QString messagepart; + QHash::iterator ctcpquote; + + // copy dequote Message + for(int i = 0; i < message.size(); i++) { + messagepart = message[i]; + if(i+1 < message.size()) { + for(ctcpquote = ctcpMDequoteHash.begin(); ctcpquote != ctcpMDequoteHash.end(); ++ctcpquote) { + if(message.mid(i,2) == ctcpquote.key()) { + dequotedMessage += ctcpquote.value(); + i++; + break; + } + } + } + dequotedMessage += messagepart; + } + return dequotedMessage; +} + + +QString Server::ctcpXdelimDequote(QString message) { + QString dequotedMessage; + QString messagepart; + QHash::iterator xdelimquote; + + for(int i = 0; i < message.size(); i++) { + messagepart = message[i]; + if(i+1 < message.size()) { + for(xdelimquote = ctcpXDelimDequoteHash.begin(); xdelimquote != ctcpXDelimDequoteHash.end(); ++xdelimquote) { + if(message.mid(i,2) == xdelimquote.key()) { + messagepart = xdelimquote.value(); + i++; + break; + } + } + } + dequotedMessage += messagepart; + } + return dequotedMessage; +} + +QStringList Server::parseCtcp(CtcpType ctcptype, QString prefix, QString target, QString message) { + QStringList messages; + QString ctcp; + + //lowlevel message dequote + QString dequotedMessage = ctcpDequote(message); + + // extract tagged / extended data + while(dequotedMessage.contains(XDELIM)) { + messages << dequotedMessage.section(XDELIM,0,0); + ctcp = ctcpXdelimDequote(dequotedMessage.section(XDELIM,1,1)); + dequotedMessage = dequotedMessage.section(XDELIM,2,2); + + //dispatch the ctcp command + QString ctcpcmd = ctcp.section(' ', 0, 0); + QString ctcpparam = ctcp.section(' ', 1); + + QString hname = ctcpcmd.toLower(); + hname[0] = hname[0].toUpper(); + hname = "handleCtcp" + hname; + if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(CtcpType, ctcptype), Q_ARG(QString, prefix), Q_ARG(QString, target), Q_ARG(QString, ctcpparam))) { + // Ok. Default handler it is. + defaultCtcpHandler(ctcptype, prefix, ctcpcmd, target, ctcpparam); + } + } + if(!dequotedMessage.isEmpty()) { + messages << dequotedMessage; + } + return messages; +} + +QString Server::ctcpPack(QString ctcpTag, QString message) { + return XDELIM + ctcpTag + ' ' + message + XDELIM; +} + +void Server::ctcpQuery(QString bufname, QString ctcpTag, QString message) { + QStringList params; + params << bufname << ctcpPack(ctcpTag, message); + putCmd("PRIVMSG", params); +} + +void Server::ctcpReply(QString bufname, QString ctcpTag, QString message) { + QStringList params; + params << bufname << ctcpPack(ctcpTag, message); + putCmd("NOTICE", params); +} /**********************************************************************************/ @@ -400,6 +500,12 @@ void Server::handleUserSay(QString bufname, QString msg) { } } +void Server::handleUserMe(QString bufname, QString msg) { + if(bufname.isEmpty()) return; // server buffer + ctcpQuery(bufname, "ACTION", msg); + emit displayMsg(Message::Action, bufname, msg, ownNick); +} + void Server::handleUserTopic(QString bufname, QString msg) { if(bufname.isEmpty()) return; QStringList params; @@ -420,6 +526,7 @@ void Server::handleUserVoice(QString bufname, QString msg) { void Server::handleServerJoin(QString prefix, QStringList params) { Q_ASSERT(params.count() == 1); QString nick = updateNickFromMask(prefix); + emit displayMsg(Message::Join, params[0], params[0], prefix); if(nick == ownNick) { // Q_ASSERT(!buffers.contains(params[0])); // cannot join a buffer twice! // Buffer *buf = new Buffer(params[0]); @@ -445,7 +552,7 @@ void Server::handleServerJoin(QString prefix, QStringList params) { nicks[nick] = n; emit nickAdded(network, nick, n); } - emit displayMsg(Message::Join, params[0], params[0], prefix); + //emit displayMsg(Message::Join, params[0], params[0], prefix); //} } @@ -565,11 +672,23 @@ void Server::handleServerPrivmsg(QString prefix, QStringList params) { Q_ASSERT(params.count() >= 2); if(params.count()<2) emit displayMsg(Message::Plain, params[0], "", prefix); else { - if(params[0] == ownNick) { - emit displayMsg(Message::Plain, "", params[1], prefix, Message::PrivMsg); + // it's possible to pack multiple privmsgs into one param using ctcp + QStringList messages = parseCtcp(Server::CtcpQuery, prefix, params[0], params[1]); + if(params[0].toLower() == ownNick.toLower()) { // Freenode sends nickname in lower case! + foreach(QString message, messages) { + if(!message.isEmpty()) { + emit displayMsg(Message::Plain, "", message, prefix, Message::PrivMsg); + } + } + } else { + //qDebug() << prefix << params; Q_ASSERT(isChannelName(params[0])); // should be channel! - emit displayMsg(Message::Plain, params[0], params[1], prefix); + foreach(QString message, messages) { + if(!message.isEmpty()) { + emit displayMsg(Message::Plain, params[0], message, prefix); + } + } } } } @@ -578,8 +697,10 @@ void Server::handleServerQuit(QString prefix, QStringList params) { QString nick = updateNickFromMask(prefix); Q_ASSERT(nicks.contains(nick)); VarMap chans = nicks[nick]["Channels"].toMap(); + QString msg; + if(params.count()) msg = params[0]; foreach(QString c, chans.keys()) { - emit displayMsg(Message::Quit, c, params[0], prefix); + emit displayMsg(Message::Quit, c, msg, prefix); } nicks.remove(nick); emit nickRemoved(network, nick); @@ -617,6 +738,7 @@ void Server::handleServer001(QString prefix, QStringList params) { /* RPL_ISUPPORT */ // TODO Complete 005 handling, also use sensible defaults for non-sent stuff void Server::handleServer005(QString prefix, QStringList params) { + //qDebug() << prefix << params; params.removeLast(); foreach(QString p, params) { QString key = p.section("=", 0, 0); @@ -693,7 +815,35 @@ void Server::handleServer353(QString prefix, QStringList params) { } } -/* RPL_NICKNAMEINUSER */ +/* ERR_ERRONEUSNICKNAME */ +void Server::handleServer432(QString prefix, QStringList params) { + if(params.size() < 2) { + // handle unreal-ircd bug, where unreal ircd doesnt supply a TARGET in ERR_ERRONEUSNICKNAME during registration phase: + // nick @@@ + // :irc.scortum.moep.net 432 @@@ :Erroneous Nickname: Illegal characters + // correct server reply: + // :irc.scortum.moep.net 432 * @@@ :Erroneous Nickname: Illegal characters + emit displayMsg(Message::Error, "", tr("There is a nickname in your identity's nicklist which contains illegal characters")); + emit displayMsg(Message::Error, "", tr("Due to a bug in Unreal IRCd (and maybe other irc-servers too) we're unable to determine the erroneous nick")); + emit displayMsg(Message::Error, "", tr("Please use: /nick to continue or clean up your nicklist")); + } else { + QString errnick = params[0]; + emit displayMsg(Message::Error, "", tr("Nick %1 contains illegal characters").arg(errnick)); + // if there is a problem while connecting to the server -> we handle it + // TODO rely on another source... + if(currentServer.isEmpty()) { + QStringList desiredNicks = identity["NickList"].toStringList(); + int nextNick = desiredNicks.indexOf(errnick) + 1; + if (desiredNicks.size() > nextNick) { + putCmd("NICK", QStringList(desiredNicks[nextNick])); + } else { + emit displayMsg(Message::Error, "", tr("No free and valid nicks in nicklist found. use: /nick to continue")); + } + } + } +} + +/* ERR_NICKNAMEINUSE */ void Server::handleServer433(QString prefix, QStringList params) { QString errnick = params[0]; emit displayMsg(Message::Error, "", tr("Nick %1 is already taken").arg(errnick)); @@ -705,11 +855,42 @@ void Server::handleServer433(QString prefix, QStringList params) { if (desiredNicks.size() > nextNick) { putCmd("NICK", QStringList(desiredNicks[nextNick])); } else { - emit displayMsg(Message::Error, "", "All nicks in nicklist taken... use: /nick to continue"); + emit displayMsg(Message::Error, "", tr("No free and valid nicks in nicklist found. use: /nick to continue")); } } } +/***********************************************************************************/ +// CTCP HANDLER + +void Server::handleCtcpAction(CtcpType ctcptype, QString prefix, QString target, QString param) { + emit displayMsg(Message::Action, target, param, prefix); +} + +void Server::handleCtcpPing(CtcpType ctcptype, QString prefix, QString target, QString param) { + if(ctcptype == CtcpQuery) { + ctcpReply(nickFromMask(prefix), "PING", param); + emit displayMsg(Message::Server, "", tr("Received CTCP PING request by %1").arg(prefix)); + } else { + // display ping answer + } +} + +void Server::handleCtcpVersion(CtcpType ctcptype, QString prefix, QString target, QString param) { + if(ctcptype == CtcpQuery) { + // FIXME use real Info about quassel :) + ctcpReply(nickFromMask(prefix), "VERSION", QString("Quassel:pre Release:*nix")); + emit displayMsg(Message::Server, "", tr("Received CTCP VERSION request by %1").arg(prefix)); + } else { + // TODO display Version answer + } +} + +void Server::defaultCtcpHandler(CtcpType ctcptype, QString prefix, QString cmd, QString target, QString param) { + emit displayMsg(Message::Error, "", tr("Received unknown CTCP %1 by %2").arg(cmd).arg(prefix)); +} + + /***********************************************************************************/ /* Exception classes for message handling */