From e670ca6e245c9e1044879b12afb1a7a38667b264 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Thu, 5 Aug 2010 11:05:42 +0200 Subject: [PATCH] Rework message splitting to properly handle encrypted messages Since cutting off a crypted message produces garbage, and we can't reliably predict how long the encrypted version will be in relation to the cleartext string, we need to do some extra trickery in the message splitting code. As a side effect, the split char now also remains on the previous line, which is aesthetically way more pleasing than starting a line with a space or punctuation. trickery for this case. Since --- src/core/coreuserinputhandler.cpp | 82 +++++++++++++++++++++---------- src/core/coreuserinputhandler.h | 5 +- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp index 185baf70..43368650 100644 --- a/src/core/coreuserinputhandler.cpp +++ b/src/core/coreuserinputhandler.cpp @@ -344,10 +344,10 @@ void CoreUserInputHandler::handleMsg(const BufferInfo &bufferInfo, const QString QByteArray encMsg = userEncode(target, msg.section(' ', 1)); #ifdef HAVE_QCA2 - encMsg = encrypt(target, encMsg); + putPrivmsg(serverEncode(target), encMsg, network()->cipher(target)); +#else + putPrivmsg(serverEncode(target), encMsg); #endif - - putPrivmsg(serverEncode(target), encMsg, false); } void CoreUserInputHandler::handleNick(const BufferInfo &bufferInfo, const QString &msg) { @@ -440,10 +440,10 @@ void CoreUserInputHandler::handleSay(const BufferInfo &bufferInfo, const QString QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg); #ifdef HAVE_QCA2 - encMsg = encrypt(bufferInfo.bufferName(), encMsg); + putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg, network()->cipher(bufferInfo.bufferName())); +#else + putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg); #endif - - putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg, false); emit displayMsg(Message::Plain, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); } @@ -548,28 +548,54 @@ void CoreUserInputHandler::defaultHandler(QString cmd, const BufferInfo &bufferI emit putCmd(serverEncode(cmd.toUpper()), serverEncode(msg.split(" "))); } -// TODO: handle cutoff of encrypted messages -void CoreUserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message, bool encrypted) { - - QByteArray temp = message; +void CoreUserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message, 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. static const char *cmd = "PRIVMSG"; - int overrun = lastParamOverrun(cmd, QList() << target << temp); - if(overrun) { - static const char *splitter = " .,-"; - int maxSplitPos = temp.count() - overrun; - int splitPos = -1; - for(const char *splitChar = splitter; *splitChar != 0; splitChar++) { - splitPos = qMax(splitPos, temp.lastIndexOf(*splitChar, maxSplitPos)); + static const char *splitter = " .,-"; + + int maxSplitPos = message.count(); + int splitPos = maxSplitPos; + forever { + QByteArray crypted = message.left(splitPos); + bool isEncrypted = false; +#ifdef HAVE_QCA2 + if(cipher && !message.isEmpty()) { + isEncrypted = cipher->encrypt(crypted); } - if(splitPos <= 0) { - splitPos = maxSplitPos; +#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! } - putCmd(cmd, QList() << target << temp.left(splitPos)); - putPrivmsg(target, temp.mid(splitPos), 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; - } else { - putCmd(cmd, QList() << target << temp); } } @@ -603,7 +629,10 @@ int CoreUserInputHandler::lastParamOverrun(const QString &cmd, const QListencrypt(message); + bool result = cipher->encrypt(message); + if(didEncrypt) + *didEncrypt = result; + return message; } #endif diff --git a/src/core/coreuserinputhandler.h b/src/core/coreuserinputhandler.h index 801024d6..9a31f69e 100644 --- a/src/core/coreuserinputhandler.h +++ b/src/core/coreuserinputhandler.h @@ -23,6 +23,7 @@ #include "corebasichandler.h" +class Cipher; class Server; class CoreUserInputHandler : public CoreBasicHandler { @@ -77,11 +78,11 @@ protected: private: void banOrUnban(const BufferInfo &bufferInfo, const QString &text, bool ban); - void putPrivmsg(const QByteArray &target, const QByteArray &message, bool isEncrypted); + void putPrivmsg(const QByteArray &target, const QByteArray &message, Cipher *cipher = 0); int lastParamOverrun(const QString &cmd, const QList ¶ms); #ifdef HAVE_QCA2 - QByteArray encrypt(const QString &target, const QByteArray &message) const; + QByteArray encrypt(const QString &target, const QByteArray &message, bool *didEncrypt = 0) const; #endif struct Command { -- 2.20.1