CVE-2015-2778: Resource Management Errors (CWE-399)
value: Quassel before 0.12-rc1 uses an incorrect data-type size when splitting a message, which allows remote attackers to cause a denial of service (crash) via a long CTCP query containing only multibyte characters.
CWE description: Weaknesses in this category are related to improper management of system resources.
Published: 2015-04-10T15:00Z
Repository: https://github.com/quassel/quassel
Language: C++
Files changed: src/core/corebasichandler.cpp, src/core/corebasichandler.h, src/core/corenetwork.cpp, src/core/corenetwork.h, src/core/coreuserinputhandler.cpp, src/core/coreuserinputhandler.h, src/core/ctcpparser.cpp
Commit date: 2015-02-27 15:38:06 -0500
Commit message: Improve the message-splitting algorithm for PRIVMSG and CTCP

This introduces a new message splitting algorithm based on
QTextBoundaryFinder.  It works by first starting with the entire
message to be sent, encoding it, and checking to see if it is over
the maximum message length.  If it is, it uses QTBF to find the
word boundary most immediately preceding the maximum length.  If no
suitable boundary can be found, it falls back to searching for
grapheme boundaries.  It repeats this process until the entire
message has been sent.

Unlike what it replaces, the new splitting code is not recursive
and cannot cause stack overflows.  Additionally, if it is unable
to split a string, it will give up gracefully and not crash the
core or cause a thread to run away.

This patch fixes two bugs.  The first is garbage characters caused
by accidentally splitting the string in the middle of a multibyte
character.  Since the new code splits at a character level instead
of a byte level, this will no longer be an issue.  The second is
the core crash caused by sending an overlength CTCP query ("/me")
containing only multibyte characters.  This bug was caused by the
old CTCP splitter using the byte index from lastParamOverrun() as
a character index for a QString.

Diff: diff --git a/src/core/corebasichandler.cpp b/src/core/corebasichandler.cpp
index dfa8a9964..fbfc76c2f 100644
--- a/src/core/corebasichandler.cpp
+++ b/src/core/corebasichandler.cpp
@@ -31,10 +31,13 @@ CoreBasicHandler::CoreBasicHandler(CoreNetwork *parent)
         network(), SLOT(displayMsg(Message::Type, BufferInfo::Type, const QString &, const QString &, const QString &, Message::Flags)));
 
     connect(this, SIGNAL(putCmd(QString, const QList<QByteArray> &, const QByteArray &)),
         network(), SLOT(putCmd(QString, const QList<QByteArray> &, const QByteArray &)));
 
+    connect(this, SIGNAL(putCmd(QString, const QList<QList<QByteArray>> &, const QByteArray &)),
+        network(), SLOT(putCmd(QString, const QList<QList<QByteArray>> &, const QByteArray &)));
+
     connect(this, SIGNAL(putRawLine(const QByteArray &)),
         network(), SLOT(putRawLine(const QByteArray &)));
 }
 
 
diff --git a/src/core/corebasichandler.h b/src/core/corebasichandler.h
index 20d057fc4..a4b5a7f1f 100644
--- a/src/core/corebasichandler.h
+++ b/src/core/corebasichandler.h
@@ -53,10 +53,11 @@ public:
     QList<QByteArray> userEncode(const QString &userNick, const QStringList &stringlist);
 
 signals:
     void displayMsg(Message::Type, BufferInfo::Type, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None);
     void putCmd(const QString &cmd, const QList<QByteArray> &params, const QByteArray &prefix = QByteArray());
+    void putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix = QByteArray());
     void putRawLine(const QByteArray &msg);
 
 protected:
     void putCmd(const QString &cmd, const QByteArray &param, const QByteArray &prefix = QByteArray());
 
diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp
index 7e9ce2681..932af6fc4 100644
--- a/src/core/corenetwork.cpp
+++ b/src/core/corenetwork.cpp
@@ -282,10 +282,20 @@ void CoreNetwork::putCmd(const QString &cmd, const QList<QByteArray> &params, co
 
     putRawLine(msg);
 }
 
 
