1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "coreconnection.h"
24 #include "clientauthhandler.h"
25 #include "clientsettings.h"
26 #include "coreaccountmodel.h"
28 #include "internalpeer.h"
30 #include "networkmodel.h"
32 #include "signalproxy.h"
35 #include "protocols/legacy/legacypeer.h"
37 CoreConnection::CoreConnection(QObject* parent)
39 , _authHandler(nullptr)
41 qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
44 void CoreConnection::init()
46 Client::signalProxy()->setHeartBeatInterval(30);
47 connect(Client::signalProxy(), &SignalProxy::lagUpdated, this, &CoreConnection::lagUpdated);
49 _reconnectTimer.setSingleShot(true);
50 connect(&_reconnectTimer, &QTimer::timeout, this, &CoreConnection::reconnectTimeout);
52 _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
53 connect(_qNetworkConfigurationManager.data(), &QNetworkConfigurationManager::onlineStateChanged, this, &CoreConnection::onlineStateChanged);
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());
62 CoreAccountModel* CoreConnection::accountModel() const
64 return Client::coreAccountModel();
67 void CoreConnection::setProgressText(const QString& text)
69 if (_progressText != text) {
71 emit progressTextChanged(text);
75 void CoreConnection::setProgressValue(int value)
77 if (_progressValue != value) {
78 _progressValue = value;
79 emit progressValueChanged(value);
83 void CoreConnection::setProgressMinimum(int minimum)
85 if (_progressMinimum != minimum) {
86 _progressMinimum = minimum;
87 emit progressRangeChanged(minimum, _progressMaximum);
91 void CoreConnection::setProgressMaximum(int maximum)
93 if (_progressMaximum != maximum) {
94 _progressMaximum = maximum;
95 emit progressRangeChanged(_progressMinimum, maximum);
99 void CoreConnection::updateProgress(int value, int max)
101 if (max != _progressMaximum) {
102 _progressMaximum = max;
103 emit progressRangeChanged(_progressMinimum, _progressMaximum);
105 setProgressValue(value);
108 void CoreConnection::reconnectTimeout()
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()) {
124 void CoreConnection::networkDetectionModeChanged(const QVariant& vmode)
126 CoreConnectionSettings s;
127 auto mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
128 if (mode == CoreConnectionSettings::UsePingTimeout)
129 Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
131 Client::signalProxy()->setMaxHeartBeatCount(-1);
135 void CoreConnection::pingTimeoutIntervalChanged(const QVariant& interval)
137 CoreConnectionSettings s;
138 if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
139 Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds
142 void CoreConnection::reconnectIntervalChanged(const QVariant& interval)
144 _reconnectTimer.setInterval(interval.toInt() * 1000);
147 void CoreConnection::onlineStateChanged(bool isOnline)
149 CoreConnectionSettings s;
150 if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
154 // qDebug() << "QNetworkConfigurationManager reports Online";
155 if (state() == Disconnected) {
156 if (_wantReconnect && s.autoReconnect()) {
162 // qDebug() << "QNetworkConfigurationManager reports Offline";
163 if (state() != Disconnected && !isLocalConnection())
164 disconnectFromCore(tr("Network is down"), true);
168 QPointer<Peer> CoreConnection::peer() const
173 return _authHandler ? _authHandler->peer() : nullptr;
176 bool CoreConnection::isEncrypted() const
178 return _peer && _peer->isSecure();
181 bool CoreConnection::isLocalConnection() const
185 if (currentAccount().isInternal())
188 return _authHandler->isLocal();
190 return _peer->isLocal();
195 void CoreConnection::onConnectionReady()
200 void CoreConnection::setState(ConnectionState state)
202 if (state != _state) {
204 emit stateChanged(state);
205 if (state == Connected)
206 _wantReconnect = true;
207 if (state == Disconnected)
212 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString& errorString)
216 disconnectFromCore(errorString, true);
219 void CoreConnection::coreSocketDisconnected()
221 setState(Disconnected);
222 _wasReconnect = false;
223 resetConnection(_wantReconnect);
226 void CoreConnection::disconnectFromCore()
228 disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
231 void CoreConnection::disconnectFromCore(const QString& errorString, bool wantReconnect)
234 _reconnectTimer.start();
236 _reconnectTimer.stop();
238 _wantReconnect = wantReconnect; // store if disconnect was requested
239 _wasReconnect = false;
242 _authHandler->close();
246 if (errorString.isEmpty())
247 emit connectionError(tr("Disconnected"));
249 emit connectionError(errorString);
252 void CoreConnection::resetConnection(bool wantReconnect)
258 _wantReconnect = wantReconnect;
261 disconnect(_authHandler, nullptr, this, nullptr);
262 _authHandler->close();
263 _authHandler->deleteLater();
264 _authHandler = nullptr;
268 disconnect(_peer, nullptr, this, nullptr);
269 // peer belongs to the sigproxy and thus gets deleted by it
277 setProgressMaximum(-1); // disable
278 setState(Disconnected);
281 emit connectionMsg(tr("Disconnected from core."));
282 emit encrypted(false);
283 setState(Disconnected);
285 // initiate if a reconnect if appropriate
286 CoreConnectionSettings s;
287 if (wantReconnect && s.autoReconnect()) {
288 _reconnectTimer.start();
294 void CoreConnection::reconnectToCore()
296 if (currentAccount().isValid()) {
297 _wasReconnect = true;
298 connectToCore(currentAccount().accountId());
302 bool CoreConnection::connectToCore(AccountId accId)
307 CoreAccountSettings s;
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());
315 if (!accId.isValid()) {
316 // check our settings and figure out what to do
317 if (!s.autoConnectOnStartup())
319 if (s.autoConnectToFixedAccount())
320 accId = s.autoConnectAccount();
322 accId = s.lastAccount();
323 if (!accId.isValid())
326 _account = accountModel()->account(accId);
327 if (!_account.accountId().isValid()) {
330 if (Quassel::runMode() != Quassel::Monolithic) {
331 if (_account.isInternal())
336 s.setLastAccount(accId);
337 connectToCurrentAccount();
341 void CoreConnection::connectToCurrentAccount()
344 qWarning() << Q_FUNC_INFO << "Already connected!";
348 if (currentAccount().isInternal()) {
349 if (Quassel::runMode() != Quassel::Monolithic) {
350 qWarning() << "Cannot connect to internal core in client-only mode!";
354 auto* peer = new InternalPeer();
356 Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
357 emit connectionMsg(tr("Initializing..."));
358 emit connectToInternalCore(peer);
363 _authHandler = new ClientAuthHandler(currentAccount(), this);
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,
372 selectOverload<const QString&, bool>(&CoreConnection::disconnectFromCore));
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 connect(_authHandler, &ClientAuthHandler::handleSslErrors, this, &CoreConnection::handleSslErrors);
385 connect(_authHandler, &ClientAuthHandler::loginSuccessful, this, &CoreConnection::onLoginSuccessful);
386 connect(_authHandler, &ClientAuthHandler::handshakeComplete, this, &CoreConnection::onHandshakeComplete);
388 setState(Connecting);
389 _authHandler->connectToCore();
392 void CoreConnection::setupCore(const Protocol::SetupData& setupData)
394 _authHandler->setupCore(setupData);
397 void CoreConnection::loginToCore(const QString& user, const QString& password, bool remember)
399 _authHandler->login(user, password, remember);
402 void CoreConnection::onLoginSuccessful(const CoreAccount& account)
404 updateProgress(0, 0);
406 // save current account data
407 accountModel()->createOrUpdateAccount(account);
408 accountModel()->save();
410 _reconnectTimer.stop();
412 setProgressText(tr("Receiving session state"));
413 setState(Synchronizing);
414 emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
417 void CoreConnection::onHandshakeComplete(RemotePeer* peer, const Protocol::SessionState& sessionState)
419 updateProgress(100, 100);
421 disconnect(_authHandler, nullptr, this, nullptr);
422 _authHandler->deleteLater();
423 _authHandler = nullptr;
426 connect(peer, &Peer::disconnected, this, &CoreConnection::coreSocketDisconnected);
427 connect(peer, &RemotePeer::statusMessage, this, &CoreConnection::connectionMsg);
428 connect(peer, &RemotePeer::socketError, this, &CoreConnection::coreSocketError);
430 Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
432 syncToCore(sessionState);
435 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState& sessionState)
437 updateProgress(100, 100);
438 setState(Synchronizing);
439 syncToCore(sessionState);
442 void CoreConnection::syncToCore(const Protocol::SessionState& sessionState)
444 setProgressText(tr("Receiving network states"));
445 updateProgress(0, 100);
448 foreach (const QVariant& vid, sessionState.identities) {
449 Client::instance()->coreIdentityCreated(vid.value<Identity>());
453 // FIXME: get rid of this crap -- why?
454 NetworkModel* networkModel = Client::networkModel();
455 Q_ASSERT(networkModel);
456 foreach (const QVariant& vinfo, sessionState.bufferInfos)
457 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
459 // prepare sync progress thingys...
460 // FIXME: Care about removal of networks
461 _numNetsToSync = sessionState.networkIds.count();
462 updateProgress(0, _numNetsToSync);
464 // create network objects
465 foreach (const QVariant& networkid, sessionState.networkIds) {
466 NetworkId netid = networkid.value<NetworkId>();
467 if (Client::network(netid))
469 auto* net = new Network(netid, Client::instance());
470 _netsToSync.insert(net);
471 connect(net, &SyncableObject::initDone, this, &CoreConnection::networkInitDone);
472 connect(net, &QObject::destroyed, this, &CoreConnection::networkInitDone);
473 Client::addNetwork(net);
478 // this is also called for destroyed networks!
479 void CoreConnection::networkInitDone()
481 QObject* net = sender();
483 disconnect(net, nullptr, this, nullptr);
484 _netsToSync.remove(net);
485 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
489 void CoreConnection::checkSyncState()
491 if (_netsToSync.isEmpty() && state() >= Synchronizing) {
492 setState(Synchronized);
493 setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
494 setProgressMaximum(-1);