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 _wantReconnect(false),
48 qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
52 void CoreConnection::init()
54 Client::signalProxy()->setHeartBeatInterval(30);
55 connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), SIGNAL(lagUpdated(int)));
57 _reconnectTimer.setSingleShot(true);
58 connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout()));
60 _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
61 connect(_qNetworkConfigurationManager, SIGNAL(onlineStateChanged(bool)), SLOT(onlineStateChanged(bool)));
63 CoreConnectionSettings s;
64 s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60);
65 s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60);
66 s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant)));
67 networkDetectionModeChanged(s.networkDetectionMode());
71 CoreAccountModel *CoreConnection::accountModel() const
73 return Client::coreAccountModel();
77 void CoreConnection::setProgressText(const QString &text)
79 if (_progressText != text) {
81 emit progressTextChanged(text);
86 void CoreConnection::setProgressValue(int value)
88 if (_progressValue != value) {
89 _progressValue = value;
90 emit progressValueChanged(value);
95 void CoreConnection::setProgressMinimum(int minimum)
97 if (_progressMinimum != minimum) {
98 _progressMinimum = minimum;
99 emit progressRangeChanged(minimum, _progressMaximum);
104 void CoreConnection::setProgressMaximum(int maximum)
106 if (_progressMaximum != maximum) {
107 _progressMaximum = maximum;
108 emit progressRangeChanged(_progressMinimum, maximum);
113 void CoreConnection::updateProgress(int value, int max)
115 if (max != _progressMaximum) {
116 _progressMaximum = max;
117 emit progressRangeChanged(_progressMinimum, _progressMaximum);
119 setProgressValue(value);
123 void CoreConnection::reconnectTimeout()
126 CoreConnectionSettings s;
127 if (_wantReconnect && s.autoReconnect()) {
128 // If using QNetworkConfigurationManager, we don't want to reconnect if we're offline
129 if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) {
130 if (!_qNetworkConfigurationManager->isOnline()) {
140 void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
142 CoreConnectionSettings s;
143 CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
144 if (mode == CoreConnectionSettings::UsePingTimeout)
145 Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
147 Client::signalProxy()->setMaxHeartBeatCount(-1);
152 void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
154 CoreConnectionSettings s;
155 if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
156 Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds
160 void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
162 _reconnectTimer.setInterval(interval.toInt() * 1000);
166 void CoreConnection::onlineStateChanged(bool isOnline)
168 CoreConnectionSettings s;
169 if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
173 // qDebug() << "QNetworkConfigurationManager reports Online";
174 if (state() == Disconnected) {
175 if (_wantReconnect && s.autoReconnect()) {
180 // qDebug() << "QNetworkConfigurationManager reports Offline";
181 if (state() != Disconnected && !isLocalConnection())
182 disconnectFromCore(tr("Network is down"), true);
187 QPointer<Peer> CoreConnection::peer() const
192 return _authHandler ? _authHandler->peer() : nullptr;
196 bool CoreConnection::isEncrypted() const
198 return _peer && _peer->isSecure();
202 bool CoreConnection::isLocalConnection() const
206 if (currentAccount().isInternal())
209 return _authHandler->isLocal();
211 return _peer->isLocal();
217 void CoreConnection::onConnectionReady()
223 void CoreConnection::setState(ConnectionState state)
225 if (state != _state) {
227 emit stateChanged(state);
228 if (state == Connected)
229 _wantReconnect = true;
230 if (state == Disconnected)
236 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
240 disconnectFromCore(errorString, true);
244 void CoreConnection::coreSocketDisconnected()
246 setState(Disconnected);
247 _wasReconnect = false;
248 resetConnection(_wantReconnect);
252 void CoreConnection::disconnectFromCore()
254 disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
258 void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
261 _reconnectTimer.start();
263 _reconnectTimer.stop();
265 _wantReconnect = wantReconnect; // store if disconnect was requested
266 _wasReconnect = false;
269 _authHandler->close();
273 if (errorString.isEmpty())
274 emit connectionError(tr("Disconnected"));
276 emit connectionError(errorString);
280 void CoreConnection::resetConnection(bool wantReconnect)
286 _wantReconnect = wantReconnect;
289 disconnect(_authHandler, 0, this, 0);
290 _authHandler->close();
291 _authHandler->deleteLater();
296 disconnect(_peer, 0, this, 0);
297 // peer belongs to the sigproxy and thus gets deleted by it
305 setProgressMaximum(-1); // disable
306 setState(Disconnected);
309 emit connectionMsg(tr("Disconnected from core."));
310 emit encrypted(false);
311 setState(Disconnected);
313 // initiate if a reconnect if appropriate
314 CoreConnectionSettings s;
315 if (wantReconnect && s.autoReconnect()) {
316 _reconnectTimer.start();
323 void CoreConnection::reconnectToCore()
325 if (currentAccount().isValid()) {
326 _wasReconnect = true;
327 connectToCore(currentAccount().accountId());
332 bool CoreConnection::connectToCore(AccountId accId)
337 CoreAccountSettings s;
339 // FIXME: Don't force connection to internal core in mono client
340 if (Quassel::runMode() == Quassel::Monolithic) {
341 _account = accountModel()->account(accountModel()->internalAccount());
342 Q_ASSERT(_account.isValid());
345 if (!accId.isValid()) {
346 // check our settings and figure out what to do
347 if (!s.autoConnectOnStartup())
349 if (s.autoConnectToFixedAccount())
350 accId = s.autoConnectAccount();
352 accId = s.lastAccount();
353 if (!accId.isValid())
356 _account = accountModel()->account(accId);
357 if (!_account.accountId().isValid()) {
360 if (Quassel::runMode() != Quassel::Monolithic) {
361 if (_account.isInternal())
366 s.setLastAccount(accId);
367 connectToCurrentAccount();
372 void CoreConnection::connectToCurrentAccount()
375 qWarning() << Q_FUNC_INFO << "Already connected!";
379 if (currentAccount().isInternal()) {
380 if (Quassel::runMode() != Quassel::Monolithic) {
381 qWarning() << "Cannot connect to internal core in client-only mode!";
385 InternalPeer *peer = new InternalPeer();
387 Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
388 emit connectionMsg(tr("Initializing..."));
389 emit connectToInternalCore(peer);
394 _authHandler = new ClientAuthHandler(currentAccount(), this);
396 connect(_authHandler, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
397 connect(_authHandler, SIGNAL(connectionReady()), SLOT(onConnectionReady()));
398 connect(_authHandler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
399 connect(_authHandler, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
400 connect(_authHandler, SIGNAL(requestDisconnect(QString,bool)), SLOT(disconnectFromCore(QString,bool)));
402 connect(_authHandler, SIGNAL(errorMessage(QString)), SIGNAL(connectionError(QString)));
403 connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)), Qt::QueuedConnection);
404 connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
405 connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool)));
406 connect(_authHandler, SIGNAL(startCoreSetup(QVariantList, QVariantList)), SIGNAL(startCoreSetup(QVariantList, QVariantList)));
407 connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString)));
408 connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess()));
409 connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)));
410 connect(_authHandler, SIGNAL(handleNoSslInClient(bool*)), SIGNAL(handleNoSslInClient(bool*)));
411 connect(_authHandler, SIGNAL(handleNoSslInCore(bool*)), SIGNAL(handleNoSslInCore(bool*)));
413 connect(_authHandler, SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)), SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)));
416 connect(_authHandler, SIGNAL(loginSuccessful(CoreAccount)), SLOT(onLoginSuccessful(CoreAccount)));
417 connect(_authHandler, SIGNAL(handshakeComplete(RemotePeer*,Protocol::SessionState)), SLOT(onHandshakeComplete(RemotePeer*,Protocol::SessionState)));
419 setState(Connecting);
420 _authHandler->connectToCore();
424 void CoreConnection::setupCore(const Protocol::SetupData &setupData)
426 _authHandler->setupCore(setupData);
430 void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
432 _authHandler->login(user, password, remember);
436 void CoreConnection::onLoginSuccessful(const CoreAccount &account)
438 updateProgress(0, 0);
440 // save current account data
441 accountModel()->createOrUpdateAccount(account);
442 accountModel()->save();
444 _reconnectTimer.stop();
446 setProgressText(tr("Receiving session state"));
447 setState(Synchronizing);
448 emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
452 void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
454 updateProgress(100, 100);
456 disconnect(_authHandler, 0, this, 0);
457 _authHandler->deleteLater();
461 connect(peer, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
462 connect(peer, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
463 connect(peer, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
465 Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
467 syncToCore(sessionState);
471 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
473 updateProgress(100, 100);
474 setState(Synchronizing);
475 syncToCore(sessionState);
479 void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
481 setProgressText(tr("Receiving network states"));
482 updateProgress(0, 100);
485 foreach(const QVariant &vid, sessionState.identities) {
486 Client::instance()->coreIdentityCreated(vid.value<Identity>());
490 // FIXME: get rid of this crap -- why?
491 NetworkModel *networkModel = Client::networkModel();
492 Q_ASSERT(networkModel);
493 foreach(const QVariant &vinfo, sessionState.bufferInfos)
494 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
496 // prepare sync progress thingys...
497 // FIXME: Care about removal of networks
498 _numNetsToSync = sessionState.networkIds.count();
499 updateProgress(0, _numNetsToSync);
501 // create network objects
502 foreach(const QVariant &networkid, sessionState.networkIds) {
503 NetworkId netid = networkid.value<NetworkId>();
504 if (Client::network(netid))
506 Network *net = new Network(netid, Client::instance());
507 _netsToSync.insert(net);
508 connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
509 connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
510 Client::addNetwork(net);
516 // this is also called for destroyed networks!
517 void CoreConnection::networkInitDone()
519 QObject *net = sender();
521 disconnect(net, 0, this, 0);
522 _netsToSync.remove(net);
523 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
528 void CoreConnection::checkSyncState()
530 if (_netsToSync.isEmpty() && state() >= Synchronizing) {
531 setState(Synchronized);
532 setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
533 setProgressMaximum(-1);