From 97a9b1646bb0d6362cef14bac3a577481cc01e49 Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Tue, 27 Dec 2016 08:26:03 -0500 Subject: [PATCH] Split messages on newlines as per IRC protocol When handling user input sent over the protocol, split apart messages on QChar::LineFeed, processing and displaying each as individual messages sent one after the other. This applies to /query, /say, /notice, and /me. Otherwise, the IRC protocol interprets anything after each line break as a new message. Fixes inability to send multi-line content via copy-paste on Quasseldroid. Quassel desktop currently works around this issue by sending multiple individual messages for every line break. Test case: 1. Install Quasseldroid (stable or alpha), connect to a core 2. Copy some text to the Android clipboard that has a line break in it (not just word-wrap). 3. Paste into Quasseldroid 4. Send message Before: Message shows up with line break (LF character on desktop), IRC server treats all lines after first as raw commands. After: Message gets split into multiple smaller messages at each line break. This should also work for /query, /say, /notice, and /me. See: https://tools.ietf.org/html/rfc1459#page-8 Closes GH-263. --- src/core/coreuserinputhandler.cpp | 80 +++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp index a978fe2b..ebbaf911 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) { @@ -437,8 +444,19 @@ 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, ignoring any carriage returns + message = message.trimmed(); + coreNetwork()->coreSession()->ctcpParser()->query(coreNetwork(), bufferInfo.bufferName(), + "ACTION", message); + emit displayMsg(Message::Action, bufferInfo.type(), bufferInfo.bufferName(), message, + network()->myNick(), Message::Self); + } } @@ -498,11 +516,20 @@ 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, ignoring any carriage returns + message = message.trimmed(); + 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); + } } @@ -565,12 +592,25 @@ 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, ignoring any carriage returns + message = message.trimmed(); + 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); + } + } } @@ -604,12 +644,22 @@ void CoreUserInputHandler::handleSay(const BufferInfo &bufferInfo, const QString 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); + + foreach (auto message, messages) { + // Handle each separated message independently, ignoring any carriage returns + message = message.trimmed(); #ifdef HAVE_QCA2 - putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc, network()->cipher(bufferInfo.bufferName())); + putPrivmsg(bufferInfo.bufferName(), message, encodeFunc, + network()->cipher(bufferInfo.bufferName())); #else - putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc); + 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); + } } -- 2.20.1