From 5fabceee9daeb53d76e804b2e54a6855a48151ac Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 28 Mar 2021 18:58:34 +0200 Subject: FEAT(client, ui): Revamped server information dialog The previous dialog was simply a message box with a bunch of HTML in order to give a little structure to the contents. This approach is very limiting in terms of UI flexibility though and therefore this commit replaces the old HTML approach with a dedicated dialog class that uses proper UI elements instead. While doing so, the ordering and grouping of information was also changed in order to make it more suitable for the every-day-user. Fixes #4733 --- src/mumble/CMakeLists.txt | 3 + src/mumble/MainWindow.cpp | 134 +------- src/mumble/ServerInformation.cpp | 199 ++++++++++++ src/mumble/ServerInformation.h | 37 +++ src/mumble/ServerInformation.ui | 643 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 885 insertions(+), 131 deletions(-) create mode 100644 src/mumble/ServerInformation.cpp create mode 100644 src/mumble/ServerInformation.h create mode 100644 src/mumble/ServerInformation.ui (limited to 'src') diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 96266ee8f..e90272181 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -176,6 +176,9 @@ set(MUMBLE_SOURCES "Screen.h" "ServerHandler.cpp" "ServerHandler.h" + "ServerInformation.cpp" + "ServerInformation.h" + "ServerInformation.ui" "Settings.cpp" "Settings.h" "SharedMemory.cpp" diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index 1b7723ed4..6d955c9d6 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -39,6 +39,7 @@ #include "SSLCipherInfo.h" #include "Screen.h" #include "ServerHandler.h" +#include "ServerInformation.h" #include "Settings.h" #include "SvgIcon.h" #include "TalkingUI.h" @@ -1403,138 +1404,9 @@ void MainWindow::on_qaServerUserList_triggered() { } } -static const QString currentCodec() { - if (Global::get().bOpus) - return QLatin1String("Opus"); - - int v = Global::get().bPreferAlpha ? Global::get().iCodecAlpha : Global::get().iCodecBeta; - CELTCodec *cc = Global::get().qmCodecs.value(v); - if (cc) - return QString::fromLatin1("CELT %1").arg(cc->version()); - else - return QString::fromLatin1("CELT %1").arg(QString::number(v, 16)); -} - void MainWindow::on_qaServerInformation_triggered() { - ConnectionPtr c = Global::get().sh->cConnection; - - if (!c) - return; - - QSslCipher qsc = Global::get().sh->qscCipher; - - QString qsVersion = tr("

Version

Protocol %1

").arg(MumbleVersion::toString(Global::get().sh->uiVersion)); - - if (Global::get().sh->qsRelease.isEmpty() || Global::get().sh->qsOS.isEmpty() || Global::get().sh->qsOSVersion.isEmpty()) { - qsVersion.append(tr("

No build information or OS version available

")); - } else { - qsVersion.append( - tr("

%1 (%2)
%3

") - .arg(Global::get().sh->qsRelease.toHtmlEscaped(), Global::get().sh->qsOS.toHtmlEscaped(), Global::get().sh->qsOSVersion.toHtmlEscaped())); - } - - QString host, uname, pw; - unsigned short port; - - Global::get().sh->getConnectionInfo(host, port, uname, pw); - - const SSLCipherInfo *ci = SSLCipherInfoLookupByOpenSSLName(qsc.name().toLatin1().constData()); - - QString cipherDescription; - if (ci && ci->message_authentication && ci->encryption && ci->key_exchange_verbose && ci->rfc_name) { - if (QString::fromLatin1(ci->message_authentication) == QLatin1String("AEAD")) { - // Authenticated Encryption with Associated Data - // See https://en.wikipedia.org/wiki/Authenticated_encryption - cipherDescription = - tr("The connection is encrypted and authenticated " - "using %1 and uses %2 as the key exchange mechanism (%3)") - .arg(QString::fromLatin1(ci->encryption), QString::fromLatin1(ci->key_exchange_verbose), - QString::fromLatin1(ci->rfc_name)); - } else { - cipherDescription = - tr("The connection is encrypted using %1, with %2 " - "for message authentication and %3 as the key " - "exchange mechanism (%4)") - .arg(QString::fromLatin1(ci->encryption), QString::fromLatin1(ci->message_authentication), - QString::fromLatin1(ci->key_exchange_verbose), QString::fromLatin1(ci->rfc_name)); - } - } - if (cipherDescription.isEmpty()) { - cipherDescription = - tr("The connection is secured by the cipher suite that OpenSSL identifies as %1").arg(qsc.name()); - } - - QString cipherPFSInfo; - if (ci) { - if (ci->forward_secret) { - cipherPFSInfo = tr("

