Save Main ToolBar state when not built against KDE. Fixes #1116
[quassel.git] / src / client / clientauthhandler.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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 "clientauthhandler.h"
22
23 // TODO: support system application proxy (new in Qt 4.6)
24
25 #ifdef HAVE_SSL
26     #include <QSslSocket>
27 #else
28     #include <QTcpSocket>
29 #endif
30
31 #include "client.h"
32 #include "clientsettings.h"
33
34 #include "protocols/legacy/legacypeer.h"
35
36 using namespace Protocol;
37
38 ClientAuthHandler::ClientAuthHandler(CoreAccount account, QObject *parent)
39     : AuthHandler(parent),
40     _peer(0),
41     _account(account)
42 {
43
44 }
45
46
47 void ClientAuthHandler::connectToCore()
48 {
49     CoreAccountSettings s;
50
51 #ifdef HAVE_SSL
52     QSslSocket *socket = new QSslSocket(this);
53     // make sure the warning is shown if we happen to connect without SSL support later
54     s.setAccountValue("ShowNoClientSslWarning", true);
55 #else
56     if (_account.useSsl()) {
57         if (s.accountValue("ShowNoClientSslWarning", true).toBool()) {
58             bool accepted = false;
59             emit handleNoSslInClient(&accepted);
60             if (!accepted) {
61                 emit errorMessage(tr("Unencrypted connection canceled"));
62                 return;
63             }
64             s.setAccountValue("ShowNoClientSslWarning", false);
65         }
66     }
67     QTcpSocket *socket = new QTcpSocket(this);
68 #endif
69
70 // TODO: Handle system proxy
71 #ifndef QT_NO_NETWORKPROXY
72     if (_account.useProxy()) {
73         QNetworkProxy proxy(_account.proxyType(), _account.proxyHostName(), _account.proxyPort(), _account.proxyUser(), _account.proxyPassword());
74         socket->setProxy(proxy);
75     }
76 #endif
77
78     setSocket(socket);
79     // handled by the base class for now; may need to rethink for protocol detection
80     //connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError)));
81     connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
82     connect(socket, SIGNAL(connected()), SLOT(onSocketConnected()));
83
84     emit statusMessage(tr("Connecting to %1...").arg(_account.accountName()));
85     socket->connectToHost(_account.hostName(), _account.port());
86 }
87
88
89 // TODO: handle protocol detection
90 void ClientAuthHandler::onSocketStateChanged(QAbstractSocket::SocketState socketState)
91 {
92     QString text;
93
94     switch(socketState) {
95         case QAbstractSocket::UnconnectedState:
96             text = tr("Disconnected");
97             // Ensure the disconnected() signal is sent even if we haven't reached the Connected state yet.
98             // The baseclass implementation will make sure to only send the signal once.
99             onSocketDisconnected();
100             break;
101         case QAbstractSocket::HostLookupState:
102             text = tr("Looking up %1...").arg(_account.hostName());
103             break;
104         case QAbstractSocket::ConnectingState:
105             text = tr("Connecting to %1...").arg(_account.hostName());
106             break;
107         case QAbstractSocket::ConnectedState:
108             text = tr("Connected to %1").arg(_account.hostName());
109             break;
110         case QAbstractSocket::ClosingState:
111             text = tr("Disconnecting from %1...").arg(_account.hostName());
112             break;
113         default:
114             break;
115     }
116
117     if (!text.isEmpty()) {
118         emit statusMessage(text);
119     }
120 }
121
122 // TODO: handle protocol detection
123 /*
124 void ClientAuthHandler::onSocketError(QAbstractSocket::SocketError error)
125 {
126     emit socketError(error, socket()->errorString());
127 }
128 */
129
130 void ClientAuthHandler::onSocketConnected()
131 {
132     // TODO: protocol detection
133
134     if (_peer) {
135         qWarning() << Q_FUNC_INFO << "Peer already exists!";
136         return;
137     }
138
139     socket()->setSocketOption(QAbstractSocket::KeepAliveOption, true);
140
141     _peer = new LegacyPeer(this, socket(), this);
142
143     connect(_peer, SIGNAL(transferProgress(int,int)), SIGNAL(transferProgress(int,int)));
144
145     // compat only
146     connect(_peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
147
148     emit statusMessage(tr("Synchronizing to core..."));
149
150     bool useSsl = false;
151 #ifdef HAVE_SSL
152     useSsl = _account.useSsl();
153 #endif
154
155     _peer->dispatch(RegisterClient(Quassel::buildInfo().fancyVersionString, useSsl));
156 }
157
158
159 void ClientAuthHandler::onProtocolVersionMismatch(int actual, int expected)
160 {
161     emit errorPopup(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
162            "We need at least protocol v%1, but the core speaks v%2 only.").arg(expected, actual));
163     requestDisconnect(tr("Incompatible protocol version, connection to core refused"));
164 }
165
166
167 void ClientAuthHandler::handle(const ClientDenied &msg)
168 {
169     emit errorPopup(msg.errorString);
170     requestDisconnect(tr("The core refused connection from this client"));
171 }
172
173
174 void ClientAuthHandler::handle(const ClientRegistered &msg)
175 {
176     _coreConfigured = msg.coreConfigured;
177     _backendInfo = msg.backendInfo;
178
179     Client::setCoreFeatures(static_cast<Quassel::Features>(msg.coreFeatures));
180
181 #ifdef HAVE_SSL
182     CoreAccountSettings s;
183     if (_account.useSsl()) {
184         if (msg.sslSupported) {
185             // Make sure the warning is shown next time we don't have SSL in the core
186             s.setAccountValue("ShowNoCoreSslWarning", true);
187
188             QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
189             Q_ASSERT(sslSocket);
190             connect(sslSocket, SIGNAL(encrypted()), SLOT(onSslSocketEncrypted()));
191             connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), SLOT(onSslErrors()));
192             qDebug() << "Starting encryption...";
193             sslSocket->flush();
194             sslSocket->startClientEncryption();
195         }
196         else {
197             if (s.accountValue("ShowNoCoreSslWarning", true).toBool()) {
198                 bool accepted = false;
199                 emit handleNoSslInCore(&accepted);
200                 if (!accepted) {
201                     requestDisconnect(tr("Unencrypted connection cancelled"));
202                     return;
203                 }
204                 s.setAccountValue("ShowNoCoreSslWarning", false);
205                 s.setAccountValue("SslCert", QString());
206             }
207             onConnectionReady();
208         }
209         return;
210     }
211 #endif
212     // if we use SSL we wait for the next step until every SSL warning has been cleared
213     onConnectionReady();
214 }
215
216
217 #ifdef HAVE_SSL
218
219 void ClientAuthHandler::onSslSocketEncrypted()
220 {
221     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
222     Q_ASSERT(socket);
223
224     if (!socket->sslErrors().count()) {
225         // Cert is valid, so we don't want to store it as known
226         // That way, a warning will appear in case it becomes invalid at some point
227         CoreAccountSettings s;
228         s.setAccountValue("SSLCert", QString());
229     }
230
231     emit encrypted(true);
232     onConnectionReady();
233 }
234
235
236 void ClientAuthHandler::onSslErrors()
237 {
238     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
239     Q_ASSERT(socket);
240
241     CoreAccountSettings s;
242     QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
243
244     if (knownDigest != socket->peerCertificate().digest()) {
245         bool accepted = false;
246         bool permanently = false;
247         emit handleSslErrors(socket, &accepted, &permanently);
248
249         if (!accepted) {
250             requestDisconnect(tr("Unencrypted connection canceled"));
251             return;
252         }
253
254         if (permanently)
255             s.setAccountValue("SslCert", socket->peerCertificate().digest());
256         else
257             s.setAccountValue("SslCert", QString());
258     }
259
260     socket->ignoreSslErrors();
261 }
262
263 #endif /* HAVE_SSL */
264
265
266 void ClientAuthHandler::onConnectionReady()
267 {
268     emit connectionReady();
269     emit statusMessage(tr("Connected to %1").arg(_account.accountName()));
270
271     if (!_coreConfigured) {
272         // start wizard
273         emit startCoreSetup(_backendInfo);
274     }
275     else // TODO: check if we need LoginEnabled
276         login();
277 }
278
279
280 void ClientAuthHandler::setupCore(const SetupData &setupData)
281 {
282     _peer->dispatch(setupData);
283 }
284
285
286 void ClientAuthHandler::handle(const SetupFailed &msg)
287 {
288     emit coreSetupFailed(msg.errorString);
289 }
290
291
292 void ClientAuthHandler::handle(const SetupDone &msg)
293 {
294     Q_UNUSED(msg)
295
296     emit coreSetupSuccessful();
297 }
298
299
300 void ClientAuthHandler::login(const QString &user, const QString &password, bool remember)
301 {
302     _account.setUser(user);
303     _account.setPassword(password);
304     _account.setStorePassword(remember);
305     login();
306 }
307
308
309 void ClientAuthHandler::login(const QString &previousError)
310 {
311     emit statusMessage(tr("Logging in..."));
312     if (_account.user().isEmpty() || _account.password().isEmpty() || !previousError.isEmpty()) {
313         bool valid = false;
314         emit userAuthenticationRequired(&_account, &valid, previousError); // *must* be a synchronous call
315         if (!valid || _account.user().isEmpty() || _account.password().isEmpty()) {
316             requestDisconnect(tr("Login canceled"));
317             return;
318         }
319     }
320
321     _peer->dispatch(Login(_account.user(), _account.password()));
322 }
323
324
325 void ClientAuthHandler::handle(const LoginFailed &msg)
326 {
327     login(msg.errorString);
328 }
329
330
331 void ClientAuthHandler::handle(const LoginSuccess &msg)
332 {
333     Q_UNUSED(msg)
334
335     emit loginSuccessful(_account);
336 }
337
338
339 void ClientAuthHandler::handle(const SessionState &msg)
340 {
341     disconnect(socket(), 0, this, 0); // this is the last message we shall ever get
342
343     // give up ownership of the peer; CoreSession takes responsibility now
344     _peer->setParent(0);
345     emit handshakeComplete(_peer, msg);
346 }