1 /***************************************************************************
2 * Copyright (C) 2005-2018 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)
41 qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
45 void CoreConnection::init()
47 Client::signalProxy()->setHeartBeatInterval(30);
48 connect(Client::signalProxy(), &SignalProxy::lagUpdated, this, &CoreConnection::lagUpdated);
50 _reconnectTimer.setSingleShot(true);
51 connect(&_reconnectTimer, &QTimer::timeout, this, &CoreConnection::reconnectTimeout);
53 _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
54 connect(_qNetworkConfigurationManager.data(), &QNetworkConfigurationManager::onlineStateChanged, this, &CoreConnection::onlineStateChanged);
56 CoreConnectionSettings s;
57 s.initAndNotify("PingTimeoutInterval", this, &CoreConnection::pingTimeoutIntervalChanged, 60);
58 s.initAndNotify("ReconnectInterval", this, &CoreConnection::reconnectIntervalChanged, 60);
59 s.notify("NetworkDetectionMode", this, &CoreConnection::networkDetectionModeChanged);
60 networkDetectionModeChanged(s.networkDetectionMode());
64 CoreAccountModel *CoreConnection::accountModel() const
66 return Client::coreAccountModel();
70 void CoreConnection::setProgressText(const QString &text)
72 if (_progressText != text) {
74 emit progressTextChanged(text);
79 void CoreConnection::setProgressValue(int value)
81 if (_progressValue != value) {
82 _progressValue = value;
83 emit progressValueChanged(value);
88 void CoreConnection::setProgressMinimum(int minimum)
90 if (_progressMinimum != minimum) {
91 _progressMinimum = minimum;
92 emit progressRangeChanged(minimum, _progressMaximum);
97 void CoreConnection::setProgressMaximum(int maximum)
99 if (_progressMaximum != maximum) {
100 _progressMaximum = maximum;
101 emit progressRangeChanged(_progressMinimum, maximum);
106 void CoreConnection::updateProgress(int value, int max)
108 if (max != _progressMaximum) {
109 _progressMaximum = max;
110 emit progressRangeChanged(_progressMinimum, _progressMaximum);
112 setProgressValue(value);
116 void CoreConnection::reconnectTimeout()
119 CoreConnectionSettings s;
120 if (_wantReconnect && s.autoReconnect()) {
121 // If using QNetworkConfigurationManager, we don't want to reconnect if we're offline
122 if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) {
123 if (!_qNetworkConfigurationManager->isOnline()) {
133 void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
135 CoreConnectionSettings s;
136 auto mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
137 if (mode == CoreConnectionSettings::UsePingTimeout)
138 Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
140 Client::signalProxy()->setMaxHeartBeatCount(-1);
145 void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
147 CoreConnectionSettings s;
148 if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
149 Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds
153 void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
155 _reconnectTimer.setInterval(interval.toInt() * 1000);
159 void CoreConnection::onlineStateChanged(bool isOnline)
161 CoreConnectionSettings s;
162 if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
166 // qDebug() << "QNetworkConfigurationManager reports Online";
167 if (state() == Disconnected) {
168 if (_wantReconnect && s.autoReconnect()) {
173 // qDebug() << "QNetworkConfigurationManager reports Offline";
174 if (state() != Disconnected && !isLocalConnection())
175 disconnectFromCore(tr("Network is down"), true);
180 QPointer<Peer> CoreConnection::peer() const
185 return _authHandler ? _authHandler->peer() : nullptr;
189 bool CoreConnection::isEncrypted() const
191 return _peer && _peer->isSecure();
195 bool CoreConnection::isLocalConnection() const
199 if (currentAccount().isInternal())
202 return _authHandler->isLocal();
204 return _peer->isLocal();
210 void CoreConnection::onConnectionReady()
216 void CoreConnection::setState(ConnectionState state)
218 if (state != _state) {
220 emit stateChanged(state);
221 if (state == Connected)
222 _wantReconnect = true;
223 if (state == Disconnected)
229 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
233 disconnectFromCore(errorString, true);
237 void CoreConnection::coreSocketDisconnected()
239 setState(Disconnected);
240 _wasReconnect = false;
241 resetConnection(_wantReconnect);
245 void CoreConnection::disconnectFromCore()
247 disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
251 void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
254 _reconnectTimer.start();
256 _reconnectTimer.stop();
258 _wantReconnect = wantReconnect; // store if disconnect was requested
259 _wasReconnect = false;
262 _authHandler->close();
266 if (errorString.isEmpty())
267 emit connectionError(tr("Disconnected"));
269 emit connectionError(errorString);
273 void CoreConnection::resetConnection(bool wantReconnect)
279 _wantReconnect = wantReconnect;
282 disconnect(_authHandler, nullptr, this, nullptr);
283 _authHandler->close();
284 _authHandler->deleteLater();
285 _authHandler = nullptr;
289 disconnect(_peer, nullptr, this, nullptr);
290 // peer belongs to the sigproxy and thus gets deleted by it
298 setProgressMaximum(-1); // disable
299 setState(Disconnected);
302 emit connectionMsg(tr("Disconnected from core."));
303 emit encrypted(false);
304 setState(Disconnected);
306 // initiate if a reconnect if appropriate
307 CoreConnectionSettings s;
308 if (wantReconnect && s.autoReconnect()) {
309 _reconnectTimer.start();
316 void CoreConnection::reconnectToCore()
318 if (currentAccount().isValid()) {
319 _wasReconnect = true;
320 connectToCore(currentAccount().accountId());
325 bool CoreConnection::connectToCore(AccountId accId)
330 CoreAccountSettings s;
332 // FIXME: Don't force connection to internal core in mono client
333 if (Quassel::runMode() == Quassel::Monolithic) {
334 _account = accountModel()->account(accountModel()->internalAccount());
335 Q_ASSERT(_account.isValid());
338 if (!accId.isValid()) {
339 // check our settings and figure out what to do
340 if (!s.autoConnectOnStartup())
342 if (s.autoConnectToFixedAccount())
343 accId = s.autoConnectAccount();
345 accId = s.lastAccount();
346 if (!accId.isValid())
349 _account = accountModel()->account(accId);
350 if (!_account.accountId().isValid()) {
353 if (Quassel::runMode() != Quassel::Monolithic) {
354 if (_account.isInternal())
359 s.setLastAccount(accId);
360 connectToCurrentAccount();
365 void CoreConnection::connectToCurrentAccount()
368 qWarning() << Q_FUNC_INFO << "Already connected!";
372 if (currentAccount().isInternal()) {
373 if (Quassel::runMode() != Quassel::Monolithic) {
374 qWarning() << "Cannot connect to internal core in client-only mode!";
378 auto *peer = new InternalPeer();
380 Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
381 emit connectionMsg(tr("Initializing..."));
382 emit connectToInternalCore(peer);
387 _authHandler = new ClientAuthHandler(currentAccount(), this);
389 connect(_authHandler, &ClientAuthHandler::disconnected, this, &CoreConnection::coreSocketDisconnected);
390 connect(_authHandler, &ClientAuthHandler::connectionReady, this, &CoreConnection::onConnectionReady);
391 connect(_authHandler, &ClientAuthHandler::socketError, this, &CoreConnection::coreSocketError);
392 connect(_authHandler, &ClientAuthHandler::transferProgress, this, &CoreConnection::updateProgress);
393 connect(_authHandler, &ClientAuthHandler::requestDisconnect, this, selectOverload<const QString&, bool>(&CoreConnection::disconnectFromCore));
395 connect(_authHandler, &ClientAuthHandler::errorMessage, this, &CoreConnection::connectionError);
396 connect(_authHandler, &ClientAuthHandler::errorPopup, this, &CoreConnection::connectionErrorPopup, Qt::QueuedConnection);
397 connect(_authHandler, &ClientAuthHandler::statusMessage, this, &CoreConnection::connectionMsg);
398 connect(_authHandler, &ClientAuthHandler::encrypted, this, &CoreConnection::encrypted);
399 connect(_authHandler, &ClientAuthHandler::startCoreSetup, this, &CoreConnection::startCoreSetup);
400 connect(_authHandler, &ClientAuthHandler::coreSetupFailed, this, &CoreConnection::coreSetupFailed);
401 connect(_authHandler, &ClientAuthHandler::coreSetupSuccessful, this, &CoreConnection::coreSetupSuccess);
402 connect(_authHandler, &ClientAuthHandler::userAuthenticationRequired, this, &CoreConnection::userAuthenticationRequired);
403 connect(_authHandler, &ClientAuthHandler::handleNoSslInClient, this, &CoreConnection::handleNoSslInClient);
404 connect(_authHandler, &ClientAuthHandler::handleNoSslInCore, this, &CoreConnection::handleNoSslInCore);
406 connect(_authHandler, &ClientAuthHandler::handleSslErrors, this, &CoreConnection::handleSslErrors);
408 connect(_authHandler, &ClientAuthHandler::loginSuccessful, this, &CoreConnection::onLoginSuccessful);
409 connect(_authHandler, &ClientAuthHandler::handshakeComplete, this, &CoreConnection::onHandshakeComplete);
411 setState(Connecting);
412 _authHandler->connectToCore();
416 void CoreConnection::setupCore(const Protocol::SetupData &setupData)
418 _authHandler->setupCore(setupData);
422 void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
424 _authHandler->login(user, password, remember);
428 void CoreConnection::onLoginSuccessful(const CoreAccount &account)
430 updateProgress(0, 0);
432 // save current account data
433 accountModel()->createOrUpdateAccount(account);
434 accountModel()->save();
436 _reconnectTimer.stop();
438 setProgressText(tr("Receiving session state"));
439 setState(Synchronizing);
440 emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
444 void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
446 updateProgress(100, 100);
448 disconnect(_authHandler, nullptr, this, nullptr);
449 _authHandler->deleteLater();
450 _authHandler = nullptr;
453 connect(peer, &Peer::disconnected, this, &CoreConnection::coreSocketDisconnected);
454 connect(peer, &RemotePeer::statusMessage, this, &CoreConnection::connectionMsg);
455 connect(peer, &RemotePeer::socketError, this, &CoreConnection::coreSocketError);
457 Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
459 syncToCore(sessionState);
463 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
465 updateProgress(100, 100);
466 setState(Synchronizing);
467 syncToCore(sessionState);
471 void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
473 setProgressText(tr("Receiving network states"));
474 updateProgress(0, 100);
477 foreach(const QVariant &vid, sessionState.identities) {
478 Client::instance()->coreIdentityCreated(vid.value<Identity>());
482 // FIXME: get rid of this crap -- why?
483 NetworkModel *networkModel = Client::networkModel();
484 Q_ASSERT(networkModel);
485 foreach(const QVariant &vinfo, sessionState.bufferInfos)
486 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
488 // prepare sync progress thingys...
489 // FIXME: Care about removal of networks
490 _numNetsToSync = sessionState.networkIds.count();
491 updateProgress(0, _numNetsToSync);
493 // create network objects
494 foreach(const QVariant &networkid, sessionState.networkIds) {
495 NetworkId netid = networkid.value<NetworkId>();
496 if (Client::network(netid))
498 auto *net = new Network(netid, Client::instance());
499 _netsToSync.insert(net);
500 connect(net, &SyncableObject::initDone, this, &CoreConnection::networkInitDone);
501 connect(net, &QObject::destroyed, this, &CoreConnection::networkInitDone);
502 Client::addNetwork(net);
508 // this is also called for destroyed networks!
509 void CoreConnection::networkInitDone()
511 QObject *net = sender();
513 disconnect(net, nullptr, this, nullptr);
514 _netsToSync.remove(net);
515 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
520 void CoreConnection::checkSyncState()
522 if (_netsToSync.isEmpty() && state() >= Synchronizing) {
523 setState(Synchronized);
524 setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
525 setProgressMaximum(-1);