X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcoreuserinputhandler.cpp;h=bc9fac9257b62c4069d65c1b40999a58d78bbe67;hp=6a53cad29c3f9e290aaf022bb9da2e452cfac081;hb=5e5f16b8e8557e5ac4c9feea9befef582498799d;hpb=e50ae7a06fc4e5d3a911c361d30953410deab609 diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp index 6a53cad2..bc9fac92 100644 --- a/src/core/coreuserinputhandler.cpp +++ b/src/core/coreuserinputhandler.cpp @@ -30,6 +30,13 @@ # include "cipher.h" #endif +#if QT_VERSION < 0x050000 +// QChar::LineFeed is Qt 5 +static const QChar QCharLF = QChar('\n'); +#else +static const QChar QCharLF = QChar::LineFeed; +#endif + CoreUserInputHandler::CoreUserInputHandler(CoreNetwork *parent) : CoreBasicHandler(parent) { @@ -169,11 +176,7 @@ void CoreUserInputHandler::handleCtcp(const BufferInfo &bufferInfo, const QStrin QString verboseMessage = tr("sending CTCP-%1 request to %2").arg(ctcpTag).arg(nick); if (ctcpTag == "PING") { -#if QT_VERSION >= 0x040700 message = QString::number(QDateTime::currentMSecsSinceEpoch()); -#else - message = QString::number(QDateTime::currentDateTime().toTime_t()); -#endif } // FIXME make this a proper event @@ -232,7 +235,7 @@ void CoreUserInputHandler::doMode(const BufferInfo &bufferInfo, const QChar& add if (!isNumber || maxModes == 0) maxModes = 1; QStringList nickList; - if (nicks == "*") { // All users in channel + if (nicks == "*" && bufferInfo.type() == BufferInfo::ChannelBuffer) { // All users in channel const QList users = network()->ircChannel(bufferInfo.bufferName())->ircUsers(); foreach(IrcUser *user, users) { if ((addOrRemove == '+' && !network()->ircChannel(bufferInfo.bufferName())->userModes(user).contains(mode)) @@ -441,8 +444,18 @@ void CoreUserInputHandler::handleMe(const BufferInfo &bufferInfo, const QString if (bufferInfo.bufferName().isEmpty() || !bufferInfo.acceptsRegularMessages()) return; // server buffer // FIXME make this a proper event - coreNetwork()->coreSession()->ctcpParser()->query(coreNetwork(), bufferInfo.bufferName(), "ACTION", msg); - emit displayMsg(Message::Action, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); + + // Split apart messages at line feeds. The IRC protocol uses those to separate commands, so + // they need to be split into multiple messages. + QStringList messages = msg.split(QCharLF); + + foreach (auto message, messages) { + // Handle each separated message independently + coreNetwork()->coreSession()->ctcpParser()->query(coreNetwork(), bufferInfo.bufferName(), + "ACTION", message); + emit displayMsg(Message::Action, bufferInfo.type(), bufferInfo.bufferName(), message, + network()->myNick(), Message::Self); + } } @@ -477,12 +490,16 @@ void CoreUserInputHandler::handleMsg(const BufferInfo &bufferInfo, const QString return; QString target = msg.section(' ', 0, 0); - QByteArray encMsg = userEncode(target, msg.section(' ', 1)); + QString msgSection = msg.section(' ', 1); + + std::function encodeFunc = [this] (const QString &target, const QString &message) -> QByteArray { + return userEncode(target, message); + }; #ifdef HAVE_QCA2 - putPrivmsg(serverEncode(target), encMsg, network()->cipher(target)); + putPrivmsg(target, msgSection, encodeFunc, network()->cipher(target)); #else - putPrivmsg(serverEncode(target), encMsg); + putPrivmsg(target, msgSection, encodeFunc); #endif } @@ -498,11 +515,19 @@ void CoreUserInputHandler::handleNick(const BufferInfo &bufferInfo, const QStrin void CoreUserInputHandler::handleNotice(const BufferInfo &bufferInfo, const QString &msg) { QString bufferName = msg.section(' ', 0, 0); - QString payload = msg.section(' ', 1); QList params; - params << serverEncode(bufferName) << channelEncode(bufferInfo.bufferName(), payload); - emit putCmd("NOTICE", params); - emit displayMsg(Message::Notice, typeByTarget(bufferName), bufferName, payload, network()->myNick(), Message::Self); + // Split apart messages at line feeds. The IRC protocol uses those to separate commands, so + // they need to be split into multiple messages. + QStringList messages = msg.section(' ', 1).split(QCharLF); + + foreach (auto message, messages) { + // Handle each separated message independently + params.clear(); + params << serverEncode(bufferName) << channelEncode(bufferInfo.bufferName(), message); + emit putCmd("NOTICE", params); + emit displayMsg(Message::Notice, typeByTarget(bufferName), bufferName, message, + network()->myNick(), Message::Self); + } } @@ -545,7 +570,18 @@ void CoreUserInputHandler::handlePing(const BufferInfo &bufferInfo, const QStrin if (param.isEmpty()) param = QTime::currentTime().toString("hh:mm:ss.zzz"); - putCmd("PING", serverEncode(param)); + // Take priority so this won't get stuck behind other queued messages. + putCmd("PING", serverEncode(param), QByteArray(), true); +} + + +void CoreUserInputHandler::handlePrint(const BufferInfo &bufferInfo, const QString &msg) +{ + if (bufferInfo.bufferName().isEmpty() || !bufferInfo.acceptsRegularMessages()) + return; // server buffer + + QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg); + emit displayMsg(Message::Info, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); } @@ -554,12 +590,24 @@ void CoreUserInputHandler::handleQuery(const BufferInfo &bufferInfo, const QStri { Q_UNUSED(bufferInfo) QString target = msg.section(' ', 0, 0); - QString message = msg.section(' ', 1); - if (message.isEmpty()) - emit displayMsg(Message::Server, BufferInfo::QueryBuffer, target, tr("Starting query with %1").arg(target), network()->myNick(), Message::Self); - else - emit displayMsg(Message::Plain, BufferInfo::QueryBuffer, target, message, network()->myNick(), Message::Self); - handleMsg(bufferInfo, msg); + // Split apart messages at line feeds. The IRC protocol uses those to separate commands, so + // they need to be split into multiple messages. + QStringList messages = msg.section(' ', 1).split(QCharLF); + + foreach (auto message, messages) { + // Handle each separated message independently + if (message.isEmpty()) { + emit displayMsg(Message::Server, BufferInfo::QueryBuffer, target, + tr("Starting query with %1").arg(target), network()->myNick(), + Message::Self); + // handleMsg is a no-op if message is empty + } else { + emit displayMsg(Message::Plain, BufferInfo::QueryBuffer, target, message, + network()->myNick(), Message::Self); + // handleMsg needs the target specified at the beginning of the message + handleMsg(bufferInfo, target + " " + message); + } + } } @@ -570,9 +618,10 @@ void CoreUserInputHandler::handleQuit(const BufferInfo &bufferInfo, const QStrin } -void CoreUserInputHandler::issueQuit(const QString &reason) +void CoreUserInputHandler::issueQuit(const QString &reason, bool forceImmediate) { - emit putCmd("QUIT", serverEncode(reason)); + // If needing an immediate QUIT (e.g. core shutdown), prepend this to the queue + emit putCmd("QUIT", serverEncode(reason), QByteArray(), forceImmediate); } @@ -588,13 +637,25 @@ void CoreUserInputHandler::handleSay(const BufferInfo &bufferInfo, const QString if (bufferInfo.bufferName().isEmpty() || !bufferInfo.acceptsRegularMessages()) return; // server buffer - QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg); + std::function encodeFunc = [this] (const QString &target, const QString &message) -> QByteArray { + return channelEncode(target, message); + }; + + // Split apart messages at line feeds. The IRC protocol uses those to separate commands, so + // they need to be split into multiple messages. + QStringList messages = msg.split(QCharLF, QString::SkipEmptyParts); + + foreach (auto message, messages) { + // Handle each separated message independently #ifdef HAVE_QCA2 - putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg, network()->cipher(bufferInfo.bufferName())); + putPrivmsg(bufferInfo.bufferName(), message, encodeFunc, + network()->cipher(bufferInfo.bufferName())); #else - putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg); + putPrivmsg(bufferInfo.bufferName(), message, encodeFunc); #endif - emit displayMsg(Message::Plain, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); + emit displayMsg(Message::Plain, bufferInfo.type(), bufferInfo.bufferName(), message, + network()->myNick(), Message::Self); + } } @@ -757,56 +818,24 @@ void CoreUserInputHandler::defaultHandler(QString cmd, const BufferInfo &bufferI } -void CoreUserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message, Cipher *cipher) +void CoreUserInputHandler::putPrivmsg(const QString &target, const QString &message, std::function encodeFunc, Cipher *cipher) { - // Encrypted messages need special care. There's no clear relation between cleartext and encrypted message length, - // so we can't just compute the maxSplitPos. Instead, we need to loop through the splitpoints until the crypted - // version is short enough... - // TODO: check out how the various possible encryption methods behave length-wise and make - // this clean by predicting the length of the crypted msg. - // For example, blowfish-ebc seems to create 8-char chunks. + Q_UNUSED(cipher); + QString cmd("PRIVMSG"); + QByteArray targetEnc = serverEncode(target); - static const char *cmd = "PRIVMSG"; - static const char *splitter = " .,-!?"; + std::function(QString &)> cmdGenerator = [&] (QString &splitMsg) -> QList { + QByteArray splitMsgEnc = encodeFunc(target, splitMsg); - int maxSplitPos = message.count(); - int splitPos = maxSplitPos; - forever { - QByteArray crypted = message.left(splitPos); - bool isEncrypted = false; #ifdef HAVE_QCA2 - if (cipher && !cipher->key().isEmpty() && !message.isEmpty()) { - isEncrypted = cipher->encrypt(crypted); + if (cipher && !cipher->key().isEmpty() && !splitMsg.isEmpty()) { + cipher->encrypt(splitMsgEnc); } #endif - int overrun = lastParamOverrun(cmd, QList() << target << crypted); - if (overrun) { - // In case this is not an encrypted msg, we can just cut off at the end - if (!isEncrypted) - maxSplitPos = message.count() - overrun; - - splitPos = -1; - for (const char *splitChar = splitter; *splitChar != 0; splitChar++) { - splitPos = qMax(splitPos, message.lastIndexOf(*splitChar, maxSplitPos) + 1); // keep split char on old line - } - if (splitPos <= 0 || splitPos > maxSplitPos) - splitPos = maxSplitPos; - - maxSplitPos = splitPos - 1; - if (maxSplitPos <= 0) { // this should never happen, but who knows... - qWarning() << tr("[Error] Could not encrypt your message: %1").arg(message.data()); - return; - } - continue; // we never come back here for !encrypted! - } - - // now we have found a valid splitpos (or didn't need to split to begin with) - putCmd(cmd, QList() << target << crypted); - if (splitPos < message.count()) - putPrivmsg(target, message.mid(splitPos), cipher); + return QList() << targetEnc << splitMsgEnc; + }; - return; - } + putCmd(cmd, network()->splitMessage(cmd, message, cmdGenerator)); }