+void CoreNetwork::putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix)
+{
+    QListIterator<QList<QByteArray>> i(params);
+    while (i.hasNext()) {
+        QList<QByteArray> msg = i.next();
+        putCmd(cmd, msg, prefix);
+    }
+}
+
+
 void CoreNetwork::setChannelJoined(const QString &channel)
 {
     _autoWhoQueue.prepend(channel.toLower()); // prepend so this new chan is the first to be checked
 
     Core::setChannelPersistent(userId(), networkId(), channel, true);
@@ -978,5 +988,81 @@ void CoreNetwork::requestSetNetworkInfo(const NetworkInfo &info)
             _lastUsedServerIndex = i;
             break;
         }
     }
 }
+
+
+QList<QList<QByteArray>> CoreNetwork::splitMessage(const QString &cmd, const QString &message, std::function<QList<QByteArray>(QString &)> cmdGenerator)
+{
+    QString wrkMsg(message);
+    QList<QList<QByteArray>> msgsToSend;
+
+    // do while (wrkMsg.size() > 0)
+    do {
+        // First, check to see if the whole message can be sent at once.  The
+        // cmdGenerator function is passed in by the caller and is used to encode
+        // and encrypt (if applicable) the message, since different callers might
+        // want to use different encoding or encode different values.
+        int splitPos = wrkMsg.size();
+        QList<QByteArray> initialSplitMsgEnc = cmdGenerator(wrkMsg);
+        int initialOverrun = userInputHandler()->lastParamOverrun(cmd, initialSplitMsgEnc);
+
+        if (initialOverrun) {
+            // If the message was too long to be sent, first try splitting it along
+            // word boundaries with QTextBoundaryFinder.
+            QString splitMsg(wrkMsg);
+            QTextBoundaryFinder qtbf(QTextBoundaryFinder::Word, splitMsg);
+            qtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
+            QList<QByteArray> splitMsgEnc;
+            int overrun = initialOverrun;
+
+            while (overrun) {
+                splitPos = qtbf.toPreviousBoundary();
+
+                // splitPos==-1 means the QTBF couldn't find a split point at all and
+                // splitPos==0 means the QTBF could only find a boundary at the beginning of
+                // the string.  Neither one of these works for us.
+                if (splitPos > 0) {
+                    // If a split point could be found, split the message there, calculate the
+                    // overrun, and continue with the loop.
+                    splitMsg = splitMsg.left(splitPos);
+                    splitMsgEnc = cmdGenerator(splitMsg);
+                    overrun = userInputHandler()->lastParamOverrun(cmd, splitMsgEnc);
+                }
+                else {
+                    // If a split point could not be found (the beginning of the message
+                    // is reached without finding a split point short enough to send) and we
+                    // are still in Word mode, switch to Grapheme mode.  We also need to restore
+                    // the full wrkMsg to splitMsg, since splitMsg may have been cut down during
+                    // the previous attempt to find a split point.
+                    if (qtbf.type() == QTextBoundaryFinder::Word) {
+                        splitMsg = wrkMsg;
+                        splitPos = splitMsg.size();
+                        QTextBoundaryFinder graphemeQtbf(QTextBoundaryFinder::Grapheme, splitMsg);
+                        graphemeQtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
+                        qtbf = graphemeQtbf;
+                    }
+                    else {
+                        // If the QTBF fails to find a split point in Grapheme mode, we give up.
+                        // This should never happen, but it should be handled anyway.
+                        qWarning() << "Unexpected failure to split message!";
+                        return msgsToSend;
+                    }
+                }
+            }
+
+            // Once a message of sendable length has been found, remove it from the wrkMsg and
+            // add it to the list of messages to be sent.
+            wrkMsg.remove(0, splitPos);
+            msgsToSend.append(splitMsgEnc);
+        }
+        else{
+            // If the entire remaining message is short enough to be sent all at once, remove
+            // it from the wrkMsg and add it to the list of messages to be sent.
+            wrkMsg.remove(0, splitPos);
+            msgsToSend.append(initialSplitMsgEnc);
+        }
+    } while (wrkMsg.size() > 0);
+
+    return msgsToSend;
+}
diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h
index 87121bab3..05565a47b 100644
--- a/src/core/corenetwork.h
+++ b/src/core/corenetwork.h
@@ -38,10 +38,12 @@
 #  include "cipher.h"
 #endif
 
 #include "coresession.h"
 
