1 /***************************************************************************
2 * Copyright (C) 2005-2013 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()));
61 connect(Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)),
62 SLOT(solidNetworkStatusChanged(Solid::Networking::Status)));
65 CoreConnectionSettings s;
66 s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60);
67 s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60);
68 s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant)));
69 networkDetectionModeChanged(s.networkDetectionMode());
73 CoreAccountModel *CoreConnection::accountModel() const
75 return Client::coreAccountModel();
79 void CoreConnection::setProgressText(const QString &text)
81 if (_progressText != text) {
83 emit progressTextChanged(text);
88 void CoreConnection::setProgressValue(int value)
90 if (_progressValue != value) {
91 _progressValue = value;
92 emit progressValueChanged(value);
97 void CoreConnection::setProgressMinimum(int minimum)
99 if (_progressMinimum != minimum) {
100 _progressMinimum = minimum;
101 emit progressRangeChanged(minimum, _progressMaximum);
106 void CoreConnection::setProgressMaximum(int maximum)
108 if (_progressMaximum != maximum) {
109 _progressMaximum = maximum;
110 emit progressRangeChanged(_progressMinimum, maximum);
115 void CoreConnection::updateProgress(int value, int max)
117 if (max != _progressMaximum) {
118 _progressMaximum = max;
119 emit progressRangeChanged(_progressMinimum, _progressMaximum);
121 setProgressValue(value);
125 void CoreConnection::reconnectTimeout()
128 CoreConnectionSettings s;
129 if (_wantReconnect && s.autoReconnect()) {
131 // If using Solid, we don't want to reconnect if we're offline
132 if (s.networkDetectionMode() == CoreConnectionSettings::UseSolid) {
133 if (Solid::Networking::status() != Solid::Networking::Connected
134 && Solid::Networking::status() != Solid::Networking::Unknown) {
138 #endif /* HAVE_KDE */
146 void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
148 CoreConnectionSettings s;
149 CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
150 if (mode == CoreConnectionSettings::UsePingTimeout)
151 Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
153 Client::signalProxy()->setMaxHeartBeatCount(-1);
158 void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
160 CoreConnectionSettings s;
161 if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
162 Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30); // interval is 30 seconds
166 void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
168 _reconnectTimer.setInterval(interval.toInt() * 1000);
174 void CoreConnection::solidNetworkStatusChanged(Solid::Networking::Status status)
176 CoreConnectionSettings s;
177 if (s.networkDetectionMode() != CoreConnectionSettings::UseSolid)
181 case Solid::Networking::Unknown:
182 case Solid::Networking::Connected:
183 //qDebug() << "Solid: Network status changed to connected or unknown";
184 if (state() == Disconnected) {
185 if (_wantReconnect && s.autoReconnect()) {
190 case Solid::Networking::Disconnecting:
191 case Solid::Networking::Unconnected:
192 if (state() != Disconnected && !isLocalConnection())
193 disconnectFromCore(tr("Network is down"), true);
202 bool CoreConnection::isEncrypted() const
204 return _peer && _peer->isSecure();
208 bool CoreConnection::isLocalConnection() const
212 if (currentAccount().isInternal())
214 if (_peer->isLocal())
221 void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState)
225 switch (socketState) {
226 case QAbstractSocket::UnconnectedState:
227 text = tr("Disconnected");
229 case QAbstractSocket::HostLookupState:
230 text = tr("Looking up %1...").arg(currentAccount().hostName());
232 case QAbstractSocket::ConnectingState:
233 text = tr("Connecting to %1...").arg(currentAccount().hostName());
235 case QAbstractSocket::ConnectedState:
236 text = tr("Connected to %1").arg(currentAccount().hostName());
238 case QAbstractSocket::ClosingState:
239 text = tr("Disconnecting from %1...").arg(currentAccount().hostName());
246 emit progressTextChanged(text);
248 setState(socketState);
252 void CoreConnection::setState(QAbstractSocket::SocketState socketState)
254 ConnectionState state;
256 switch (socketState) {
257 case QAbstractSocket::UnconnectedState:
258 state = Disconnected;
260 case QAbstractSocket::HostLookupState:
261 case QAbstractSocket::ConnectingState:
262 case QAbstractSocket::ConnectedState: // we'll set it to Connected in connectionReady()
266 state = Disconnected;
273 void CoreConnection::onConnectionReady()
279 void CoreConnection::setState(ConnectionState state)
281 if (state != _state) {
283 emit stateChanged(state);
284 if (state == Disconnected)
290 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
294 disconnectFromCore(errorString, true);
298 void CoreConnection::coreSocketDisconnected()
300 _wasReconnect = false;
301 resetConnection(_wantReconnect);
302 // FIXME handle disconnects gracefully
306 void CoreConnection::disconnectFromCore()
308 disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
312 void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
315 _reconnectTimer.start();
317 _reconnectTimer.stop();
319 _wantReconnect = wantReconnect; // store if disconnect was requested
320 _wasReconnect = false;
323 _authHandler->close();
327 if (errorString.isEmpty())
328 emit connectionError(tr("Disconnected"));
330 emit connectionError(errorString);
334 void CoreConnection::resetConnection(bool wantReconnect)
340 _wantReconnect = wantReconnect;
343 disconnect(_authHandler, 0, this, 0);
344 _authHandler->close();
345 _authHandler->deleteLater();
350 disconnect(_peer, 0, this, 0);
351 // peer belongs to the sigproxy and thus gets deleted by it
359 setProgressMaximum(-1); // disable
360 setState(Disconnected);
363 emit connectionMsg(tr("Disconnected from core."));
364 emit encrypted(false);
366 // initiate if a reconnect if appropriate
367 CoreConnectionSettings s;
368 if (wantReconnect && s.autoReconnect()) {
369 _reconnectTimer.start();
376 void CoreConnection::reconnectToCore()
378 if (currentAccount().isValid()) {
379 _wasReconnect = true;
380 connectToCore(currentAccount().accountId());
385 bool CoreConnection::connectToCore(AccountId accId)
390 CoreAccountSettings s;
392 // FIXME: Don't force connection to internal core in mono client
393 if (Quassel::runMode() == Quassel::Monolithic) {
394 _account = accountModel()->account(accountModel()->internalAccount());
395 Q_ASSERT(_account.isValid());
398 if (!accId.isValid()) {
399 // check our settings and figure out what to do
400 if (!s.autoConnectOnStartup())
402 if (s.autoConnectToFixedAccount())
403 accId = s.autoConnectAccount();
405 accId = s.lastAccount();
406 if (!accId.isValid())
409 _account = accountModel()->account(accId);
410 if (!_account.accountId().isValid()) {
413 if (Quassel::runMode() != Quassel::Monolithic) {
414 if (_account.isInternal())
419 s.setLastAccount(accId);
420 connectToCurrentAccount();
425 void CoreConnection::connectToCurrentAccount()
428 qWarning() << Q_FUNC_INFO << "Already connected!";
432 resetConnection(false);
434 if (currentAccount().isInternal()) {
435 if (Quassel::runMode() != Quassel::Monolithic) {
436 qWarning() << "Cannot connect to internal core in client-only mode!";
439 emit startInternalCore();
441 InternalPeer *peer = new InternalPeer();
443 Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
444 emit connectToInternalCore(peer);
449 _authHandler = new ClientAuthHandler(currentAccount(), this);
451 connect(_authHandler, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
452 connect(_authHandler, SIGNAL(connectionReady()), SLOT(onConnectionReady()));
453 connect(_authHandler, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
454 connect(_authHandler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
455 connect(_authHandler, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
456 connect(_authHandler, SIGNAL(requestDisconnect(QString,bool)), SLOT(disconnectFromCore(QString,bool)));
458 connect(_authHandler, SIGNAL(errorMessage(QString)), SIGNAL(connectionError(QString)));
459 connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)), Qt::QueuedConnection);
460 connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
461 connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool)));
462 connect(_authHandler, SIGNAL(startCoreSetup(QVariantList)), SIGNAL(startCoreSetup(QVariantList)));
463 connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString)));
464 connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess()));
465 connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)));
466 connect(_authHandler, SIGNAL(handleNoSslInClient(bool*)), SIGNAL(handleNoSslInClient(bool*)));
467 connect(_authHandler, SIGNAL(handleNoSslInCore(bool*)), SIGNAL(handleNoSslInCore(bool*)));
469 connect(_authHandler, SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)), SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)));
472 connect(_authHandler, SIGNAL(loginSuccessful(CoreAccount)), SLOT(onLoginSuccessful(CoreAccount)));
473 connect(_authHandler, SIGNAL(handshakeComplete(RemotePeer*,Protocol::SessionState)), SLOT(onHandshakeComplete(RemotePeer*,Protocol::SessionState)));
475 _authHandler->connectToCore();
479 void CoreConnection::setupCore(const Protocol::SetupData &setupData)
481 _authHandler->setupCore(setupData);
485 void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
487 _authHandler->login(user, password, remember);
491 void CoreConnection::onLoginSuccessful(const CoreAccount &account)
493 updateProgress(0, 0);
495 // save current account data
496 accountModel()->createOrUpdateAccount(account);
497 accountModel()->save();
499 _reconnectTimer.stop();
501 setProgressText(tr("Receiving session state"));
502 setState(Synchronizing);
503 emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
507 void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
509 updateProgress(100, 100);
511 disconnect(_authHandler, 0, this, 0);
512 _authHandler->deleteLater();
516 connect(peer, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
517 connect(peer, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
518 connect(peer, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
520 Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
522 syncToCore(sessionState);
526 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
528 updateProgress(100, 100);
530 Client::setCoreFeatures(Quassel::features()); // mono connection...
532 setState(Synchronizing);
533 syncToCore(sessionState);
537 void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
539 setProgressText(tr("Receiving network states"));
540 updateProgress(0, 100);
543 foreach(const QVariant &vid, sessionState.identities) {
544 Client::instance()->coreIdentityCreated(vid.value<Identity>());
548 // FIXME: get rid of this crap -- why?
549 NetworkModel *networkModel = Client::networkModel();
550 Q_ASSERT(networkModel);
551 foreach(const QVariant &vinfo, sessionState.bufferInfos)
552 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
554 // prepare sync progress thingys...
555 // FIXME: Care about removal of networks
556 _numNetsToSync = sessionState.networkIds.count();
557 updateProgress(0, _numNetsToSync);
559 // create network objects
560 foreach(const QVariant &networkid, sessionState.networkIds) {
561 NetworkId netid = networkid.value<NetworkId>();
562 if (Client::network(netid))
564 Network *net = new Network(netid, Client::instance());
565 _netsToSync.insert(net);
566 connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
567 connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
568 Client::addNetwork(net);
574 // this is also called for destroyed networks!
575 void CoreConnection::networkInitDone()
577 QObject *net = sender();
579 disconnect(net, 0, this, 0);
580 _netsToSync.remove(net);
581 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
586 void CoreConnection::checkSyncState()
588 if (_netsToSync.isEmpty() && state() >= Synchronizing) {
589 setState(Synchronized);
590 setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
591 setProgressMaximum(-1);