Add a flag to enable Qt deprecation warnings on Qt < 5.13
[quassel.git] / src / client / coreconnection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2019 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 "coreconnection.h"
22
23 #include "client.h"
24 #include "clientauthhandler.h"
25 #include "clientsettings.h"
26 #include "coreaccountmodel.h"
27 #include "identity.h"
28 #include "internalpeer.h"
29 #include "network.h"
30 #include "networkmodel.h"
31 #include "quassel.h"
32 #include "signalproxy.h"
33 #include "util.h"
34
35 #include "protocols/legacy/legacypeer.h"
36
37 CoreConnection::CoreConnection(QObject* parent)
38     : QObject(parent)
39     , _authHandler(nullptr)
40 {
41     qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
42 }
43
44 void CoreConnection::init()
45 {
46     Client::signalProxy()->setHeartBeatInterval(30);
47     connect(Client::signalProxy(), &SignalProxy::lagUpdated, this, &CoreConnection::lagUpdated);
48
49     _reconnectTimer.setSingleShot(true);
50     connect(&_reconnectTimer, &QTimer::timeout, this, &CoreConnection::reconnectTimeout);
51
52     _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
53     connect(_qNetworkConfigurationManager.data(), &QNetworkConfigurationManager::onlineStateChanged, this, &CoreConnection::onlineStateChanged);
54
55     CoreConnectionSettings s;
56     s.initAndNotify("PingTimeoutInterval", this, &CoreConnection::pingTimeoutIntervalChanged, 60);
57     s.initAndNotify("ReconnectInterval", this, &CoreConnection::reconnectIntervalChanged, 60);
58     s.notify("NetworkDetectionMode", this, &CoreConnection::networkDetectionModeChanged);
59     networkDetectionModeChanged(s.networkDetectionMode());
60 }
61
62 CoreAccountModel* CoreConnection::accountModel() const
63 {
64     return Client::coreAccountModel();
65 }
66
67 void CoreConnection::setProgressText(const QString& text)
68 {
69     if (_progressText != text) {
70         _progressText = text;
71         emit progressTextChanged(text);
72     }
73 }
74
75 void CoreConnection::setProgressValue(int value)
76 {
77     if (_progressValue != value) {
78         _progressValue = value;
79         emit progressValueChanged(value);
80     }
81 }
82
83 void CoreConnection::setProgressMinimum(int minimum)
84 {
85     if (_progressMinimum != minimum) {
86         _progressMinimum = minimum;
87         emit progressRangeChanged(minimum, _progressMaximum);
88     }
89 }
90
91 void CoreConnection::setProgressMaximum(int maximum)
92 {
93     if (_progressMaximum != maximum) {
94         _progressMaximum = maximum;
95         emit progressRangeChanged(_progressMinimum, maximum);
96     }
97 }
98
99 void CoreConnection::updateProgress(int value, int max)
100 {
101     if (max != _progressMaximum) {
102         _progressMaximum = max;
103         emit progressRangeChanged(_progressMinimum, _progressMaximum);
104     }
105     setProgressValue(value);
106 }
107
108 void CoreConnection::reconnectTimeout()
109 {
110     if (!_peer) {
111         CoreConnectionSettings s;
112         if (_wantReconnect && s.autoReconnect()) {
113             // If using QNetworkConfigurationManager, we don't want to reconnect if we're offline
114             if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) {
115                 if (!_qNetworkConfigurationManager->isOnline()) {
116                     return;
117                 }
118             }
119             reconnectToCore();
120         }
121     }
122 }
123
124 void CoreConnection::networkDetectionModeChanged(const QVariant& vmode)
125 {
126     CoreConnectionSettings s;
127     auto mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
128     if (mode == CoreConnectionSettings::UsePingTimeout)
129         Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
130     else {
131         Client::signalProxy()->setMaxHeartBeatCount(-1);
132     }
133 }
134
135 void CoreConnection::pingTimeoutIntervalChanged(const QVariant& interval)
136 {
137     CoreConnectionSettings s;
138     if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
139         Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30);  // interval is 30 seconds
140 }
141
142 void CoreConnection::reconnectIntervalChanged(const QVariant& interval)
143 {
144     _reconnectTimer.setInterval(interval.toInt() * 1000);
145 }
146
147 void CoreConnection::onlineStateChanged(bool isOnline)
148 {
149     CoreConnectionSettings s;
150     if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
151         return;
152
153     if (isOnline) {
154         // qDebug() << "QNetworkConfigurationManager reports Online";
155         if (state() == Disconnected) {
156             if (_wantReconnect && s.autoReconnect()) {
157                 reconnectToCore();
158             }
159         }
160     }
161     else {
162         // qDebug() << "QNetworkConfigurationManager reports Offline";
163         if (state() != Disconnected && !isLocalConnection())
164             disconnectFromCore(tr("Network is down"), true);
165     }
166 }
167
168 QPointer<Peer> CoreConnection::peer() const
169 {
170     if (_peer) {
171         return _peer;
172     }
173     return _authHandler ? _authHandler->peer() : nullptr;
174 }
175
176 bool CoreConnection::isEncrypted() const
177 {
178     return _peer && _peer->isSecure();
179 }
180
181 bool CoreConnection::isLocalConnection() const
182 {
183     if (!isConnected())
184         return false;
185     if (currentAccount().isInternal())
186         return true;
187     if (_authHandler)
188         return _authHandler->isLocal();
189     if (_peer)
190         return _peer->isLocal();
191
192     return false;
193 }
194
195 void CoreConnection::onConnectionReady()
196 {
197     setState(Connected);
198 }
199
200 void CoreConnection::setState(ConnectionState state)
201 {
202     if (state != _state) {
203         _state = state;
204         emit stateChanged(state);
205         if (state == Connected)
206             _wantReconnect = true;
207         if (state == Disconnected)
208             emit disconnected();
209     }
210 }
211
212 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString& errorString)
213 {
214     Q_UNUSED(error)
215
216     disconnectFromCore(errorString, true);
217 }
218
219 void CoreConnection::coreSocketDisconnected()
220 {
221     setState(Disconnected);
222     _wasReconnect = false;
223     resetConnection(_wantReconnect);
224 }
225
226 void CoreConnection::disconnectFromCore()
227 {
228     disconnectFromCore(QString(), false);  // requested disconnect, so don't try to reconnect
229 }
230
231 void CoreConnection::disconnectFromCore(const QString& errorString, bool wantReconnect)
232 {
233     if (wantReconnect)
234         _reconnectTimer.start();
235     else
236         _reconnectTimer.stop();
237
238     _wantReconnect = wantReconnect;  // store if disconnect was requested
239     _wasReconnect = false;
240
241     if (_authHandler)
242         _authHandler->close();
243     else if (_peer)
244         _peer->close();
245
246     if (errorString.isEmpty())
247         emit connectionError(tr("Disconnected"));
248     else
249         emit connectionError(errorString);
250 }
251
252 void CoreConnection::resetConnection(bool wantReconnect)
253 {
254     if (_resetting)
255         return;
256     _resetting = true;
257
258     _wantReconnect = wantReconnect;
259
260     if (_authHandler) {
261         disconnect(_authHandler, nullptr, this, nullptr);
262         _authHandler->close();
263         _authHandler->deleteLater();
264         _authHandler = nullptr;
265     }
266
267     if (_peer) {
268         disconnect(_peer, nullptr, this, nullptr);
269         // peer belongs to the sigproxy and thus gets deleted by it
270         _peer->close();
271         _peer = nullptr;
272     }
273
274     _netsToSync.clear();
275     _numNetsToSync = 0;
276
277     setProgressMaximum(-1);  // disable
278     setState(Disconnected);
279     emit lagUpdated(-1);
280
281     emit connectionMsg(tr("Disconnected from core."));
282     emit encrypted(false);
283     setState(Disconnected);
284
285     // initiate if a reconnect if appropriate
286     CoreConnectionSettings s;
287     if (wantReconnect && s.autoReconnect()) {
288         _reconnectTimer.start();
289     }
290
291     _resetting = false;
292 }
293
294 void CoreConnection::reconnectToCore()
295 {
296     if (currentAccount().isValid()) {
297         _wasReconnect = true;
298         connectToCore(currentAccount().accountId());
299     }
300 }
301
302 bool CoreConnection::connectToCore(AccountId accId)
303 {
304     if (isConnected())
305         return false;
306
307     CoreAccountSettings s;
308
309     // FIXME: Don't force connection to internal core in mono client
310     if (Quassel::runMode() == Quassel::Monolithic) {
311         _account = accountModel()->account(accountModel()->internalAccount());
312         Q_ASSERT(_account.isValid());
313     }
314     else {
315         if (!accId.isValid()) {
316             // check our settings and figure out what to do
317             if (!s.autoConnectOnStartup())
318                 return false;
319             if (s.autoConnectToFixedAccount())
320                 accId = s.autoConnectAccount();
321             else
322                 accId = s.lastAccount();
323             if (!accId.isValid())
324                 return false;
325         }
326         _account = accountModel()->account(accId);
327         if (!_account.accountId().isValid()) {
328             return false;
329         }
330         if (Quassel::runMode() != Quassel::Monolithic) {
331             if (_account.isInternal())
332                 return false;
333         }
334     }
335
336     s.setLastAccount(accId);
337     connectToCurrentAccount();
338     return true;
339 }
340
341 void CoreConnection::connectToCurrentAccount()
342 {
343     if (_authHandler) {
344         qWarning() << Q_FUNC_INFO << "Already connected!";
345         return;
346     }
347
348     if (currentAccount().isInternal()) {
349         if (Quassel::runMode() != Quassel::Monolithic) {
350             qWarning() << "Cannot connect to internal core in client-only mode!";
351             return;
352         }
353
354         auto* peer = new InternalPeer();
355         _peer = peer;
356         Client::instance()->signalProxy()->addPeer(peer);  // sigproxy will take ownership
357         emit connectionMsg(tr("Initializing..."));
358         emit connectToInternalCore(peer);
359         setState(Connected);
360         return;
361     }
362
363     _authHandler = new ClientAuthHandler(currentAccount(), this);
364
365     connect(_authHandler, &ClientAuthHandler::disconnected, this, &CoreConnection::coreSocketDisconnected);
366     connect(_authHandler, &ClientAuthHandler::connectionReady, this, &CoreConnection::onConnectionReady);
367     connect(_authHandler, &ClientAuthHandler::socketError, this, &CoreConnection::coreSocketError);
368     connect(_authHandler, &ClientAuthHandler::transferProgress, this, &CoreConnection::updateProgress);
369     connect(_authHandler,
370             &ClientAuthHandler::requestDisconnect,
371             this,
372             selectOverload<const QString&, bool>(&CoreConnection::disconnectFromCore));
373
374     connect(_authHandler, &ClientAuthHandler::errorMessage, this, &CoreConnection::connectionError);
375     connect(_authHandler, &ClientAuthHandler::errorPopup, this, &CoreConnection::connectionErrorPopup, Qt::QueuedConnection);
376     connect(_authHandler, &ClientAuthHandler::statusMessage, this, &CoreConnection::connectionMsg);
377     connect(_authHandler, &ClientAuthHandler::encrypted, this, &CoreConnection::encrypted);
378     connect(_authHandler, &ClientAuthHandler::startCoreSetup, this, &CoreConnection::startCoreSetup);
379     connect(_authHandler, &ClientAuthHandler::coreSetupFailed, this, &CoreConnection::coreSetupFailed);
380     connect(_authHandler, &ClientAuthHandler::coreSetupSuccessful, this, &CoreConnection::coreSetupSuccess);
381     connect(_authHandler, &ClientAuthHandler::userAuthenticationRequired, this, &CoreConnection::userAuthenticationRequired);
382     connect(_authHandler, &ClientAuthHandler::handleNoSslInClient, this, &CoreConnection::handleNoSslInClient);
383     connect(_authHandler, &ClientAuthHandler::handleNoSslInCore, this, &CoreConnection::handleNoSslInCore);
384 #ifdef HAVE_SSL
385     connect(_authHandler, &ClientAuthHandler::handleSslErrors, this, &CoreConnection::handleSslErrors);
386 #endif
387     connect(_authHandler, &ClientAuthHandler::loginSuccessful, this, &CoreConnection::onLoginSuccessful);
388     connect(_authHandler, &ClientAuthHandler::handshakeComplete, this, &CoreConnection::onHandshakeComplete);
389
390     setState(Connecting);
391     _authHandler->connectToCore();
392 }
393
394 void CoreConnection::setupCore(const Protocol::SetupData& setupData)
395 {
396     _authHandler->setupCore(setupData);
397 }
398
399 void CoreConnection::loginToCore(const QString& user, const QString& password, bool remember)
400 {
401     _authHandler->login(user, password, remember);
402 }
403
404 void CoreConnection::onLoginSuccessful(const CoreAccount& account)
405 {
406     updateProgress(0, 0);
407
408     // save current account data
409     accountModel()->createOrUpdateAccount(account);
410     accountModel()->save();
411
412     _reconnectTimer.stop();
413
414     setProgressText(tr("Receiving session state"));
415     setState(Synchronizing);
416     emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
417 }
418
419 void CoreConnection::onHandshakeComplete(RemotePeer* peer, const Protocol::SessionState& sessionState)
420 {
421     updateProgress(100, 100);
422
423     disconnect(_authHandler, nullptr, this, nullptr);
424     _authHandler->deleteLater();
425     _authHandler = nullptr;
426
427     _peer = peer;
428     connect(peer, &Peer::disconnected, this, &CoreConnection::coreSocketDisconnected);
429     connect(peer, &RemotePeer::statusMessage, this, &CoreConnection::connectionMsg);
430     connect(peer, &RemotePeer::socketError, this, &CoreConnection::coreSocketError);
431
432     Client::signalProxy()->addPeer(_peer);  // sigproxy takes ownership of the peer!
433
434     syncToCore(sessionState);
435 }
436
437 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState& sessionState)
438 {
439     updateProgress(100, 100);
440     setState(Synchronizing);
441     syncToCore(sessionState);
442 }
443
444 void CoreConnection::syncToCore(const Protocol::SessionState& sessionState)
445 {
446     setProgressText(tr("Receiving network states"));
447     updateProgress(0, 100);
448
449     // create identities
450     foreach (const QVariant& vid, sessionState.identities) {
451         Client::instance()->coreIdentityCreated(vid.value<Identity>());
452     }
453
454     // create buffers
455     // FIXME: get rid of this crap -- why?
456     NetworkModel* networkModel = Client::networkModel();
457     Q_ASSERT(networkModel);
458     foreach (const QVariant& vinfo, sessionState.bufferInfos)
459         networkModel->bufferUpdated(vinfo.value<BufferInfo>());  // create BufferItems
460
461     // prepare sync progress thingys...
462     // FIXME: Care about removal of networks
463     _numNetsToSync = sessionState.networkIds.count();
464     updateProgress(0, _numNetsToSync);
465
466     // create network objects
467     foreach (const QVariant& networkid, sessionState.networkIds) {
468         NetworkId netid = networkid.value<NetworkId>();
469         if (Client::network(netid))
470             continue;
471         auto* net = new Network(netid, Client::instance());
472         _netsToSync.insert(net);
473         connect(net, &SyncableObject::initDone, this, &CoreConnection::networkInitDone);
474         connect(net, &QObject::destroyed, this, &CoreConnection::networkInitDone);
475         Client::addNetwork(net);
476     }
477     checkSyncState();
478 }
479
480 // this is also called for destroyed networks!
481 void CoreConnection::networkInitDone()
482 {
483     QObject* net = sender();
484     Q_ASSERT(net);
485     disconnect(net, nullptr, this, nullptr);
486     _netsToSync.remove(net);
487     updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
488     checkSyncState();
489 }
490
491 void CoreConnection::checkSyncState()
492 {
493     if (_netsToSync.isEmpty() && state() >= Synchronizing) {
494         setState(Synchronized);
495         setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
496         setProgressMaximum(-1);
497         emit synchronized();
498     }
499 }