+#include <functional>
+
 class CoreIdentity;
 class CoreUserInputHandler;
 class CoreIgnoreListManager;
 class Event;
 
@@ -91,10 +93,12 @@ public:
     inline QHostAddress localAddress() const { return socket.localAddress(); }
     inline QHostAddress peerAddress() const { return socket.peerAddress(); }
     inline quint16 localPort() const { return socket.localPort(); }
     inline quint16 peerPort() const { return socket.peerPort(); }
 
+    QList<QList<QByteArray>> splitMessage(const QString &cmd, const QString &message, std::function<QList<QByteArray>(QString &)> cmdGenerator);
+
 public slots:
     virtual void setMyNick(const QString &mynick);
 
     virtual void requestConnect() const;
     virtual void requestDisconnect() const;
@@ -110,10 +114,11 @@ public slots:
     void disconnectFromIrc(bool requested = true, const QString &reason = QString(), bool withReconnect = false);
 
     void userInput(BufferInfo bufferInfo, QString msg);
     void putRawLine(QByteArray input);
     void putCmd(const QString &cmd, const QList<QByteArray> &params, const QByteArray &prefix = QByteArray());
+    void putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix = QByteArray());
 
     void setChannelJoined(const QString &channel);
     void setChannelParted(const QString &channel);
     void addChannelKey(const QString &channel, const QString &key);
     void removeChannelKey(const QString &channel);
diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp
index 33d1f67a8..72ac99602 100644
--- a/src/core/coreuserinputhandler.cpp
+++ b/src/core/coreuserinputhandler.cpp
@@ -471,16 +471,20 @@ void CoreUserInputHandler::handleMsg(const BufferInfo &bufferInfo, const QString
     Q_UNUSED(bufferInfo);
     if (!msg.contains(' '))
         return;
 
     QString target = msg.section(' ', 0, 0);
-    QByteArray encMsg = userEncode(target, msg.section(' ', 1));
+    QString msgSection = msg.section(' ', 1);
+
+    std::function<QByteArray(const QString &, const QString &)> 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
 }
 
 
 void CoreUserInputHandler::handleNick(const BufferInfo &bufferInfo, const QString &msg)
@@ -592,15 +596,18 @@ void CoreUserInputHandler::handleQuote(const BufferInfo &bufferInfo, const QStri
 void CoreUserInputHandler::handleSay(const BufferInfo &bufferInfo, const QString &msg)
 {
     if (bufferInfo.bufferName().isEmpty() || !bufferInfo.acceptsRegularMessages())
         return;  // server buffer
 
-    QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg);
+    std::function<QByteArray(const QString &, const QString &)> encodeFunc = [this] (const QString &target, const QString &message) -> QByteArray {
+        return channelEncode(target, message);
+    };
+
 #ifdef HAVE_QCA2
-    putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg, network()->cipher(bufferInfo.bufferName()));
+    putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc, network()->cipher(bufferInfo.bufferName()));
 #else
-    putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg);
+    putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc);
 #endif
     emit displayMsg(Message::Plain, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self);
 }
 
 
@@ -761,60 +768,27 @@ void CoreUserInputHandler::defaultHandler(QString cmd, const BufferInfo &bufferI
     Q_UNUSED(bufferInfo);
     emit putCmd(serverEncode(cmd.toUpper()), serverEncode(msg.split(" ")));
 }
 
 