The connection provides perfect forward secrecy

"); - } else { - cipherPFSInfo = tr("

The connection does not provide perfect forward secrecy

"); - } - } - - QString qsControl = - tr("

Control channel

" - "

The connection uses %1

" - "%2" - "%3" - "

%4 ms average latency (%5 deviation)

" - "

Remote host %6 (port %7)

") - .arg(c->sessionProtocolString().toHtmlEscaped(), cipherDescription, cipherPFSInfo, - QString::fromLatin1("%1").arg(boost::accumulators::mean(Global::get().sh->accTCP), 0, 'f', 2), - QString::fromLatin1("%1").arg(sqrt(boost::accumulators::variance(Global::get().sh->accTCP)), 0, 'f', 2), - host.toHtmlEscaped(), QString::number(port)); - if (Global::get().uiMaxUsers) { - qsControl += tr("

Connected users: %1/%2

").arg(ModelItem::c_qhUsers.count()).arg(Global::get().uiMaxUsers); - } - - QString qsVoice, qsCrypt, qsAudio; - - if (NetworkConfig::TcpModeEnabled()) { - qsVoice = tr("Voice channel is sent over control channel"); - } else { - qsVoice = tr("

Voice channel

Encrypted with 128 bit OCB-AES128
%1 ms average latency (%4 " - "deviation)

") - .arg(boost::accumulators::mean(Global::get().sh->accUDP), 0, 'f', 2) - .arg(sqrt(boost::accumulators::variance(Global::get().sh->accUDP)), 0, 'f', 2); - qsCrypt = QString::fromLatin1("

%1

" - "" - "" - "" - "" - "
%2%3
%4%8%12
%5%9%13
%6%10%14
%7%11%15
") - .arg(tr("UDP Statistics")) - .arg(tr("To Server")) - .arg(tr("From Server")) - .arg(tr("Good")) - .arg(tr("Late")) - .arg(tr("Lost")) - .arg(tr("Resync")) - .arg(c->csCrypt->uiRemoteGood) - .arg(c->csCrypt->uiRemoteLate) - .arg(c->csCrypt->uiRemoteLost) - .arg(c->csCrypt->uiRemoteResync) - .arg(c->csCrypt->uiGood) - .arg(c->csCrypt->uiLate) - .arg(c->csCrypt->uiLost) - .arg(c->csCrypt->uiResync); - } - qsAudio = tr("

Audio bandwidth

Maximum %1 kbit/s
Current %2 kbit/s
Codec: %3

