modernize: Reformat ALL the source... again!
[quassel.git] / src / core / identserver.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "identserver.h"
22
23 #include <limits>
24
25 #include "corenetwork.h"
26 #include "logmessage.h"
27
28 IdentServer::IdentServer(QObject* parent)
29     : QObject(parent)
30 {
31     connect(&_server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
32     connect(&_v6server, &QTcpServer::newConnection, this, &IdentServer::incomingConnection);
33 }
34
35 bool IdentServer::startListening()
36 {
37     uint16_t port = Quassel::optionValue("ident-port").toUShort();
38
39     bool success = false;
40     if (_v6server.listen(QHostAddress("::1"), port)) {
41         quInfo() << qPrintable(tr("Listening for identd clients on IPv6 %1 port %2").arg("::1").arg(_v6server.serverPort()));
42
43         success = true;
44     }
45
46     if (_server.listen(QHostAddress("127.0.0.1"), port)) {
47         quInfo() << qPrintable(tr("Listening for identd clients on IPv4 %1 port %2").arg("127.0.0.1").arg(_server.serverPort()));
48
49         success = true;
50     }
51
52     if (!success) {
53         quError() << qPrintable(tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
54     }
55
56     return success;
57 }
58
59 void IdentServer::stopListening(const QString& msg)
60 {
61     bool wasListening = false;
62
63     if (_server.isListening()) {
64         wasListening = true;
65         _server.close();
66     }
67     if (_v6server.isListening()) {
68         wasListening = true;
69         _v6server.close();
70     }
71
72     if (wasListening) {
73         if (msg.isEmpty())
74             quInfo() << "No longer listening for identd clients.";
75         else
76             quInfo() << qPrintable(msg);
77     }
78 }
79
80 void IdentServer::incomingConnection()
81 {
82     auto server = qobject_cast<QTcpServer*>(sender());
83     Q_ASSERT(server);
84     while (server->hasPendingConnections()) {
85         QTcpSocket* socket = server->nextPendingConnection();
86         connect(socket, &QIODevice::readyRead, this, &IdentServer::respond);
87         connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
88     }
89 }
90
91 void IdentServer::respond()
92 {
93     auto* socket = qobject_cast<QTcpSocket*>(sender());
94     Q_ASSERT(socket);
95
96     qint64 transactionId = _socketId;
97
98     if (!socket->canReadLine()) {
99         return;
100     }
101
102     QByteArray query = socket->readLine();
103     if (query.endsWith("\r\n"))
104         query.chop(2);
105     else if (query.endsWith("\n"))
106         query.chop(1);
107
108     QList<QByteArray> split = query.split(',');
109
110     bool success = false;
111
112     quint16 localPort = 0;
113     if (!split.empty()) {
114         localPort = split[0].trimmed().toUShort(&success, 10);
115     }
116
117     Request request{socket, localPort, query, transactionId, _requestId++};
118     if (!success) {
119         request.respondError("INVALID-PORT");
120     }
121     else if (responseAvailable(request)) {
122         // success
123     }
124     else if (lowestSocketId() < transactionId) {
125         _requestQueue.emplace_back(request);
126     }
127     else {
128         request.respondError("NO-USER");
129     }
130 }
131
132 void Request::respondSuccess(const QString& user)
133 {
134     if (socket) {
135         QString data = query + " : USERID : Quassel : " + user + "\r\n";
136         socket->write(data.toUtf8());
137         socket->flush();
138         socket->close();
139     }
140 }
141
142 void Request::respondError(const QString& error)
143 {
144     if (socket) {
145         QString data = query + " : ERROR : " + error + "\r\n";
146         socket->write(data.toUtf8());
147         socket->flush();
148         socket->close();
149     }
150 }
151
152 bool IdentServer::responseAvailable(Request request) const
153 {
154     if (!_connections.contains(request.localPort)) {
155         return false;
156     }
157
158     request.respondSuccess(_connections[request.localPort]);
159     return true;
160 }
161
162 void IdentServer::addSocket(const CoreIdentity* identity,
163                             const QHostAddress& localAddress,
164                             quint16 localPort,
165                             const QHostAddress& peerAddress,
166                             quint16 peerPort,
167                             qint64 socketId)
168 {
169     Q_UNUSED(localAddress)
170     Q_UNUSED(peerAddress)
171     Q_UNUSED(peerPort)
172
173     const CoreNetwork* network = qobject_cast<CoreNetwork*>(sender());
174     _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);
175     ;
176     processWaiting(socketId);
177 }
178
179 void IdentServer::removeSocket(const CoreIdentity* identity,
180                                const QHostAddress& localAddress,
181                                quint16 localPort,
182                                const QHostAddress& peerAddress,
183                                quint16 peerPort,
184                                qint64 socketId)
185 {
186     Q_UNUSED(identity)
187     Q_UNUSED(localAddress)
188     Q_UNUSED(peerAddress)
189     Q_UNUSED(peerPort)
190
191     _connections.remove(localPort);
192     processWaiting(socketId);
193 }
194
195 qint64 IdentServer::addWaitingSocket()
196 {
197     qint64 newSocketId = _socketId++;
198     _waiting.push_back(newSocketId);
199     return newSocketId;
200 }
201
202 qint64 IdentServer::lowestSocketId() const
203 {
204     if (_waiting.empty()) {
205         return std::numeric_limits<qint64>::max();
206     }
207
208     return _waiting.front();
209 }
210
211 void IdentServer::removeWaitingSocket(qint64 socketId)
212 {
213     _waiting.remove(socketId);
214 }
215
216 void IdentServer::processWaiting(qint64 socketId)
217 {
218     removeWaitingSocket(socketId);
219
220     _requestQueue.remove_if([this, socketId](Request request) {
221         if (socketId < request.transactionId && responseAvailable(request)) {
222             return true;
223         }
224         else if (lowestSocketId() < request.transactionId) {
225             return false;
226         }
227         else {
228             request.respondError("NO-USER");
229             return true;
230         }
231     });
232 }
233
234 bool operator==(const Request& a, const Request& b)
235 {
236     return a.requestId == b.requestId;
237 }