-void CoreUserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message, Cipher *cipher)
+void CoreUserInputHandler::putPrivmsg(const QString &target, const QString &message, std::function<QByteArray(const QString &, const QString &)> 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.
+    QString cmd("PRIVMSG");
+    QByteArray targetEnc = serverEncode(target);
 
-    static const char *cmd = "PRIVMSG";
-    static const char *splitter = " .,-!?";
+    std::function<QList<QByteArray>(QString &)> cmdGenerator = [&] (QString &splitMsg) -> QList<QByteArray> {
+        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<QByteArray>() << 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<QByteArray>() << target << crypted);
-        if (splitPos < message.count())
-            putPrivmsg(target, message.mid(splitPos), cipher);
+        return QList<QByteArray>() << targetEnc << splitMsgEnc;
+    };
 
-        return;
-    }
+    putCmd(cmd, network()->splitMessage(cmd, message, cmdGenerator));
 }
 
 
 // returns 0 if the message will not be chopped by the irc server or number of chopped bytes if message is too long
 int CoreUserInputHandler::lastParamOverrun(const QString &cmd, const QList<QByteArray> &params)
diff --git a/src/core/coreuserinputhandler.h b/src/core/coreuserinputhandler.h
index 69a429ee8..6e69ce605 100644
--- a/src/core/coreuserinputhandler.h
+++ b/src/core/coreuserinputhandler.h
@@ -86,11 +86,11 @@ protected:
     void timerEvent(QTimerEvent *event);
 
 private:
     void doMode(const BufferInfo& bufferInfo, const QChar &addOrRemove, const QChar &mode, const QString &nickList);
     void banOrUnban(const BufferInfo &bufferInfo, const QString &text, bool ban);
-    void putPrivmsg(const QByteArray &target, const QByteArray &message, Cipher *cipher = 0);
+    void putPrivmsg(const QString &target, const QString &message, std::function<QByteArray(const QString &, const QString &)> encodeFunc, Cipher *cipher = 0);
 
 #ifdef HAVE_QCA2
     QByteArray encrypt(const QString &target, const QByteArray &message, bool *didEncrypt = 0) const;
 #endif
 
diff --git a/src/core/ctcpparser.cpp b/src/core/ctcpparser.cpp
index fba3d13e9..37b0af3e9 100644
--- a/src/core/ctcpparser.cpp
+++ b/src/core/ctcpparser.cpp
@@ -310,33 +310,17 @@ QByteArray CtcpParser::pack(const QByteArray &ctcpTag, const QByteArray &message
 }
 
 
 void CtcpParser::query(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message)
 {
-    QList<QByteArray> params;
-    params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message)));
-
-    static const char *splitter = " .,-!?";
-    int maxSplitPos = message.count();
-    int splitPos = maxSplitPos;
+    QString cmd("PRIVMSG");
 
-    int overrun = net->userInputHandler()->lastParamOverrun("PRIVMSG", params);
-    if (overrun) {
-        maxSplitPos = message.count() - overrun -2;
-        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;
-
-        params = params.mid(0, 1) <<  lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message.left(splitPos))));
-    }
-    net->putCmd("PRIVMSG", params);
+    std::function<QList<QByteArray>(QString &)> cmdGenerator = [&] (QString &splitMsg) -> QList<QByteArray> {
+        return QList<QByteArray>() << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, splitMsg)));
+    };
 
