diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index b7bed67d4..742b9679e 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -467,6 +467,9 @@ namespace BitTorrent virtual void topTorrentsQueuePos(const QList &ids) = 0; virtual void bottomTorrentsQueuePos(const QList &ids) = 0; + virtual QString lastExternalIPv4Address() const = 0; + virtual QString lastExternalIPv6Address() const = 0; + signals: void startupProgressUpdated(int progress); void addTorrentFailed(const InfoHash &infoHash, const QString &reason); diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 5e52b3aa1..6cf2050c2 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -4945,6 +4945,16 @@ void SessionImpl::setTrackerFilteringEnabled(const bool enabled) } } +QString SessionImpl::lastExternalIPv4Address() const +{ + return m_lastExternalIPv4Address; +} + +QString SessionImpl::lastExternalIPv6Address() const +{ + return m_lastExternalIPv6Address; +} + bool SessionImpl::isListening() const { return m_nativeSessionExtension->isSessionListening(); @@ -5932,11 +5942,19 @@ void SessionImpl::handleExternalIPAlert(const lt::external_ip_alert *alert) LogMsg(tr("Detected external IP. IP: \"%1\"") .arg(externalIP), Log::INFO); - if (m_lastExternalIP != externalIP) + const bool isIPv6 = alert->external_address.is_v6(); + const bool isIPv4 = alert->external_address.is_v4(); + if (isIPv6 && (externalIP != m_lastExternalIPv6Address)) { - if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIP.isEmpty()) + if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIPv6Address.isEmpty()) reannounceToAllTrackers(); - m_lastExternalIP = externalIP; + m_lastExternalIPv6Address = externalIP; + } + else if (isIPv4 && (externalIP != m_lastExternalIPv4Address)) + { + if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIPv4Address.isEmpty()) + reannounceToAllTrackers(); + m_lastExternalIPv4Address = externalIP; } } diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index b1fe4efe0..32e72ffcc 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -444,6 +444,9 @@ namespace BitTorrent void topTorrentsQueuePos(const QList &ids) override; void bottomTorrentsQueuePos(const QList &ids) override; + QString lastExternalIPv4Address() const override; + QString lastExternalIPv6Address() const override; + // Torrent interface void handleTorrentResumeDataRequested(const TorrentImpl *torrent); void handleTorrentShareLimitChanged(TorrentImpl *torrent); @@ -807,7 +810,8 @@ namespace BitTorrent QList m_moveStorageQueue; - QString m_lastExternalIP; + QString m_lastExternalIPv4Address; + QString m_lastExternalIPv6Address; bool m_needUpgradeDownloadPath = false; diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index ec2fe2493..2b6caa5dc 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -359,6 +359,19 @@ void Preferences::setStatusbarDisplayed(const bool displayed) setValue(u"Preferences/General/StatusbarDisplayed"_s, displayed); } +bool Preferences::isStatusbarExternalIPDisplayed() const +{ + return value(u"Preferences/General/StatusbarExternalIPDisplayed"_s, true); +} + +void Preferences::setStatusbarExternalIPDisplayed(const bool displayed) +{ + if (displayed == isStatusbarExternalIPDisplayed()) + return; + + setValue(u"Preferences/General/StatusbarExternalIPDisplayed"_s, displayed); +} + bool Preferences::isSplashScreenDisabled() const { return value(u"Preferences/General/NoSplashScreen"_s, true); diff --git a/src/base/preferences.h b/src/base/preferences.h index a0ee4d0af..fcf36c483 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -119,6 +119,8 @@ public: void setHideZeroComboValues(int n); bool isStatusbarDisplayed() const; void setStatusbarDisplayed(bool displayed); + bool isStatusbarExternalIPDisplayed() const; + void setStatusbarExternalIPDisplayed(bool displayed); bool isToolbarDisplayed() const; void setToolbarDisplayed(bool displayed); bool isSplashScreenDisabled() const; diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index b80d0cc70..75029639f 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1443,6 +1443,7 @@ void MainWindow::loadPreferences() } showStatusBar(pref->isStatusbarDisplayed()); + m_statusBar->showStatusBarExternalIP(pref->isStatusbarExternalIPDisplayed()); m_transferListWidget->setAlternatingRowColors(pref->useAlternatingRowColors()); m_propertiesWidget->getFilesList()->setAlternatingRowColors(pref->useAlternatingRowColors()); diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 264018117..c0f10967a 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -344,6 +344,7 @@ void OptionsDialog::loadBehaviorTabOptions() m_ui->checkFileLog->setChecked(app()->isFileLoggerEnabled()); m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled()); + m_ui->checkBoxExternalIPStatusBar->setChecked(pref->isStatusbarExternalIPDisplayed()); connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); @@ -423,6 +424,7 @@ void OptionsDialog::loadBehaviorTabOptions() connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkBoxExternalIPStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); } void OptionsDialog::saveBehaviorTabOptions() const @@ -507,6 +509,7 @@ void OptionsDialog::saveBehaviorTabOptions() const app()->setStartUpWindowState(m_ui->windowStateComboBox->currentData().value()); session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked()); + pref->setStatusbarExternalIPDisplayed(m_ui->checkBoxExternalIPStatusBar->isChecked()); } void OptionsDialog::loadDownloadsTabOptions() diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 82d36aa70..216394d19 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -772,6 +772,13 @@ + + + + Show external IP in status bar + + + diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 8eef15e7b..d07de204f 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -86,6 +86,9 @@ StatusBar::StatusBar(QWidget *parent) m_upSpeedLbl->setStyleSheet(u"text-align:left;"_s); m_upSpeedLbl->setMinimumWidth(200); + m_lastExternalIPsLbl = new QLabel(tr("External IP: N/A")); + m_lastExternalIPsLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + m_DHTLbl = new QLabel(tr("DHT: %1 nodes").arg(0), this); m_DHTLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); @@ -129,14 +132,21 @@ StatusBar::StatusBar(QWidget *parent) #ifndef Q_OS_MACOS statusSep4->setFrameShadow(QFrame::Raised); #endif - layout->addWidget(m_DHTLbl); + QFrame *statusSep5 = new QFrame(this); + statusSep5->setFrameStyle(QFrame::VLine); +#ifndef Q_OS_MACOS + statusSep5->setFrameShadow(QFrame::Raised); +#endif + layout->addWidget(m_lastExternalIPsLbl); layout->addWidget(statusSep1); - layout->addWidget(m_connecStatusLblIcon); + layout->addWidget(m_DHTLbl); layout->addWidget(statusSep2); + layout->addWidget(m_connecStatusLblIcon); + layout->addWidget(statusSep3); layout->addWidget(m_altSpeedsBtn); layout->addWidget(statusSep4); layout->addWidget(m_dlSpeedLbl); - layout->addWidget(statusSep3); + layout->addWidget(statusSep5); layout->addWidget(m_upSpeedLbl); addPermanentWidget(container); @@ -170,6 +180,11 @@ void StatusBar::showRestartRequired() insertWidget(1, restartLbl); } +void StatusBar::showStatusBarExternalIP(const bool display) +{ + m_lastExternalIPsLbl->setVisible(display); +} + void StatusBar::updateConnectionStatus() { const BitTorrent::SessionStatus &sessionStatus = BitTorrent::Session::instance()->status(); @@ -212,6 +227,23 @@ void StatusBar::updateDHTNodesNumber() } } +void StatusBar::updateExternalAddressesLabel() +{ + const QString lastExternalIPv4Address = BitTorrent::Session::instance()->lastExternalIPv4Address(); + const QString lastExternalIPv6Address = BitTorrent::Session::instance()->lastExternalIPv6Address(); + QString addressText = tr("External IP: N/A"); + + const bool hasIPv4Address = !lastExternalIPv4Address.isEmpty(); + const bool hasIPv6Address = !lastExternalIPv6Address.isEmpty(); + + if (hasIPv4Address && hasIPv6Address) + addressText = tr("External IPs: %1, %2").arg(lastExternalIPv4Address, lastExternalIPv6Address); + else if (hasIPv4Address || hasIPv6Address) + addressText = tr("External IP: %1%2").arg(lastExternalIPv4Address, lastExternalIPv6Address); + + m_lastExternalIPsLbl->setText(addressText); +} + void StatusBar::updateSpeedLabels() { const BitTorrent::SessionStatus &sessionStatus = BitTorrent::Session::instance()->status(); @@ -235,6 +267,7 @@ void StatusBar::refresh() { updateConnectionStatus(); updateDHTNodesNumber(); + updateExternalAddressesLabel(); updateSpeedLabels(); } diff --git a/src/gui/statusbar.h b/src/gui/statusbar.h index be5f993d0..36d74f948 100644 --- a/src/gui/statusbar.h +++ b/src/gui/statusbar.h @@ -47,6 +47,8 @@ public: StatusBar(QWidget *parent = nullptr); ~StatusBar() override; + void showStatusBarExternalIP(bool display); + signals: void alternativeSpeedsButtonClicked(); void connectionButtonClicked(); @@ -62,10 +64,12 @@ private slots: private: void updateConnectionStatus(); void updateDHTNodesNumber(); + void updateExternalAddressesLabel(); void updateSpeedLabels(); QPushButton *m_dlSpeedLbl = nullptr; QPushButton *m_upSpeedLbl = nullptr; + QLabel *m_lastExternalIPsLbl = nullptr; QLabel *m_DHTLbl = nullptr; QPushButton *m_connecStatusLblIcon = nullptr; QPushButton *m_altSpeedsBtn = nullptr; diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index fd6cd442c..dd90e4e13 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -127,6 +127,7 @@ void AppController::preferencesAction() // Language data[u"locale"_s] = pref->getLocale(); data[u"performance_warning"_s] = session->isPerformanceWarningEnabled(); + data[u"status_bar_external_ip"_s] = pref->isStatusbarExternalIPDisplayed(); // Transfer List data[u"confirm_torrent_deletion"_s] = pref->confirmTorrentDeletion(); // Log file @@ -510,6 +511,8 @@ void AppController::setPreferencesAction() } if (hasKey(u"performance_warning"_s)) session->setPerformanceWarningEnabled(it.value().toBool()); + if (hasKey(u"status_bar_external_ip"_s)) + pref->setStatusbarExternalIPDisplayed(it.value().toBool()); // Transfer List if (hasKey(u"confirm_torrent_deletion"_s)) pref->setConfirmTorrentDeletion(it.value().toBool()); diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 1ad55dbae..d11500741 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -87,6 +87,8 @@ namespace const QString KEY_TRANSFER_DLRATELIMIT = u"dl_rate_limit"_s; const QString KEY_TRANSFER_DLSPEED = u"dl_info_speed"_s; const QString KEY_TRANSFER_FREESPACEONDISK = u"free_space_on_disk"_s; + const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4 = u"last_external_address_v4"_s; + const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6 = u"last_external_address_v6"_s; const QString KEY_TRANSFER_UPDATA = u"up_info_data"_s; const QString KEY_TRANSFER_UPRATELIMIT = u"up_rate_limit"_s; const QString KEY_TRANSFER_UPSPEED = u"up_info_speed"_s; @@ -161,6 +163,8 @@ namespace map[KEY_TRANSFER_AVERAGE_TIME_QUEUE] = cacheStatus.averageJobTime; map[KEY_TRANSFER_TOTAL_QUEUED_SIZE] = cacheStatus.queuedBytes; + map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4] = session->lastExternalIPv4Address(); + map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6] = session->lastExternalIPv6Address(); map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dhtNodes; map[KEY_TRANSFER_CONNECTION_STATUS] = session->isListening() ? (sessionStatus.hasIncomingConnections ? u"connected"_s : u"firewalled"_s) @@ -450,6 +454,8 @@ void SyncController::updateFreeDiskSpace(const qint64 freeDiskSpace) // - "dl_info_data": bytes downloaded // - "dl_info_speed": download speed // - "dl_rate_limit: download rate limit +// - "last_external_address_v4": last external address v4 +// - "last_external_address_v6": last external address v6 // - "up_info_data: bytes uploaded // - "up_info_speed: upload speed // - "up_rate_limit: upload speed limit diff --git a/src/webui/api/transfercontroller.cpp b/src/webui/api/transfercontroller.cpp index 6bb9abea2..f1ed455e4 100644 --- a/src/webui/api/transfercontroller.cpp +++ b/src/webui/api/transfercontroller.cpp @@ -45,6 +45,8 @@ const QString KEY_TRANSFER_DLRATELIMIT = u"dl_rate_limit"_s; const QString KEY_TRANSFER_UPSPEED = u"up_info_speed"_s; const QString KEY_TRANSFER_UPDATA = u"up_info_data"_s; const QString KEY_TRANSFER_UPRATELIMIT = u"up_rate_limit"_s; +const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4 = u"last_external_address_v4"_s; +const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6 = u"last_external_address_v6"_s; const QString KEY_TRANSFER_DHT_NODES = u"dht_nodes"_s; const QString KEY_TRANSFER_CONNECTION_STATUS = u"connection_status"_s; @@ -57,6 +59,8 @@ const QString KEY_TRANSFER_CONNECTION_STATUS = u"connection_status"_s; // - "up_info_data": Data uploaded this session // - "dl_rate_limit": Download rate limit // - "up_rate_limit": Upload rate limit +// - "last_external_address_v4": external IPv4 address +// - "last_external_address_v6": external IPv6 address // - "dht_nodes": DHT nodes connected to // - "connection_status": Connection status void TransferController::infoAction() @@ -71,6 +75,8 @@ void TransferController::infoAction() dict[KEY_TRANSFER_UPDATA] = static_cast(sessionStatus.totalPayloadUpload); dict[KEY_TRANSFER_DLRATELIMIT] = BitTorrent::Session::instance()->downloadSpeedLimit(); dict[KEY_TRANSFER_UPRATELIMIT] = BitTorrent::Session::instance()->uploadSpeedLimit(); + dict[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4] = BitTorrent::Session::instance()->lastExternalIPv4Address(); + dict[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6] = BitTorrent::Session::instance()->lastExternalIPv6Address(); dict[KEY_TRANSFER_DHT_NODES] = static_cast(sessionStatus.dhtNodes); if (!BitTorrent::Session::instance()->isListening()) dict[KEY_TRANSFER_CONNECTION_STATUS] = u"disconnected"_s; diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index f8508db75..cccc0e140 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -266,6 +266,8 @@ + + diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 4357ded37..35cde92b1 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -928,6 +928,25 @@ window.addEventListener("DOMContentLoaded", () => { $("freeSpaceOnDisk").textContent = "QBT_TR(Free space: %1)QBT_TR[CONTEXT=HttpServer]".replace("%1", window.qBittorrent.Misc.friendlyUnit(serverState.free_space_on_disk)); + if (window.qBittorrent.Cache.preferences.get().status_bar_external_ip) { + const lastExternalAddressV4 = serverState.last_external_address_v4; + const lastExternalAddressV6 = serverState.last_external_address_v6; + const hasIPv4Address = lastExternalAddressV4 !== ""; + const hasIPv6Address = lastExternalAddressV6 !== ""; + let lastExternalAddressLabel = "QBT_TR(External IP: N/A)QBT_TR[CONTEXT=HttpServer]"; + if (hasIPv4Address && hasIPv6Address) + lastExternalAddressLabel = "QBT_TR(External IPs: %1, %2)QBT_TR[CONTEXT=HttpServer]"; + else if (hasIPv4Address || hasIPv6Address) + lastExternalAddressLabel = "QBT_TR(External IP: %1%2)QBT_TR[CONTEXT=HttpServer]"; + // replace in reverse order ('%2' before '%1') in case address contains a % character. + // for example, see https://en.wikipedia.org/wiki/IPv6_address#Scoped_literal_IPv6_addresses_(with_zone_index) + $("externalIPs").textContent = lastExternalAddressLabel.replace("%2", lastExternalAddressV6).replace("%1", lastExternalAddressV4); + + const elem = document.getElementById("externalIPs"); + elem.classList.remove("invisible"); + elem.previousElementSibling.classList.remove("invisible"); + } + const dhtElement = document.getElementById("DHTNodes"); if (window.qBittorrent.Cache.preferences.get().dht) { dhtElement.textContent = "QBT_TR(DHT: %1 nodes)QBT_TR[CONTEXT=StatusBar]".replace("%1", serverState.dht_nodes); diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 31febeb83..cbf35908d 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -80,6 +80,11 @@ +
+ + +
+
@@ -2411,6 +2416,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Language updateWebuiLocaleSelect(pref.locale); $("performanceWarning").checked = pref.performance_warning; + $("statusBarExternalIP").checked = pref.status_bar_external_ip; // HTTP Server $("webui_domain_textarea").value = pref.web_ui_domain_list; @@ -2836,6 +2842,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Language settings["locale"] = $("locale_select").value; settings["performance_warning"] = $("performanceWarning").checked; + settings["status_bar_external_ip"] = $("statusBarExternalIP").checked; // HTTP Server settings["web_ui_domain_list"] = $("webui_domain_textarea").value;