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