-    if (splitPos < message.count())
-        query(net, bufname, ctcpTag, message.mid(splitPos));
+    net->putCmd(cmd, net->splitMessage(cmd, message, cmdGenerator));
 }
 
 
 void CtcpParser::reply(CoreNetwork *net, const QString &bufname, const QString &ctcpTag, const QString &message)
 {

Vulnerable code:     QByteArray encMsg = userEncode(target, msg.section(' ', 1));
    putPrivmsg(serverEncode(target), encMsg, network()->cipher(target));
    putPrivmsg(serverEncode(target), encMsg);
    QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg);
    putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg, network()->cipher(bufferInfo.bufferName()));
    putPrivmsg(serverEncode(bufferInfo.bufferName()), encMsg);
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";
    static const char *splitter = " .,-!?";
    int maxSplitPos = message.count();
    int splitPos = maxSplitPos;
    forever {
        QByteArray crypted = message.left(splitPos);
        bool isEncrypted = false;
        if (cipher && !cipher->key().isEmpty() && !message.isEmpty()) {
            isEncrypted = cipher->encrypt(crypted);
        int overrun = lastParamOverrun(cmd, QList<QByteArray>() << 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<QByteArray>() << target << crypted);
        if (splitPos < message.count())
            putPrivmsg(target, message.mid(splitPos), cipher);
        return;
    }
    void putPrivmsg(const QByteArray &target, const QByteArray &message, Cipher *cipher = 0);
    QList<QByteArray> params;
    params << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message)));

    static const char *splitter = " .,-!?";
    int maxSplitPos = message.count();
    int splitPos = maxSplitPos;
    int overrun = net->userInputHandler()->lastParamOverrun("PRIVMSG", params);
    if (overrun) {
        maxSplitPos = message.count() - overrun -2;
        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;

        params = params.mid(0, 1) <<  lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, message.left(splitPos))));
    }
    net->putCmd("PRIVMSG", params);
    if (splitPos < message.count())
        query(net, bufname, ctcpTag, message.mid(splitPos));
Fixed code:     connect(this, SIGNAL(putCmd(QString, const QList<QList<QByteArray>> &, const QByteArray &)),
        network(), SLOT(putCmd(QString, const QList<QList<QByteArray>> &, const QByteArray &)));

    void putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix = QByteArray());
void CoreNetwork::putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix)
{
    QListIterator<QList<QByteArray>> i(params);
    while (i.hasNext()) {
        QList<QByteArray> msg = i.next();
        putCmd(cmd, msg, prefix);
    }
}




