modernize: Use '= default' instead of empty ctor/dtor bodies
[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 <limits>
22
23 #include "corenetwork.h"
24 #include "identserver.h"
25 #include "logmessage.h"
26
27 IdentServer::IdentServer(QObject *parent)
28     : QObject(parent)
29 {
30     connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
31     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
32 }
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(
42                 tr("Listening for identd clients on IPv6 %1 port %2")
43                         .arg("::1")
44                         .arg(_v6server.serverPort())
45         );
46
47         success = true;
48     }
49
50     if (_server.listen(QHostAddress("127.0.0.1"), port)) {
51         quInfo() << qPrintable(
52                 tr("Listening for identd clients on IPv4 %1 port %2")
53                         .arg("127.0.0.1")
54                         .arg(_server.serverPort())
55         );
56
57         success = true;
58     }
59
60     if (!success) {
61         quError() << qPrintable(tr("Identd could not open any network interfaces to listen on! No identd functionality will be available"));
62     }
63
64     return success;
65 }
66
67
68 void IdentServer::stopListening(const QString &msg)
69 {
70     bool wasListening = false;
71
72     if (_server.isListening()) {
73         wasListening = true;
74         _server.close();
75     }
76     if (_v6server.isListening()) {
77         wasListening = true;
78         _v6server.close();
79     }
80
81     if (wasListening) {
82         if (msg.isEmpty())
83             quInfo() << "No longer listening for identd clients.";
84         else
85             quInfo() << qPrintable(msg);
86     }
87 }
88
89
90 void IdentServer::incomingConnection()
91 {
92     auto server = qobject_cast<QTcpServer *>(sender());
93     Q_ASSERT(server);
94     while (server->hasPendingConnections()) {
95         QTcpSocket *socket = server->nextPendingConnection();
96         connect(socket, SIGNAL(readyRead()), this, SLOT(respond()));
97         connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
98     }
99 }
100
101
102 void IdentServer::respond()
103 {
104     QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
105     Q_ASSERT(socket);
106
107     qint64 transactionId = _socketId;
108
109     if (!socket->canReadLine()) {
110         return;
111     }
112
113     QByteArray query = socket->readLine();
114     if (query.endsWith("\r\n"))
115         query.chop(2);
116     else if (query.endsWith("\n"))
117         query.chop(1);
118
119     QList<QByteArray> split = query.split(',');
120
121     bool success = false;
122
123     quint16 localPort = 0;
124     if (!split.empty()) {
125         localPort = split[0].trimmed().toUShort(&success, 10);
126     }
127
128     Request request{socket, localPort, query, transactionId, _requestId++};
129     if (!success) {
130         request.respondError("INVALID-PORT");
131     }
132     else if (responseAvailable(request)) {
133         // success
134     }
135     else if (lowestSocketId() < transactionId) {
136         _requestQueue.emplace_back(request);
137     }
138     else {
139         request.respondError("NO-USER");
140     }
141 }
142
143
144 void Request::respondSuccess(const QString &user)
145 {
146     if (socket) {
147         QString data = query + " : USERID : Quassel : " + user + "\r\n";
148         socket->write(data.toUtf8());
149         socket->flush();
150         socket->close();
151     }
152 }
153
154
155 void Request::respondError(const QString &error)
156 {
157     if (socket) {
158         QString data = query + " : ERROR : " + error + "\r\n";
159         socket->write(data.toUtf8());
160         socket->flush();
161         socket->close();
162     }
163 }
164
165
166 bool IdentServer::responseAvailable(Request request) const
167 {
168     if (!_connections.contains(request.localPort)) {
169         return false;
170     }
171
172     request.respondSuccess(_connections[request.localPort]);
173     return true;
174 }
175
176
177 void IdentServer::addSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
178                             const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
179 {
180     Q_UNUSED(localAddress)
181     Q_UNUSED(peerAddress)
182     Q_UNUSED(peerPort)
183
184     const CoreNetwork *network = qobject_cast<CoreNetwork *>(sender());
185     _connections[localPort] = network->coreSession()->strictCompliantIdent(identity);;
186     processWaiting(socketId);
187 }
188
189
190 void IdentServer::removeSocket(const CoreIdentity *identity, const QHostAddress &localAddress, quint16 localPort,
191                                const QHostAddress &peerAddress, quint16 peerPort, qint64 socketId)
192 {
193     Q_UNUSED(identity)
194     Q_UNUSED(localAddress)
195     Q_UNUSED(peerAddress)
196     Q_UNUSED(peerPort)
197
198     _connections.remove(localPort);
199     processWaiting(socketId);
200 }
201
202
203 qint64 IdentServer::addWaitingSocket()
204 {
205     qint64 newSocketId = _socketId++;
206     _waiting.push_back(newSocketId);
207     return newSocketId;
208 }
209
210
211 qint64 IdentServer::lowestSocketId() const
212 {
213     if (_waiting.empty()) {
214         return std::numeric_limits<qint64>::max();
215     }
216
217     return _waiting.front();
218 }
219
220
221 void IdentServer::removeWaitingSocket(qint64 socketId)
222 {
223     _waiting.remove(socketId);
224 }
225
226
227 void IdentServer::processWaiting(qint64 socketId)
228 {
229     removeWaitingSocket(socketId);
230
231     _requestQueue.remove_if([this, socketId](Request request) {
232         if (socketId < request.transactionId && responseAvailable(request)) {
233             return true;
234         }
235         else if (lowestSocketId() < request.transactionId) {
236             return false;
237         }
238         else {
239             request.respondError("NO-USER");
240             return true;
241         }
242     });
243 }
244
245
246 bool operator==(const Request &a, const Request &b)
247 {
248     return a.requestId == b.requestId;
249 }