Call socketDisconnected() from socketStateChanged()
[quassel.git] / src / core / corenetwork.cpp
index 80986f0..942e32f 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2014 by the Quassel Project                        *
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -71,7 +71,6 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
     connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue()));
 
     connect(&socket, SIGNAL(connected()), this, SLOT(socketInitialized()));
-    connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
     connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
     connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
     connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
@@ -82,7 +81,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
     connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *)));
 
     if (Quassel::isOptionSet("oidentd")) {
-        connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection);
+        connect(this, SIGNAL(socketOpen(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection);
         connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)));
     }
 }
@@ -232,17 +231,18 @@ void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool
         _quitReason = reason;
 
     displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting. (%1)").arg((!requested && !withReconnect) ? tr("Core Shutdown") : _quitReason));
-    switch (socket.state()) {
-    case QAbstractSocket::ConnectedState:
-        userInputHandler()->issueQuit(_quitReason);
+    if (socket.state() == QAbstractSocket::UnconnectedState) {
+        socketDisconnected();
+    } else {
+        if (socket.state() == QAbstractSocket::ConnectedState) {
+            userInputHandler()->issueQuit(_quitReason);
+        } else {
+            socket.close();
+        }
         if (requested || withReconnect) {
             // the irc server has 10 seconds to close the socket
             _socketCloseTimer.start(10000);
-            break;
         }
-    default:
-        socket.close();
-        socketDisconnected();
     }
 }
 
@@ -283,6 +283,16 @@ void CoreNetwork::putCmd(const QString &cmd, const QList<QByteArray> &params, co
 }
 
 
+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
@@ -416,11 +426,7 @@ void CoreNetwork::socketHasData()
         else if (s.endsWith("\n"))
             s.chop(1);
         NetworkDataEvent *event = new NetworkDataEvent(EventManager::NetworkIncoming, this, s);
-#if QT_VERSION >= 0x040700
         event->setTimestamp(QDateTime::currentDateTimeUtc());
-#else
-        event->setTimestamp(QDateTime::currentDateTime().toUTC());
-#endif
         emit newEvent(event);
     }
 }
@@ -444,14 +450,6 @@ void CoreNetwork::socketError(QAbstractSocket::SocketError error)
 
 void CoreNetwork::socketInitialized()
 {
-    Server server = usedServer();
-#ifdef HAVE_SSL
-    if (server.useSsl && !socket.isEncrypted())
-        return;
-#endif
-#if QT_VERSION >= 0x040600
-    socket.setSocketOption(QAbstractSocket::KeepAliveOption, true);
-#endif
     CoreIdentity *identity = identityPtr();
     if (!identity) {
         qCritical() << "Identity invalid!";
@@ -459,6 +457,15 @@ void CoreNetwork::socketInitialized()
         return;
     }
 
+    emit socketOpen(identity, localAddress(), localPort(), peerAddress(), peerPort());
+
+    Server server = usedServer();
+#ifdef HAVE_SSL
+    if (server.useSsl && !socket.isEncrypted())
+        return;
+#endif
+    socket.setSocketOption(QAbstractSocket::KeepAliveOption, true);
+
     emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort());
 
     // TokenBucket to avoid sending too much at once
@@ -530,6 +537,7 @@ void CoreNetwork::socketStateChanged(QAbstractSocket::SocketState socketState)
     switch (socketState) {
     case QAbstractSocket::UnconnectedState:
         state = Network::Disconnected;
+        socketDisconnected();
         break;
     case QAbstractSocket::HostLookupState:
     case QAbstractSocket::ConnectingState:
@@ -982,3 +990,79 @@ void CoreNetwork::requestSetNetworkInfo(const NetworkInfo &info)
         }
     }
 }
+
+
+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;
+}