QList<QList<QByteArray>> CoreNetwork::splitMessage(const QString &cmd, const QString &message, std::function<QList<QByteArray>(QString &)> cmdGenerator)
{
    QString wrkMsg(message);
    QList<QList<QByteArray>> msgsToSend;

    // do while (wrkMsg.size() > 0)
    do {
        // First, check to see if the whole message can be sent at once.  The
        // cmdGenerator function is passed in by the caller and is used to encode
        // and encrypt (if applicable) the message, since different callers might
        // want to use different encoding or encode different values.
        int splitPos = wrkMsg.size();
        QList<QByteArray> initialSplitMsgEnc = cmdGenerator(wrkMsg);
        int initialOverrun = userInputHandler()->lastParamOverrun(cmd, initialSplitMsgEnc);

        if (initialOverrun) {
            // If the message was too long to be sent, first try splitting it along
            // word boundaries with QTextBoundaryFinder.
            QString splitMsg(wrkMsg);
            QTextBoundaryFinder qtbf(QTextBoundaryFinder::Word, splitMsg);
            qtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
            QList<QByteArray> splitMsgEnc;
            int overrun = initialOverrun;

            while (overrun) {
                splitPos = qtbf.toPreviousBoundary();

                // splitPos==-1 means the QTBF couldn't find a split point at all and
                // splitPos==0 means the QTBF could only find a boundary at the beginning of
                // the string.  Neither one of these works for us.
                if (splitPos > 0) {
                    // If a split point could be found, split the message there, calculate the
                    // overrun, and continue with the loop.
                    splitMsg = splitMsg.left(splitPos);
                    splitMsgEnc = cmdGenerator(splitMsg);
                    overrun = userInputHandler()->lastParamOverrun(cmd, splitMsgEnc);
                }
                else {
                    // If a split point could not be found (the beginning of the message
                    // is reached without finding a split point short enough to send) and we
                    // are still in Word mode, switch to Grapheme mode.  We also need to restore
                    // the full wrkMsg to splitMsg, since splitMsg may have been cut down during
                    // the previous attempt to find a split point.
                    if (qtbf.type() == QTextBoundaryFinder::Word) {
                        splitMsg = wrkMsg;
                        splitPos = splitMsg.size();
                        QTextBoundaryFinder graphemeQtbf(QTextBoundaryFinder::Grapheme, splitMsg);
                        graphemeQtbf.setPosition(initialSplitMsgEnc[1].size() - initialOverrun);
                        qtbf = graphemeQtbf;
                    }
                    else {
                        // If the QTBF fails to find a split point in Grapheme mode, we give up.
                        // This should never happen, but it should be handled anyway.
                        qWarning() << "Unexpected failure to split message!";
                        return msgsToSend;
                    }
                }
            }

            // Once a message of sendable length has been found, remove it from the wrkMsg and
            // add it to the list of messages to be sent.
            wrkMsg.remove(0, splitPos);
            msgsToSend.append(splitMsgEnc);
        }
        else{
            // If the entire remaining message is short enough to be sent all at once, remove
            // it from the wrkMsg and add it to the list of messages to be sent.
            wrkMsg.remove(0, splitPos);
            msgsToSend.append(initialSplitMsgEnc);
        }
    } while (wrkMsg.size() > 0);

    return msgsToSend;
}
#include <functional>

    QList<QList<QByteArray>> splitMessage(const QString &cmd, const QString &message, std::function<QList<QByteArray>(QString &)> cmdGenerator);

    void putCmd(const QString &cmd, const QList<QList<QByteArray>> &params, const QByteArray &prefix = QByteArray());
    QString msgSection = msg.section(' ', 1);

    std::function<QByteArray(const QString &, const QString &)> encodeFunc = [this] (const QString &target, const QString &message) -> QByteArray {
        return userEncode(target, message);
    };
    putPrivmsg(target, msgSection, encodeFunc, network()->cipher(target));
    putPrivmsg(target, msgSection, encodeFunc);
    std::function<QByteArray(const QString &, const QString &)> encodeFunc = [this] (const QString &target, const QString &message) -> QByteArray {
        return channelEncode(target, message);
    };

    putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc, network()->cipher(bufferInfo.bufferName()));
    putPrivmsg(bufferInfo.bufferName(), msg, encodeFunc);
void CoreUserInputHandler::putPrivmsg(const QString &target, const QString &message, std::function<QByteArray(const QString &, const QString &)> encodeFunc, Cipher *cipher)
    QString cmd("PRIVMSG");
    QByteArray targetEnc = serverEncode(target);
    std::function<QList<QByteArray>(QString &)> cmdGenerator = [&] (QString &splitMsg) -> QList<QByteArray> {
        QByteArray splitMsgEnc = encodeFunc(target, splitMsg);
        if (cipher && !cipher->key().isEmpty() && !splitMsg.isEmpty()) {
            cipher->encrypt(splitMsgEnc);
        return QList<QByteArray>() << targetEnc << splitMsgEnc;
    };
    putCmd(cmd, network()->splitMessage(cmd, message, cmdGenerator));
    void putPrivmsg(const QString &target, const QString &message, std::function<QByteArray(const QString &, const QString &)> encodeFunc, Cipher *cipher = 0);
    QString cmd("PRIVMSG");
    std::function<QList<QByteArray>(QString &)> cmdGenerator = [&] (QString &splitMsg) -> QList<QByteArray> {
        return QList<QByteArray>() << net->serverEncode(bufname) << lowLevelQuote(pack(net->serverEncode(ctcpTag), net->userEncode(bufname, splitMsg)));
    };
    net->putCmd(cmd, net->splitMessage(cmd, message, cmdGenerator));