uisupport: Provide helpers for dealing with widget changes
[quassel.git] / src / core / identserver.cpp
index 21ea5ff..1841156 100644 (file)
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
-#include <logger.h>
+#include <limits>
 
 #include "corenetwork.h"
 #include "identserver.h"
+#include "logmessage.h"
 
-IdentServer::IdentServer(bool strict, QObject *parent) : QObject(parent), _strict(strict) {
-    connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
-    connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
+IdentServer::IdentServer(QObject *parent)
+    : QObject(parent)
+{
+    connect(&_server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
+    connect(&_v6server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
 }
 
-IdentServer::~IdentServer() = default;
 
-bool IdentServer::startListening() {
-    uint16_t port = 10113;
+bool IdentServer::startListening()
+{
+    uint16_t port = Quassel::optionValue("ident-port").toUShort();
 
     bool success = false;
     if (_v6server.listen(QHostAddress("::1"), port)) {
@@ -45,25 +48,27 @@ bool IdentServer::startListening() {
     }
 
     if (_server.listen(QHostAddress("127.0.0.1"), port)) {
-        success = true;
-
         quInfo() << qPrintable(
                 tr("Listening for identd clients on IPv4 %1 port %2")
                         .arg("127.0.0.1")
                         .arg(_server.serverPort())
         );
+
+        success = true;
     }
 
     if (!success) {
-        quError() << qPrintable(
-                tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
+        quError() << qPrintable(tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
     }
 
     return success;
 }
 
-void IdentServer::stopListening(const QString &msg) {
+
+void IdentServer::stopListening(const QString &msg)
+{
     bool wasListening = false;
+
     if (_server.isListening()) {
         wasListening = true;
         _server.close();
@@ -72,6 +77,7 @@ void IdentServer::stopListening(const QString &msg) {
         wasListening = true;
         _v6server.close();
     }
+
     if (wasListening) {
         if (msg.isEmpty())
             quInfo() << "No longer listening for identd clients.";
@@ -80,79 +86,164 @@ void IdentServer::stopListening(const QString &msg) {
     }
 }
 
-void IdentServer::incomingConnection() {
-    auto *server = qobject_cast<QTcpServer *>(sender());
+
+void IdentServer::incomingConnection()
+{
+    auto server = qobject_cast<QTcpServer *>(sender());
     Q_ASSERT(server);
     while (server->hasPendingConnections()) {
         QTcpSocket *socket = server->nextPendingConnection();
-        connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
+        connect(socket, &QIODevice::readyRead, this, &IdentServer::respond);
+        connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
     }
 }
 
-void IdentServer::respond() {
+
+void IdentServer::respond()
+{
     auto *socket = qobject_cast<QTcpSocket *>(sender());
     Q_ASSERT(socket);
 
-    if (socket->canReadLine()) {
-        QByteArray s = socket->readLine();
-        if (s.endsWith("\r\n"))
-            s.chop(2);
-        else if (s.endsWith("\n"))
-            s.chop(1);
+    qint64 transactionId = _socketId;
 
-        QList<QByteArray> split = s.split(',');
+    if (!socket->canReadLine()) {
+        return;
+    }
 
-        bool success = false;
+    QByteArray query = socket->readLine();
+    if (query.endsWith("\r\n"))
+        query.chop(2);
+    else if (query.endsWith("\n"))
+        query.chop(1);
 
-        uint16_t localPort;
-        if (!split.empty()) {
-            localPort = split[0].toUShort(&success, 10);
-        }
+    QList<QByteArray> split = query.split(',');
 
-        QString user;
-        if (success) {
-            if (_connections.contains(localPort)) {
-                user = _connections[localPort];
-            } else {
-                success = false;
-            }
-        }
+    bool success = false;
+
+    quint16 localPort = 0;
+    if (!split.empty()) {
+        localPort = split[0].trimmed().toUShort(&success, 10);
+    }
+
+    Request request{socket, localPort, query, transactionId, _requestId++};
+    if (!success) {
+        request.respondError("INVALID-PORT");
+    }
+    else if (responseAvailable(request)) {
+        // success
+    }
+    else if (lowestSocketId() < transactionId) {
+        _requestQueue.emplace_back(request);
+    }
+    else {
+        request.respondError("NO-USER");
+    }
+}
+
+
+void Request::respondSuccess(const QString &user)
+{
+    if (socket) {
+        QString data = query + " : USERID : Quassel : " + user + "\r\n";
+        socket->write(data.toUtf8());
+        socket->flush();
+        socket->close();
+    }
+}
 
-        QString data;
-        if (success) {
-            data += s + " : USERID : Quassel : " + user + "\r\n";
-        } else {
-            data += s + " : ERROR : NO-USER\r\n";
-        }
 
+void Request::respondError(const QString &error)
+{
+    if (socket) {
+        QString data = query + " : ERROR : " + error + "\r\n";
         socket->write(data.toUtf8());
         socket->flush();
         socket->close();
-        socket->deleteLater();
     }
 }
 
 
-bool IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
-                            const QHostAddress &peerAddress, quint16 peerPort) {
+bool IdentServer::responseAvailable(Request request) const
+{
+    if (!_connections.contains(request.localPort)) {
+        return false;
+    }
+
+    request.respondSuccess(_connections[request.localPort]);
+    return true;
+}
+
+
+void IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
+                            const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
+{
     Q_UNUSED(localAddress)
     Q_UNUSED(peerAddress)
     Q_UNUSED(peerPort)
 
     const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
     _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
-    return true;
+    processWaiting(socketId);
 }
 
 
-//! not yet implemented
-bool IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
-                               const QHostAddress &peerAddress, quint16 peerPort) {
+void IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
+                               const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
+{
     Q_UNUSED(identity)
     Q_UNUSED(localAddress)
     Q_UNUSED(peerAddress)
     Q_UNUSED(peerPort)
 
     _connections.remove(localPort);
-    return true;
+    processWaiting(socketId);
+}
+
+
+qint64 IdentServer::addWaitingSocket()
+{
+    qint64 newSocketId = _socketId++;
+    _waiting.push_back(newSocketId);
+    return newSocketId;
+}
+
+
+qint64 IdentServer::lowestSocketId() const
+{
+    if (_waiting.empty()) {
+        return std::numeric_limits<qint64>::max();
+    }
+
+    return _waiting.front();
+}
+
+
+void IdentServer::removeWaitingSocket(qint64 socketId)
+{
+    _waiting.remove(socketId);
+}
+
+
+void IdentServer::processWaiting(qint64 socketId)
+{
+    removeWaitingSocket(socketId);
+
+    _requestQueue.remove_if([this, socketId](Request request) {
+        if (socketId < request.transactionId && responseAvailable(request)) {
+            return true;
+        }
+        else if (lowestSocketId() < request.transactionId) {
+            return false;
+        }
+        else {
+            request.respondError("NO-USER");
+            return true;
+        }
+    });
+}
+
+
+bool operator==(const Request &a, const Request &b)
+{
+    return a.requestId == b.requestId;
 }