") - .arg(Global::get().iMaxBandwidth / 1000.0, 0, 'f', 1) - .arg(Global::get().iAudioBandwidth / 1000.0, 0, 'f', 1) - .arg(currentCodec()); - - QMessageBox qmb(QMessageBox::Information, tr("Mumble Server Information"), - qsVersion + qsControl + qsVoice + qsCrypt + qsAudio, QMessageBox::Ok, this); - qmb.setDefaultButton(QMessageBox::Ok); - qmb.setEscapeButton(QMessageBox::Ok); - - QPushButton *qp = qmb.addButton(tr("&View Certificate"), QMessageBox::ActionRole); - int res = qmb.exec(); - if ((res == 0) && (qmb.clickedButton() == qp)) { - ViewCert vc(Global::get().sh->qscCert, this); - vc.exec(); - } + ServerInformation *infoDialog = new ServerInformation(this); + infoDialog->show(); } void MainWindow::on_qaServerTexture_triggered() { diff --git a/src/mumble/ServerInformation.cpp b/src/mumble/ServerInformation.cpp new file mode 100644 index 000000000..dd8e41278 --- /dev/null +++ b/src/mumble/ServerInformation.cpp @@ -0,0 +1,199 @@ +// Copyright 2021 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "ServerInformation.h" +#include "CELTCodec.h" +#include "Connection.h" +#include "MainWindow.h" +#include "NetworkConfig.h" +#include "SSL.h" +#include "SSLCipherInfo.h" +#include "ServerHandler.h" +#include "UserModel.h" +#include "Version.h" +#include "ViewCert.h" +#include "Global.h" + +#include + +#include + +#include + +QString ServerInformation::m_unknownStr = tr("Unknown"); + +ServerInformation::ServerInformation(QWidget *parent) : QDialog(parent) { + setupUi(this); + + updateFields(); +} + +void ServerInformation::updateFields() { + updateServerInformation(); + updateAudioBandwidth(); + updateConnectionDetails(); +} + +void ServerInformation::on_okButton_clicked() { + accept(); +} + +void ServerInformation::on_viewCertButton_clicked() { + hide(); + + ViewCert certViewer(Global::get().sh->qscCert, this); + certViewer.exec(); + + accept(); +} + +void ServerInformation::updateServerInformation() { + QString host, userName, password; + unsigned short port; + + Global::get().sh->getConnectionInfo(host, port, userName, password); + + const int userCount = ModelItem::c_qhUsers.count(); + const unsigned int maxUserCount = Global::get().uiMaxUsers; + + QString release = Global::get().sh->qsRelease; + if (release.isEmpty()) { + release = m_unknownStr; + } + QString os = Global::get().sh->qsOS; + if (os.isEmpty()) { + os = m_unknownStr; + } else if (!Global::get().sh->qsOSVersion.isEmpty()) { + os += QString::fromLatin1(" (%1)").arg(Global::get().sh->qsOSVersion); + } + + serverInfo_host->setText(host); + serverInfo_port->setText(QString::number(port)); + serverInfo_users->setText(QString::fromLatin1("%1 / %2").arg(userCount).arg(maxUserCount)); + serverInfo_protocol->setText(MumbleVersion::toString(Global::get().sh->uiVersion)); + serverInfo_release->setText(release); + serverInfo_os->setText(os); +} + +static const QString currentCodec() { + if (Global::get().bOpus) + return QLatin1String("Opus"); + + int v = Global::get().bPreferAlpha ? Global::get().iCodecAlpha : Global::get().iCodecBeta; + CELTCodec *cc = Global::get().qmCodecs.value(v); + if (cc) + return QString::fromLatin1("CELT %1").arg(cc->version()); + else + return QString::fromLatin1("CELT %1").arg(QString::number(v, 16)); +} + +void ServerInformation::updateAudioBandwidth() { + // The bandwidths are in bit/s, so we divide by 1000 to get kBit/s + const float maxBandwidthAllowed = Global::get().iMaxBandwidth / 1000.0f; + const float currentBandwidth = Global::get().iAudioBandwidth / 1000.0f; + + audio_current->setText(QString::fromLatin1("%1 kBit/s").arg(currentBandwidth, 0, 'f', 1)); + audio_allowed->setText(QString::fromLatin1("%1 kBit/s").arg(maxBandwidthAllowed, 0, 'f', 1)); + audio_codec->setText(currentCodec()); +} + +QString getCipherID(const QSslCipher &cipher, const SSLCipherInfo *cipherInfo) { + if (cipherInfo && cipherInfo->rfc_name) { + return QString::fromUtf8(cipherInfo->rfc_name); + } + + return cipher.name(); +} + +void ServerInformation::updateConnectionDetails() { + QString latencyString = QString::fromUtf8("%1 ms (σ = %2 ms)"); + const ConnectionPtr connection = Global::get().sh->cConnection; + + if (!connection) { + return; + } + + // UDP + if (NetworkConfig::TcpModeEnabled()) { + connection_udp_infoMessage->show(); + + connection_udp_encryption->hide(); + connection_udp_encryptionLabel->hide(); + connection_udp_latency->hide(); + connection_udp_latencyLabel->hide(); + connection_udp_statisticsGroup->hide(); + } else { + connection_udp_infoMessage->hide(); + + connection_udp_encryption->show(); + connection_udp_encryptionLabel->show(); + connection_udp_latency->show(); + connection_udp_latencyLabel->show(); + connection_udp_statisticsGroup->show(); + + // Actually fill in data + const float latency = boost::accumulators::mean(Global::get().sh->accUDP); + const float deviation = std::sqrt(boost::accumulators::variance(Global::get().sh->accUDP)); + + connection_udp_encryption->setText("128 bit OCB-AES128"); + connection_udp_latency->setText(latencyString.arg(latency, 0, 'f', 1).arg(deviation, 0, 'f', 1)); + + populateUDPStatistics(*connection); + } + + + // TCP + const float latency = boost::accumulators::mean(Global::get().sh->accTCP); + const float deviation = std::sqrt(boost::accumulators::variance(Global::get().sh->accTCP)); + + QSslCipher cipher = Global::get().sh->qscCipher; + const SSLCipherInfo *cipherInfo = SSLCipherInfoLookupByOpenSSLName(cipher.name().toLatin1().constData()); + + const QString cipherID = getCipherID(cipher, cipherInfo); + + connection_tcp_tls->setText(MumbleSSL::protocolToString(connection->sessionProtocol()).toHtmlEscaped()); + connection_tcp_latency->setText(latencyString.arg(latency, 0, 'f', 1).arg(deviation, 0, 'f', 1)); + connection_tcp_cipher->setText(cipherID.isEmpty() ? m_unknownStr : cipherID); + + if (cipherInfo) { + if (cipherInfo->forward_secret) { + connection_tcp_forwardSecrecy->setText(tr("The connection provides perfect forward secrecy.")); + } else { + connection_tcp_forwardSecrecy->setText(tr("The connection does NOT provide perfect forward secrecy.")); + } + } else { + connection_tcp_forwardSecrecy->setText(tr("No information about forward secrecy available.")); + } +} + +void ServerInformation::populateUDPStatistics(const Connection &connection) { + // statistics + constexpr int toServerCol = 0; + constexpr int fromServerCol = 1; + constexpr int goodRow = 0; + constexpr int lateRow = 1; + constexpr int lostRow = 2; + constexpr int resyncRow = 3; + + QTableWidgetItem *toGoodItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiRemoteGood)); + QTableWidgetItem *fromGoodItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiGood)); + QTableWidgetItem *toLateItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiRemoteLate)); + QTableWidgetItem *fromLateItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiLate)); + QTableWidgetItem *toLostItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiRemoteLost)); + QTableWidgetItem *fromLostItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiLost)); + QTableWidgetItem *toResyncItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiRemoteResync)); + QTableWidgetItem *fromResyncItem = new QTableWidgetItem(QString::number(connection.csCrypt->uiResync)); + + connection_udp_statisticsTable->setItem(goodRow, toServerCol, toGoodItem); + connection_udp_statisticsTable->setItem(goodRow, fromServerCol, fromGoodItem); + connection_udp_statisticsTable->setItem(lateRow, toServerCol, toLateItem); + connection_udp_statisticsTable->setItem(lateRow, fromServerCol, fromLateItem); + connection_udp_statisticsTable->setItem(lostRow, toServerCol, toLostItem); + connection_udp_statisticsTable->setItem(lostRow, fromServerCol, fromLostItem); + connection_udp_statisticsTable->setItem(resyncRow, toServerCol, toResyncItem); + connection_udp_statisticsTable->setItem(resyncRow, fromServerCol, fromResyncItem); + + connection_udp_statisticsTable->adjustSize(); +} diff --git a/src/mumble/ServerInformation.h b/src/mumble/ServerInformation.h new file mode 100644 index 000000000..41669b5d4 --- /dev/null +++ b/src/mumble/ServerInformation.h @@ -0,0 +1,37 @@ +// Copyright 2021 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_SERVERINFORMATION_H_ +#define MUMBLE_MUMBLE_SERVERINFORMATION_H_ + +#include + +#include "ui_ServerInformation.h" + +class Connection; + +class ServerInformation : public QDialog, private Ui::ServerInformation { + Q_OBJECT; + Q_DISABLE_COPY(ServerInformation); + +public: + ServerInformation(QWidget *parent = nullptr); + + void updateFields(); + +public slots: + void on_okButton_clicked(); + void on_viewCertButton_clicked(); + +private: + static QString m_unknownStr; + + void updateServerInformation(); + void updateAudioBandwidth(); + void updateConnectionDetails(); + void populateUDPStatistics(const Connection &connection); +}; + +#endif // MUMBLE_MUMBLE_SERVERINFORMATION_H_ diff --git a/src/mumble/ServerInformation.ui b/src/mumble/ServerInformation.ui new file mode 100644 index 000000000..ba274deda --- /dev/null +++ b/src/mumble/ServerInformation.ui @@ -0,0 +1,643 @@ + + + ServerInformation + + + + 0 + 0 + 578 + 585 + + + + Server information + + + + + + true + + + + + 0 + -203 + 541 + 731 + + + + + + + + 0 + 0 + + + + Server Information + + + + + + <b>Host:</b> + + + + + + + + 0 + 0 + + + + <host> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Port:</span></p></body></html> + + + + + + + + 0 + 0 + + + + <port> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Users</b>: + + + + + + + + 0 + 0 + + + + <users> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Protocol:</span></p></body></html> + + + + + + + + 0 + 0 + + + + <protocol> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Release:</span></p></body></html> + + + + + + + + 0 + 0 + + + + <release> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <html><head/><body><p><span style=" font-weight:600;">OS:</span></p></body></html> + + + + + + + + 0 + 0 + + + + <os> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + 0 + 0 + + + + Audio + + + + + + <html><head/><body><p><span style=" font-weight:600;">Allowed:</span></p></body></html> + + + + + + + + 0 + 0 + + + + <allowed> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Current:</b> + + + + + + + + 0 + 0 + + + + <current> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Codec:</b> + + + + + + + + 0 + 0 + + + + <codec> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + 0 + 0 + + + + Connection details + + + + + + QTabWidget::North + + + QTabWidget::Rounded + + + 0 + + + + UDP (Voice) + + + + + + <b>Encryption:</b> + + + + + + + + 0 + 0 + + + + <encryption> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Avg. latency:</b> + + + + + + + + 0 + 0 + + + + <latency> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + Statistics + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + + Good + + + + + Late + + + + + Lost + + + + + Resync + + + + + Outgoing + + + + + Incoming + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + Voice packets is currently sent over TCP. + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + + TCP (Control) + + + + + + <b>TLS version:</b> + + + + + + + + 0 + 0 + + + + <TLS> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Cipher suite:</b> + + + + + + + + 0 + 0 + + + + <Cipher> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Avg. latency:</b> + + + + + + + + 0 + 0 + + + + <latency> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <forward secrecy> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 5 + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &View certificate + + + + + + + &Ok + + + + + + + + + + -- cgit v1.2.3