modernize: Prefer default member init over ctor init
[quassel.git] / src / client / coreconnection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "coreconnection.h"
22
23 #include "client.h"
24 #include "clientauthhandler.h"
25 #include "clientsettings.h"
26 #include "coreaccountmodel.h"
27 #include "identity.h"
28 #include "internalpeer.h"
29 #include "network.h"
30 #include "networkmodel.h"
31 #include "quassel.h"
32 #include "signalproxy.h"
33 #include "util.h"
34
35 #include "protocols/legacy/legacypeer.h"
36
37 CoreConnection::CoreConnection(QObject *parent)
38     : QObject(parent),
39     _authHandler(nullptr)
40 {
41     qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
42 }
43
44
45 void CoreConnection::init()
46 {
47     Client::signalProxy()->setHeartBeatInterval(30);
48     connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), SIGNAL(lagUpdated(int)));
49
50     _reconnectTimer.setSingleShot(true);
51     connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout()));
52
53     _qNetworkConfigurationManager = new QNetworkConfigurationManager(this);
54     connect(_qNetworkConfigurationManager, SIGNAL(onlineStateChanged(bool)), SLOT(onlineStateChanged(bool)));
55
56     CoreConnectionSettings s;
57     s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60);
58     s.initAndNotify("ReconnectInterval", this, SLOT(reconnectIntervalChanged(QVariant)), 60);
59     s.notify("NetworkDetectionMode", this, SLOT(networkDetectionModeChanged(QVariant)));
60     networkDetectionModeChanged(s.networkDetectionMode());
61 }
62
63
64 CoreAccountModel *CoreConnection::accountModel() const
65 {
66     return Client::coreAccountModel();
67 }
68
69
70 void CoreConnection::setProgressText(const QString &text)
71 {
72     if (_progressText != text) {
73         _progressText = text;
74         emit progressTextChanged(text);
75     }
76 }
77
78
79 void CoreConnection::setProgressValue(int value)
80 {
81     if (_progressValue != value) {
82         _progressValue = value;
83         emit progressValueChanged(value);
84     }
85 }
86
87
88 void CoreConnection::setProgressMinimum(int minimum)
89 {
90     if (_progressMinimum != minimum) {
91         _progressMinimum = minimum;
92         emit progressRangeChanged(minimum, _progressMaximum);
93     }
94 }
95
96
97 void CoreConnection::setProgressMaximum(int maximum)
98 {
99     if (_progressMaximum != maximum) {
100         _progressMaximum = maximum;
101         emit progressRangeChanged(_progressMinimum, maximum);
102     }
103 }
104
105
106 void CoreConnection::updateProgress(int value, int max)
107 {
108     if (max != _progressMaximum) {
109         _progressMaximum = max;
110         emit progressRangeChanged(_progressMinimum, _progressMaximum);
111     }
112     setProgressValue(value);
113 }
114
115
116 void CoreConnection::reconnectTimeout()
117 {
118     if (!_peer) {
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()) {
124                     return;
125                }
126             }
127             reconnectToCore();
128         }
129     }
130 }
131
132
133 void CoreConnection::networkDetectionModeChanged(const QVariant &vmode)
134 {
135     CoreConnectionSettings s;
136     CoreConnectionSettings::NetworkDetectionMode mode = (CoreConnectionSettings::NetworkDetectionMode)vmode.toInt();
137     if (mode == CoreConnectionSettings::UsePingTimeout)
138         Client::signalProxy()->setMaxHeartBeatCount(s.pingTimeoutInterval() / 30);
139     else {
140         Client::signalProxy()->setMaxHeartBeatCount(-1);
141     }
142 }
143
144
145 void CoreConnection::pingTimeoutIntervalChanged(const QVariant &interval)
146 {
147     CoreConnectionSettings s;
148     if (s.networkDetectionMode() == CoreConnectionSettings::UsePingTimeout)
149         Client::signalProxy()->setMaxHeartBeatCount(interval.toInt() / 30);  // interval is 30 seconds
150 }
151
152
153 void CoreConnection::reconnectIntervalChanged(const QVariant &interval)
154 {
155     _reconnectTimer.setInterval(interval.toInt() * 1000);
156 }
157
158
159 void CoreConnection::onlineStateChanged(bool isOnline)
160 {
161     CoreConnectionSettings s;
162     if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager)
163         return;
164
165     if(isOnline) {
166         // qDebug() << "QNetworkConfigurationManager reports Online";
167         if (state() == Disconnected) {
168             if (_wantReconnect && s.autoReconnect()) {
169                 reconnectToCore();
170             }
171         }
172     } else {
173         // qDebug() << "QNetworkConfigurationManager reports Offline";
174         if (state() != Disconnected && !isLocalConnection())
175             disconnectFromCore(tr("Network is down"), true);
176     }
177 }
178
179
180 QPointer<Peer> CoreConnection::peer() const
181 {
182     if (_peer) {
183         return _peer;
184     }
185     return _authHandler ? _authHandler->peer() : nullptr;
186 }
187
188
189 bool CoreConnection::isEncrypted() const
190 {
191     return _peer && _peer->isSecure();
192 }
193
194
195 bool CoreConnection::isLocalConnection() const
196 {
197     if (!isConnected())
198         return false;
199     if (currentAccount().isInternal())
200         return true;
201     if (_authHandler)
202         return _authHandler->isLocal();
203     if (_peer)
204         return _peer->isLocal();
205
206     return false;
207 }
208
209
210 void CoreConnection::onConnectionReady()
211 {
212     setState(Connected);
213 }
214
215
216 void CoreConnection::setState(ConnectionState state)
217 {
218     if (state != _state) {
219         _state = state;
220         emit stateChanged(state);
221         if (state == Connected)
222             _wantReconnect = true;
223         if (state == Disconnected)
224             emit disconnected();
225     }
226 }
227
228
229 void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
230 {
231     Q_UNUSED(error)
232
233     disconnectFromCore(errorString, true);
234 }
235
236
237 void CoreConnection::coreSocketDisconnected()
238 {
239     setState(Disconnected);
240     _wasReconnect = false;
241     resetConnection(_wantReconnect);
242 }
243
244
245 void CoreConnection::disconnectFromCore()
246 {
247     disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
248 }
249
250
251 void CoreConnection::disconnectFromCore(const QString &errorString, bool wantReconnect)
252 {
253     if (wantReconnect)
254         _reconnectTimer.start();
255     else
256         _reconnectTimer.stop();
257
258     _wantReconnect = wantReconnect; // store if disconnect was requested
259     _wasReconnect = false;
260
261     if (_authHandler)
262         _authHandler->close();
263     else if(_peer)
264         _peer->close();
265
266     if (errorString.isEmpty())
267         emit connectionError(tr("Disconnected"));
268     else
269         emit connectionError(errorString);
270 }
271
272
273 void CoreConnection::resetConnection(bool wantReconnect)
274 {
275     if (_resetting)
276         return;
277     _resetting = true;
278
279     _wantReconnect = wantReconnect;
280
281     if (_authHandler) {
282         disconnect(_authHandler, nullptr, this, nullptr);
283         _authHandler->close();
284         _authHandler->deleteLater();
285         _authHandler = nullptr;
286     }
287
288     if (_peer) {
289         disconnect(_peer, nullptr, this, nullptr);
290         // peer belongs to the sigproxy and thus gets deleted by it
291         _peer->close();
292         _peer = nullptr;
293     }
294
295     _netsToSync.clear();
296     _numNetsToSync = 0;
297
298     setProgressMaximum(-1); // disable
299     setState(Disconnected);
300     emit lagUpdated(-1);
301
302     emit connectionMsg(tr("Disconnected from core."));
303     emit encrypted(false);
304     setState(Disconnected);
305
306     // initiate if a reconnect if appropriate
307     CoreConnectionSettings s;
308     if (wantReconnect && s.autoReconnect()) {
309         _reconnectTimer.start();
310     }
311
312     _resetting = false;
313 }
314
315
316 void CoreConnection::reconnectToCore()
317 {
318     if (currentAccount().isValid()) {
319         _wasReconnect = true;
320         connectToCore(currentAccount().accountId());
321     }
322 }
323
324
325 bool CoreConnection::connectToCore(AccountId accId)
326 {
327     if (isConnected())
328         return false;
329
330     CoreAccountSettings s;
331
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());
336     }
337     else {
338         if (!accId.isValid()) {
339             // check our settings and figure out what to do
340             if (!s.autoConnectOnStartup())
341                 return false;
342             if (s.autoConnectToFixedAccount())
343                 accId = s.autoConnectAccount();
344             else
345                 accId = s.lastAccount();
346             if (!accId.isValid())
347                 return false;
348         }
349         _account = accountModel()->account(accId);
350         if (!_account.accountId().isValid()) {
351             return false;
352         }
353         if (Quassel::runMode() != Quassel::Monolithic) {
354             if (_account.isInternal())
355                 return false;
356         }
357     }
358
359     s.setLastAccount(accId);
360     connectToCurrentAccount();
361     return true;
362 }
363
364
365 void CoreConnection::connectToCurrentAccount()
366 {
367     if (_authHandler) {
368         qWarning() << Q_FUNC_INFO << "Already connected!";
369         return;
370     }
371
372     if (currentAccount().isInternal()) {
373         if (Quassel::runMode() != Quassel::Monolithic) {
374             qWarning() << "Cannot connect to internal core in client-only mode!";
375             return;
376         }
377
378         InternalPeer *peer = new InternalPeer();
379         _peer = peer;
380         Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
381         emit connectionMsg(tr("Initializing..."));
382         emit connectToInternalCore(peer);
383         setState(Connected);
384         return;
385     }
386
387     _authHandler = new ClientAuthHandler(currentAccount(), this);
388
389     connect(_authHandler, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
390     connect(_authHandler, SIGNAL(connectionReady()), SLOT(onConnectionReady()));
391     connect(_authHandler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
392     connect(_authHandler, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
393     connect(_authHandler, SIGNAL(requestDisconnect(QString,bool)), SLOT(disconnectFromCore(QString,bool)));
394
395     connect(_authHandler, SIGNAL(errorMessage(QString)), SIGNAL(connectionError(QString)));
396     connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)), Qt::QueuedConnection);
397     connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
398     connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool)));
399     connect(_authHandler, SIGNAL(startCoreSetup(QVariantList, QVariantList)), SIGNAL(startCoreSetup(QVariantList, QVariantList)));
400     connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString)));
401     connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess()));
402     connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)));
403     connect(_authHandler, SIGNAL(handleNoSslInClient(bool*)), SIGNAL(handleNoSslInClient(bool*)));
404     connect(_authHandler, SIGNAL(handleNoSslInCore(bool*)), SIGNAL(handleNoSslInCore(bool*)));
405 #ifdef HAVE_SSL
406     connect(_authHandler, SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)), SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)));
407 #endif
408
409     connect(_authHandler, SIGNAL(loginSuccessful(CoreAccount)), SLOT(onLoginSuccessful(CoreAccount)));
410     connect(_authHandler, SIGNAL(handshakeComplete(RemotePeer*,Protocol::SessionState)), SLOT(onHandshakeComplete(RemotePeer*,Protocol::SessionState)));
411
412     setState(Connecting);
413     _authHandler->connectToCore();
414 }
415
416
417 void CoreConnection::setupCore(const Protocol::SetupData &setupData)
418 {
419     _authHandler->setupCore(setupData);
420 }
421
422
423 void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
424 {
425     _authHandler->login(user, password, remember);
426 }
427
428
429 void CoreConnection::onLoginSuccessful(const CoreAccount &account)
430 {
431     updateProgress(0, 0);
432
433     // save current account data
434     accountModel()->createOrUpdateAccount(account);
435     accountModel()->save();
436
437     _reconnectTimer.stop();
438
439     setProgressText(tr("Receiving session state"));
440     setState(Synchronizing);
441     emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
442 }
443
444
445 void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
446 {
447     updateProgress(100, 100);
448
449     disconnect(_authHandler, nullptr, this, nullptr);
450     _authHandler->deleteLater();
451     _authHandler = nullptr;
452
453     _peer = peer;
454     connect(peer, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
455     connect(peer, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
456     connect(peer, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
457
458     Client::signalProxy()->addPeer(_peer);  // sigproxy takes ownership of the peer!
459
460     syncToCore(sessionState);
461 }
462
463
464 void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
465 {
466     updateProgress(100, 100);
467     setState(Synchronizing);
468     syncToCore(sessionState);
469 }
470
471
472 void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
473 {
474     setProgressText(tr("Receiving network states"));
475     updateProgress(0, 100);
476
477     // create identities
478     foreach(const QVariant &vid, sessionState.identities) {
479         Client::instance()->coreIdentityCreated(vid.value<Identity>());
480     }
481
482     // create buffers
483     // FIXME: get rid of this crap -- why?
484     NetworkModel *networkModel = Client::networkModel();
485     Q_ASSERT(networkModel);
486     foreach(const QVariant &vinfo, sessionState.bufferInfos)
487         networkModel->bufferUpdated(vinfo.value<BufferInfo>());  // create BufferItems
488
489     // prepare sync progress thingys...
490     // FIXME: Care about removal of networks
491     _numNetsToSync = sessionState.networkIds.count();
492     updateProgress(0, _numNetsToSync);
493
494     // create network objects
495     foreach(const QVariant &networkid, sessionState.networkIds) {
496         NetworkId netid = networkid.value<NetworkId>();
497         if (Client::network(netid))
498             continue;
499         Network *net = new Network(netid, Client::instance());
500         _netsToSync.insert(net);
501         connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
502         connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
503         Client::addNetwork(net);
504     }
505     checkSyncState();
506 }
507
508
509 // this is also called for destroyed networks!
510 void CoreConnection::networkInitDone()
511 {
512     QObject *net = sender();
513     Q_ASSERT(net);
514     disconnect(net, nullptr, this, nullptr);
515     _netsToSync.remove(net);
516     updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
517     checkSyncState();
518 }
519
520
521 void CoreConnection::checkSyncState()
522 {
523     if (_netsToSync.isEmpty() && state() >= Synchronizing) {
524         setState(Synchronized);
525         setProgressText(tr("Synchronized to %1").arg(currentAccount().accountName()));
526         setProgressMaximum(-1);
527         emit synchronized();
528     }
529 }