From e375f7a6a6de045735a897ef05bf6dcc82770ff5 Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Mon, 18 Jun 2018 16:16:37 -0500 Subject: [PATCH] client: Detect legacy CoreInfo, request on show Add feature flag 'SyncedCoreInfo' to signify the core supports signals for the CoreInfo object. Detect cores without support for 'SyncedCoreInfo', and poll for core information on every CoreInfoDlg show. This avoids regressing functionality for older cores. Synchronize the CoreInfo object before connecting, ensuring it's ready to receive parameters on first connect, mimicking backlog requester. This avoids a warning about "No registered receiver" for the CoreInfo handler. Reset CoreInfo object on disconnect, update CoreInfoDlg with "Disconnected from core" message. This reduces confusion about the state of the core; before, it would simply stop updating. Remove several nullpointer checks, CoreInfo now should never be null. Add Client::coreInfoResynchronized() signal for CoreInfo resynchronization, allowing for periodic updates on legacy cores. --- src/client/client.cpp | 42 +++++++++++++++++++------ src/client/client.h | 33 ++++++++++++++++++++ src/common/coreinfo.cpp | 8 +++++ src/common/coreinfo.h | 8 +++++ src/common/quassel.h | 1 + src/qtui/coreinfodlg.cpp | 67 ++++++++++++++++++++++++++++++++++------ src/qtui/coreinfodlg.h | 7 +++++ 7 files changed, 147 insertions(+), 19 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 6cd94c14..32f599d3 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -101,7 +101,7 @@ Client::Client(QObject *parent) _backlogManager(new ClientBacklogManager(this)), _bufferViewManager(0), _bufferViewOverlay(new BufferViewOverlay(this)), - _coreInfo(nullptr), + _coreInfo(new CoreInfo(this)), _dccConfig(0), _ircListHelper(new ClientIrcListHelper(this)), _inputHandler(0), @@ -175,6 +175,9 @@ void Client::init() coreAccountModel()->load(); + // Attach CoreInfo + p->synchronize(coreInfo()); + connect(coreConnection(), SIGNAL(stateChanged(CoreConnection::ConnectionState)), SLOT(connectionStateChanged(CoreConnection::ConnectionState))); coreConnection()->init(); } @@ -407,11 +410,6 @@ void Client::setSyncedToCore() SignalProxy *p = signalProxy(); p->synchronize(bufferSyncer()); - // create CoreInfo - Q_ASSERT(!_coreInfo); - _coreInfo = new CoreInfo(this); - p->synchronize(coreInfo()); - // create a new BufferViewManager Q_ASSERT(!_bufferViewManager); _bufferViewManager = new ClientBufferViewManager(p, this); @@ -486,6 +484,27 @@ void Client::requestInitialBacklog() } +void Client::requestLegacyCoreInfo() +{ + // On older cores, the CoreInfo object was only synchronized on demand. Synchronize now if + // needed. + if (isConnected() && !isCoreFeatureEnabled(Quassel::Feature::SyncedCoreInfo)) { + // Delete the existing core info object (it will always exist as client is single-threaded) + _coreInfo->deleteLater(); + // No need to set to null when creating new one immediately after + + // Create a fresh, unsynchronized CoreInfo object, emulating legacy behavior of CoreInfo not + // persisting + _coreInfo = new CoreInfo(this); + // Synchronize the new object + signalProxy()->synchronize(_coreInfo); + + // Let others know signal handlers have been reset + emit coreInfoResynchronized(); + } +} + + void Client::disconnectFromCore() { if (!coreConnection()->isConnected()) @@ -512,10 +531,7 @@ void Client::setDisconnectedFromCore() _bufferSyncer = 0; } - if (_coreInfo) { - _coreInfo->deleteLater(); - _coreInfo = nullptr; - } + _coreInfo->reset(); if (_bufferViewManager) { _bufferViewManager->deleteLater(); @@ -711,6 +727,12 @@ void Client::markBufferAsRead(BufferId id) } +void Client::refreshLegacyCoreInfo() +{ + instance()->requestLegacyCoreInfo(); +} + + void Client::changePassword(const QString &oldPassword, const QString &newPassword) { CoreAccount account = currentCoreAccount(); account.setPassword(newPassword); diff --git a/src/client/client.h b/src/client/client.h index 3676e296..f8baa983 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -149,6 +149,17 @@ public: static void mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2); static void purgeKnownBufferIds(); + /** + * Requests client to resynchronize the CoreInfo object for legacy (pre-0.13) cores + * + * This provides compatibility with updating core information for legacy cores, and can be + * removed after protocol break. + * + * NOTE: On legacy (pre-0.13) cores, any existing connected signals will be destroyed and must + * be re-added after calling this, in addition to checking for existing data in coreInfo(). + */ + static void refreshLegacyCoreInfo(); + static void changePassword(const QString &oldPassword, const QString &newPassword); static void kickClient(int peerId); @@ -178,6 +189,17 @@ signals: void disconnected(); void coreConnectionStateChanged(bool); + /** + * Signals that core information has been resynchronized, removing existing signal handlers + * + * Whenever this is emitted, one should re-add any handlers for CoreInfo::coreDataChanged() and + * apply any existing information in the coreInfo() object. + * + * Only emitted on legacy (pre-0.13) cores. Generally, one should use the + * CoreInfo::coreDataChanged() signal too. + */ + void coreInfoResynchronized(); + //! The identity with the given ID has been newly created in core and client. /** \param id The ID of the newly created identity. */ @@ -258,6 +280,17 @@ private: void requestInitialBacklog(); + /** + * Deletes and resynchronizes the CoreInfo object for legacy (pre-0.13) cores + * + * This provides compatibility with updating core information for legacy cores, and can be + * removed after protocol break. + * + * NOTE: On legacy (pre-0.13) cores, any existing connected signals will be destroyed and must + * be re-added after calling this, in addition to checking for existing data in coreInfo(). + */ + void requestLegacyCoreInfo(); + static void addNetwork(Network *); static QPointer instanceptr; diff --git a/src/common/coreinfo.cpp b/src/common/coreinfo.cpp index e4019c7a..cfa86ab7 100644 --- a/src/common/coreinfo.cpp +++ b/src/common/coreinfo.cpp @@ -41,3 +41,11 @@ void CoreInfo::setConnectedClientData(const int peerCount, const QVariantList pe _coreData["sessionConnectedClientData"] = peerData; setCoreData(_coreData); } + +void CoreInfo::reset() +{ + // Clear any stored data + _coreData.clear(); + // Propagate changes to listeners + emit coreDataChanged(_coreData); +} diff --git a/src/common/coreinfo.h b/src/common/coreinfo.h index 26879d4c..284336cb 100644 --- a/src/common/coreinfo.h +++ b/src/common/coreinfo.h @@ -39,7 +39,15 @@ public: void setConnectedClientData(int, QVariantList); + /** + * Reset the core info state, clearing anything saved + */ + void reset(); + signals: + /** + * Signals that core information has changed + */ void coreDataChanged(QVariantMap); public slots: diff --git a/src/common/quassel.h b/src/common/quassel.h index 50ea82a2..952f8fa8 100644 --- a/src/common/quassel.h +++ b/src/common/quassel.h @@ -138,6 +138,7 @@ public: EcdsaCertfpKeys, ///< ECDSA keys for CertFP in identities #endif LongMessageId, ///< 64-bit IDs for messages + SyncedCoreInfo, ///< CoreInfo dynamically updated using signals }; Q_ENUMS(Feature) diff --git a/src/qtui/coreinfodlg.cpp b/src/qtui/coreinfodlg.cpp index e80450ff..063e7a43 100644 --- a/src/qtui/coreinfodlg.cpp +++ b/src/qtui/coreinfodlg.cpp @@ -28,10 +28,17 @@ CoreInfoDlg::CoreInfoDlg(QWidget *parent) : QDialog(parent) { ui.setupUi(this); - CoreInfo *coreInfo = Client::coreInfo(); - connect(coreInfo, SIGNAL(coreDataChanged(const QVariantMap &)), this, SLOT(coreInfoChanged(const QVariantMap &))); - coreInfoChanged(coreInfo->coreData()); + // Listen for resynchronization events (pre-0.13 cores only) + connect(Client::instance(), SIGNAL(coreInfoResynchronized()), + this, SLOT(coreInfoResynchronized())); + + // Update legacy core info for Quassel cores earlier than 0.13. This does nothing on modern + // cores. + Client::refreshLegacyCoreInfo(); + + // Display existing core info, set up signal handlers + coreInfoResynchronized(); // Warning icon ui.coreUnsupportedIcon->setPixmap(icon::get("dialog-warning").pixmap(16)); @@ -41,10 +48,40 @@ CoreInfoDlg::CoreInfoDlg(QWidget *parent) : QDialog(parent) { } +void CoreInfoDlg::coreInfoResynchronized() { + // CoreInfo object has been recreated, or this is the first time the dialog's been shown + + CoreInfo *coreInfo = Client::coreInfo(); + // Listen for changes to core information + connect(coreInfo, SIGNAL(coreDataChanged(const QVariantMap &)), + this, SLOT(coreInfoChanged(const QVariantMap &))); + + // Update with any known core information set before connecting the signal. This is needed for + // both modern (0.13+) and legacy cores. + coreInfoChanged(coreInfo->coreData()); +} + + void CoreInfoDlg::coreInfoChanged(const QVariantMap &coreInfo) { - ui.labelCoreVersion->setText(coreInfo["quasselVersion"].toString()); - ui.labelCoreVersionDate->setText(coreInfo["quasselBuildDate"].toString()); // "BuildDate" for compatibility - ui.labelClientCount->setNum(coreInfo["sessionConnectedClients"].toInt()); + if(coreInfo.isEmpty()) { + // We're missing data for some reason + if (Client::isConnected()) { + // Core info is entirely empty despite being connected. Something's probably wrong. + ui.labelCoreVersion->setText(tr("Unknown")); + ui.labelCoreVersionDate->setText(tr("Unknown")); + } else { + // We're disconnected. Mark as such. + ui.labelCoreVersion->setText(tr("Disconnected from core")); + ui.labelCoreVersionDate->setText(tr("Not available")); + } + ui.labelClientCount->setNum(0); + // Don't return, allow the code below to remove any existing session widgets + } else { + ui.labelCoreVersion->setText(coreInfo["quasselVersion"].toString()); + // "BuildDate" for compatibility + ui.labelCoreVersionDate->setText(coreInfo["quasselBuildDate"].toString()); + ui.labelClientCount->setNum(coreInfo["sessionConnectedClients"].toInt()); + } auto coreSessionSupported = false; auto ids = _widgets.keys(); @@ -80,14 +117,26 @@ void CoreInfoDlg::coreInfoChanged(const QVariantMap &coreInfo) { ui.coreSessionScrollArea->setVisible(coreSessionSupported); - // Hide the information bar when core sessions are supported - ui.coreUnsupportedWidget->setVisible(!coreSessionSupported); + // Hide the information bar when core sessions are supported or when disconnected + ui.coreUnsupportedWidget->setVisible( + !(coreSessionSupported || Client::isConnected() == false)); + + // Update uptime for immediate display, don't wait for the timer + updateUptime(); } void CoreInfoDlg::updateUptime() { CoreInfo *coreInfo = Client::coreInfo(); - if (coreInfo) { + + if (!Client::isConnected()) { + // Not connected, don't bother trying to calculate the uptime + ui.labelUptime->setText(tr("Not available")); + } else if (coreInfo->coreData().isEmpty()) { + // Core info is entirely empty despite being connected. Something's probably wrong. + ui.labelUptime->setText(tr("Unknown")); + } else { + // Connected, format the uptime for display QDateTime startTime = coreInfo->at("startTime").toDateTime(); int64_t uptime = startTime.secsTo(QDateTime::currentDateTime().toUTC()); diff --git a/src/qtui/coreinfodlg.h b/src/qtui/coreinfodlg.h index 15140f79..0636534b 100644 --- a/src/qtui/coreinfodlg.h +++ b/src/qtui/coreinfodlg.h @@ -39,6 +39,13 @@ protected: void timerEvent(QTimerEvent *) override { updateUptime(); } private slots: + /** + * Handler for recreation of CoreInfo object, including first-time setup + * + * Applies existing CoreInfo information to the dialog, too. + */ + void coreInfoResynchronized(); + void on_closeButton_clicked() { reject(); } void updateUptime(); void disconnectClicked(int peerId); -- 2.20.1