1 /***************************************************************************
2 * Copyright (C) 2005-2015 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),
47 _qNetworkConfigurationManager(0)
49 qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
53 void CoreConnection::init()
55 Client::signalProxy()->setHeartBeatInterval(30);
56 connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), SIGNAL(lagUpdated(int)));
58 _reconnectTimer.setSingleShot(true);
59 connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout()));
62 connect(Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)),
63 SLOT(solidNetworkStatusChanged(Solid::Networking::Status)));
65 _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
66 connect(_qNetworkConfigurationManager, SIGNAL(onlineStateChanged(bool)), SLOT(onlineStateChanged(bool)));
68 CoreConnectionSettings s;
69 s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60);
70 s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60);
71 s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant)));
72 networkDetectionModeChanged(s.networkDetectionMode());
76 CoreAccountModel *CoreConnection::accountModel() const
78 return Client::coreAccountModel();
82 void CoreConnection::setProgressText(const QString &text)
84 if (_progressText != text) {
86 emit progressTextChanged(text);
91 void CoreConnection::setProgressValue(int value)
93 if (_progressValue != value) {
94 _progressValue = value;
95 emit progressValueChanged(value);
100 void CoreConnection::setProgressMinimum(int minimum)
102 if (_progressMinimum != minimum) {
103 _progressMinimum = minimum;
104 emit progressRangeChanged(minimum, _progressMaximum);
109 void CoreConnection::setProgressMaximum(int maximum)
111 if (_progressMaximum != maximum) {
112 _progressMaximum = maximum;
113 emit progressRangeChanged(_progressMinimum, maximum);
118 void CoreConnection::updateProgress(int value, int max)
120 if (max != _progressMaximum) {
121 _progressMaximum = max;
122 emit progressRangeChanged(_progressMinimum, _progressMaximum);
124 setProgressValue(value);
128 void CoreConnection::reconnectTimeout()
131 CoreConnectionSettings s;
132 if (_wantReconnect && s.autoReconnect()) {
134 // If using Solid, we don't want to reconnect if we're offline
135 if (s.networkDetectionMode() == CoreConnectionSettings::UseSolid) {
136 if (Solid::Networking::status() != Solid::Networking::Connected
137 && Solid::Networking::status() != Solid::Networking::Unknown) {
141 #endif /* HAVE_KDE4 */
142 // If using QNetworkConfigurationManager, ditto
143 if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) {
144 if (!_qNetworkConfigurationManager->isOnline()) {
155 void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
157 CoreConnectionSettings s;
158 CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
159 if (mode == CoreConnectionSettings::UsePingTimeout)
160 Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
162 Client::signalProxy()->setMaxHeartBeatCount(-1);
167 void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
169 CoreConnectionSettings s;
170 if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
171 Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds
175 void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
177 _reconnectTimer.setInterval(interval.toInt() * 1000);
183 void CoreConnection::solidNetworkStatusChanged(Solid::Networking::Status status)
185 CoreConnectionSettings s;
186 if (s.networkDetectionMode() != CoreConnectionSettings::UseSolid)
190 case Solid::Networking::Unknown:
191 case Solid::Networking::Connected:
192 //qDebug() << "Solid: Network status changed to connected or unknown";
193 if (state() == Disconnected) {
194 if (_wantReconnect && s.autoReconnect()) {
199 case Solid::Networking::Disconnecting:
200 case Solid::Networking::Unconnected:
201 if (state() != Disconnected && !isLocalConnection())
202 disconnectFromCore(tr("Network is down"), true);
211 void CoreConnection::onlineStateChanged(bool isOnline)
213 CoreConnectionSettings s;
214 if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
218 // qDebug() << "QNetworkConfigurationManager reports Online";
219 if (state() == Disconnected) {
220 if (_wantReconnect && s.autoReconnect()) {
225 // qDebug() << "QNetworkConfigurationManager reports Offline";
226 if (state() != Disconnected && !isLocalConnection())
227 disconnectFromCore(tr("Network is down"), true);
231 bool CoreConnection::isEncrypted() const
233 return _peer && _peer->isSecure();
237 bool CoreConnection::isLocalConnection() const
241 if (currentAccount().isInternal())
244 return _authHandler->isLocal();
246 return _peer->isLocal();
252 void CoreConnection::onConnectionReady()
258 void CoreConnection::setState(ConnectionState state)
260 if (state != _state) {
262 emit stateChanged(state);
263 if (state == Disconnected)
269 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
273 disconnectFromCore(errorString, true);
277 void CoreConnection::coreSocketDisconnected()
279 setState(Disconnected);
280 _wasReconnect = false;
281 resetConnection(_wantReconnect);
285 void CoreConnection::disconnectFromCore()
287 disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
291 void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
294 _reconnectTimer.start();
296 _reconnectTimer.stop();
298 _wantReconnect = wantReconnect; // store if disconnect was requested
299 _wasReconnect = false;
302 _authHandler->close();
306 if (errorString.isEmpty())
307 emit connectionError(tr("Disconnected"));
309 emit connectionError(errorString);
313 void CoreConnection::resetConnection(bool wantReconnect)
319 _wantReconnect = wantReconnect;
322 disconnect(_authHandler, 0, this, 0);
323 _authHandler->close();
324 _authHandler->deleteLater();
329 disconnect(_peer, 0, this, 0);
330 // peer belongs to the sigproxy and thus gets deleted by it
338 setProgressMaximum(-1); // disable
339 setState(Disconnected);
342 emit connectionMsg(tr("Disconnected from core."));
343 emit encrypted(false);
344 setState(Disconnected);
346 // initiate if a reconnect if appropriate
347 CoreConnectionSettings s;
348 if (wantReconnect && s.autoReconnect()) {
349 _reconnectTimer.start();
356 void CoreConnection::reconnectToCore()
358 if (currentAccount().isValid()) {
359 _wasReconnect = true;
360 connectToCore(currentAccount().accountId());
365 bool CoreConnection::connectToCore(AccountId accId)
370 CoreAccountSettings s;
372 // FIXME: Don't force connection to internal core in mono client
373 if (Quassel::runMode() == Quassel::Monolithic) {
374 _account = accountModel()->account(accountModel()->internalAccount());
375 Q_ASSERT(_account.isValid());
378 if (!accId.isValid()) {
379 // check our settings and figure out what to do
380 if (!s.autoConnectOnStartup())
382 if (s.autoConnectToFixedAccount())
383 accId = s.autoConnectAccount();
385 accId = s.lastAccount();
386 if (!accId.isValid())
389 _account = accountModel()->account(accId);
390 if (!_account.accountId().isValid()) {
393 if (Quassel::runMode() != Quassel::Monolithic) {
394 if (_account.isInternal())
399 s.setLastAccount(accId);
400 connectToCurrentAccount();
405 void CoreConnection::connectToCurrentAccount()
408 qWarning() << Q_FUNC_INFO << "Already connected!";
412 if (currentAccount().isInternal()) {
413 if (Quassel::runMode() != Quassel::Monolithic) {
414 qWarning() << "Cannot connect to internal core in client-only mode!";
417 emit startInternalCore();
419 InternalPeer *peer = new InternalPeer();
421 Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
422 emit connectToInternalCore(peer);
428 _authHandler = new ClientAuthHandler(currentAccount(), this);
430 connect(_authHandler, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
431 connect(_authHandler, SIGNAL(connectionReady()), SLOT(onConnectionReady()));
432 connect(_authHandler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
433 connect(_authHandler, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
434 connect(_authHandler, SIGNAL(requestDisconnect(QString,bool)), SLOT(disconnectFromCore(QString,bool)));
436 connect(_authHandler, SIGNAL(errorMessage(QString)), SIGNAL(connectionError(QString)));
437 connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)), Qt::QueuedConnection);
438 connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
439 connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool)));
440 connect(_authHandler, SIGNAL(startCoreSetup(QVariantList)), SIGNAL(startCoreSetup(QVariantList)));
441 connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString)));
442 connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess()));
443 connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)));
444 connect(_authHandler, SIGNAL(handleNoSslInClient(bool*)), SIGNAL(handleNoSslInClient(bool*)));
445 connect(_authHandler, SIGNAL(handleNoSslInCore(bool*)), SIGNAL(handleNoSslInCore(bool*)));
447 connect(_authHandler, SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)), SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)));
450 connect(_authHandler, SIGNAL(loginSuccessful(CoreAccount)), SLOT(onLoginSuccessful(CoreAccount)));
451 connect(_authHandler, SIGNAL(handshakeComplete(RemotePeer*,Protocol::SessionState)), SLOT(onHandshakeComplete(RemotePeer*,Protocol::SessionState)));
453 setState(Connecting);
454 _authHandler->connectToCore();
458 void CoreConnection::setupCore(const Protocol::SetupData &setupData)
460 _authHandler->setupCore(setupData);
464 void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
466 _authHandler->login(user, password, remember);
470 void CoreConnection::onLoginSuccessful(const CoreAccount &account)
472 updateProgress(0, 0);
474 // save current account data
475 accountModel()->createOrUpdateAccount(account);
476 accountModel()->save();
478 _reconnectTimer.stop();
480 setProgressText(tr("Receiving session state"));
481 setState(Synchronizing);
482 emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
486 void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
488 updateProgress(100, 100);
490 disconnect(_authHandler, 0, this, 0);
491 _authHandler->deleteLater();
495 connect(peer, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
496 connect(peer, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
497 connect(peer, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
499 Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
501 syncToCore(sessionState);
505 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
507 updateProgress(100, 100);
509 Client::setCoreFeatures(Quassel::features()); // mono connection...
511 setState(Synchronizing);
512 syncToCore(sessionState);
516 void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
518 setProgressText(tr("Receiving network states"));
519 updateProgress(0, 100);
522 foreach(const QVariant &vid, sessionState.identities) {
523 Client::instance()->coreIdentityCreated(vid.value<Identity>());
527 // FIXME: get rid of this crap -- why?
528 NetworkModel *networkModel = Client::networkModel();
529 Q_ASSERT(networkModel);
530 foreach(const QVariant &vinfo, sessionState.bufferInfos)
531 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
533 // prepare sync progress thingys...
534 // FIXME: Care about removal of networks
535 _numNetsToSync = sessionState.networkIds.count();
536 updateProgress(0, _numNetsToSync);
538 // create network objects
539 foreach(const QVariant &networkid, sessionState.networkIds) {
540 NetworkId netid = networkid.value<NetworkId>();
541 if (Client::network(netid))
543 Network *net = new Network(netid, Client::instance());
544 _netsToSync.insert(net);
545 connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
546 connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
547 Client::addNetwork(net);
553 // this is also called for destroyed networks!
554 void CoreConnection::networkInitDone()
556 QObject *net = sender();
558 disconnect(net, 0, this, 0);
559 _netsToSync.remove(net);
560 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
565 void CoreConnection::checkSyncState()
567 if (_netsToSync.isEmpty() && state() >= Synchronizing) {
568 setState(Synchronized);
569 setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
570 setProgressMaximum(-1);