mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-13 00:33:09 -07:00
Merge pull request #10175 from Chocobo1/cert
Load WebUI certificate & key from file path
This commit is contained in:
commit
40eb8a1f4a
17 changed files with 304 additions and 295 deletions
|
@ -7,6 +7,7 @@ application.cpp
|
||||||
cmdoptions.cpp
|
cmdoptions.cpp
|
||||||
filelogger.cpp
|
filelogger.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
upgrade.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(qBittorrent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(qBittorrent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
|
@ -17,13 +17,15 @@ usesystemqtsingleapplication {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/application.h \
|
$$PWD/application.h \
|
||||||
$$PWD/cmdoptions.h \
|
$$PWD/cmdoptions.h \
|
||||||
$$PWD/filelogger.h
|
$$PWD/filelogger.h \
|
||||||
|
$$PWD/upgrade.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/application.cpp \
|
$$PWD/application.cpp \
|
||||||
$$PWD/cmdoptions.cpp \
|
$$PWD/cmdoptions.cpp \
|
||||||
$$PWD/filelogger.cpp \
|
$$PWD/filelogger.cpp \
|
||||||
$$PWD/main.cpp
|
$$PWD/main.cpp \
|
||||||
|
$$PWD/upgrade.cpp
|
||||||
|
|
||||||
stacktrace {
|
stacktrace {
|
||||||
unix {
|
unix {
|
||||||
|
@ -37,6 +39,3 @@ stacktrace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# upgrade code
|
|
||||||
HEADERS += $$PWD/upgrade.h
|
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include <QPen>
|
#include <QPen>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSplashScreen>
|
#include <QSplashScreen>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#ifdef QBT_STATIC_QT
|
#ifdef QBT_STATIC_QT
|
||||||
#include <QtPlugin>
|
#include <QtPlugin>
|
||||||
|
|
84
src/app/upgrade.cpp
Normal file
84
src/app/upgrade.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2019 Mike Tzou (Chocobo1)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "upgrade.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "base/logger.h"
|
||||||
|
#include "base/profile.h"
|
||||||
|
#include "base/settingsstorage.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void exportWebUIHttpsFiles()
|
||||||
|
{
|
||||||
|
const auto migrate = [](const QString &oldKey, const QString &newKey, const QString &savePath) {
|
||||||
|
SettingsStorage *settingsStorage {SettingsStorage::instance()};
|
||||||
|
const QByteArray oldData {settingsStorage->loadValue(oldKey).toByteArray()};
|
||||||
|
const QString newData {settingsStorage->loadValue(newKey).toString()};
|
||||||
|
const QString errorMsgFormat {QObject::tr("Migrate preferences failed: WebUI https, file: \"%1\", error: \"%2\"")};
|
||||||
|
|
||||||
|
if (!newData.isEmpty() || oldData.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(savePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
LogMsg(errorMsgFormat.arg(savePath, file.errorString()) , Log::WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.write(oldData) != oldData.size()) {
|
||||||
|
file.close();
|
||||||
|
Utils::Fs::forceRemove(savePath);
|
||||||
|
LogMsg(errorMsgFormat.arg(savePath, QLatin1String("Write incomplete.")) , Log::WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsStorage->storeValue(newKey, savePath);
|
||||||
|
settingsStorage->removeValue(oldKey);
|
||||||
|
|
||||||
|
LogMsg(QObject::tr("Migrated preferences: WebUI https, exported data to file: \"%1\"").arg(savePath)
|
||||||
|
, Log::INFO);
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString configPath {specialFolderLocation(SpecialFolder::Config)};
|
||||||
|
migrate(QLatin1String("Preferences/WebUI/HTTPS/Certificate")
|
||||||
|
, QLatin1String("Preferences/WebUI/HTTPS/CertificatePath")
|
||||||
|
, Utils::Fs::toNativePath(configPath + QLatin1String("WebUICertificate.crt")));
|
||||||
|
migrate(QLatin1String("Preferences/WebUI/HTTPS/Key")
|
||||||
|
, QLatin1String("Preferences/WebUI/HTTPS/KeyPath")
|
||||||
|
, Utils::Fs::toNativePath(configPath + QLatin1String("WebUIPrivateKey.pem")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool upgrade(const bool /*ask*/)
|
||||||
|
{
|
||||||
|
exportWebUIHttpsFiles();
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -28,9 +28,4 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
bool upgrade(bool /*ask*/ = true)
|
bool upgrade(bool ask = true);
|
||||||
{
|
|
||||||
// Intentionally left no-op as a placeholder for future use
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,119 +30,26 @@
|
||||||
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QMutableListIterator>
|
#include <QMutableListIterator>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
|
#include <QSslCipher>
|
||||||
|
#include <QSslSocket>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
#include <QSslSocket>
|
|
||||||
#else
|
|
||||||
#include <QTcpSocket>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#include "base/utils/net.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
|
||||||
static const int KEEP_ALIVE_DURATION = 7 * 1000; // milliseconds
|
namespace
|
||||||
static const int CONNECTIONS_LIMIT = 500;
|
|
||||||
static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
|
|
||||||
|
|
||||||
using namespace Http;
|
|
||||||
|
|
||||||
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
|
||||||
: QTcpServer(parent)
|
|
||||||
, m_requestHandler(requestHandler)
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
, m_https(false)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
setProxy(QNetworkProxy::NoProxy);
|
const int KEEP_ALIVE_DURATION = 7 * 1000; // milliseconds
|
||||||
#ifndef QT_NO_OPENSSL
|
const int CONNECTIONS_LIMIT = 500;
|
||||||
QSslSocket::setDefaultCiphers(safeCipherList());
|
const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
|
||||||
#endif
|
|
||||||
|
|
||||||
QTimer *dropConnectionTimer = new QTimer(this);
|
QList<QSslCipher> safeCipherList()
|
||||||
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
|
{
|
||||||
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
Server::~Server()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::incomingConnection(qintptr socketDescriptor)
|
|
||||||
{
|
|
||||||
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
|
|
||||||
|
|
||||||
QTcpSocket *serverSocket;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
if (m_https)
|
|
||||||
serverSocket = new QSslSocket(this);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
serverSocket = new QTcpSocket(this);
|
|
||||||
|
|
||||||
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
|
|
||||||
delete serverSocket;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
if (m_https) {
|
|
||||||
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
|
|
||||||
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
|
|
||||||
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
|
|
||||||
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
|
|
||||||
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Connection *c = new Connection(serverSocket, m_requestHandler, this);
|
|
||||||
m_connections.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::dropTimedOutConnection()
|
|
||||||
{
|
|
||||||
QMutableListIterator<Connection *> i(m_connections);
|
|
||||||
while (i.hasNext()) {
|
|
||||||
auto connection = i.next();
|
|
||||||
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
|
|
||||||
delete connection;
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &key)
|
|
||||||
{
|
|
||||||
QSslKey sslKey(key, QSsl::Rsa);
|
|
||||||
if (sslKey.isNull())
|
|
||||||
sslKey = QSslKey(key, QSsl::Ec);
|
|
||||||
|
|
||||||
const QList<QSslCertificate> certs = QSslCertificate::fromData(certificates);
|
|
||||||
const bool areCertsValid = !certs.empty() && std::all_of(certs.begin(), certs.end(), [](const QSslCertificate &c) { return !c.isNull(); });
|
|
||||||
|
|
||||||
if (!sslKey.isNull() && areCertsValid) {
|
|
||||||
m_key = sslKey;
|
|
||||||
m_certificates = certs;
|
|
||||||
m_https = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
disableHttps();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::disableHttps()
|
|
||||||
{
|
|
||||||
m_https = false;
|
|
||||||
m_certificates.clear();
|
|
||||||
m_key.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QSslCipher> Server::safeCipherList() const
|
|
||||||
{
|
|
||||||
const QStringList badCiphers = {"idea", "rc4"};
|
const QStringList badCiphers = {"idea", "rc4"};
|
||||||
const QList<QSslCipher> allCiphers = QSslSocket::supportedCiphers();
|
const QList<QSslCipher> allCiphers = QSslSocket::supportedCiphers();
|
||||||
QList<QSslCipher> safeCiphers;
|
QList<QSslCipher> safeCiphers;
|
||||||
|
@ -160,5 +67,82 @@ QList<QSslCipher> Server::safeCipherList() const
|
||||||
}
|
}
|
||||||
|
|
||||||
return safeCiphers;
|
return safeCiphers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Http;
|
||||||
|
|
||||||
|
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||||
|
: QTcpServer(parent)
|
||||||
|
, m_requestHandler(requestHandler)
|
||||||
|
, m_https(false)
|
||||||
|
{
|
||||||
|
setProxy(QNetworkProxy::NoProxy);
|
||||||
|
QSslSocket::setDefaultCiphers(safeCipherList());
|
||||||
|
|
||||||
|
QTimer *dropConnectionTimer = new QTimer(this);
|
||||||
|
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
|
||||||
|
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::incomingConnection(qintptr socketDescriptor)
|
||||||
|
{
|
||||||
|
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
|
||||||
|
|
||||||
|
QTcpSocket *serverSocket;
|
||||||
|
if (m_https)
|
||||||
|
serverSocket = new QSslSocket(this);
|
||||||
|
else
|
||||||
|
serverSocket = new QTcpSocket(this);
|
||||||
|
|
||||||
|
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
|
||||||
|
delete serverSocket;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_https) {
|
||||||
|
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
|
||||||
|
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
|
||||||
|
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
|
||||||
|
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection *c = new Connection(serverSocket, m_requestHandler, this);
|
||||||
|
m_connections.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::dropTimedOutConnection()
|
||||||
|
{
|
||||||
|
QMutableListIterator<Connection *> i(m_connections);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
auto connection = i.next();
|
||||||
|
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
|
||||||
|
delete connection;
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &privateKey)
|
||||||
|
{
|
||||||
|
const QList<QSslCertificate> certs {Utils::Net::loadSSLCertificate(certificates)};
|
||||||
|
const QSslKey key {Utils::Net::loadSSLKey(privateKey)};
|
||||||
|
|
||||||
|
if (certs.isEmpty() || key.isNull()) {
|
||||||
|
disableHttps();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_key = key;
|
||||||
|
m_certificates = certs;
|
||||||
|
m_https = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::disableHttps()
|
||||||
|
{
|
||||||
|
m_https = false;
|
||||||
|
m_certificates.clear();
|
||||||
|
m_key.clear();
|
||||||
}
|
}
|
||||||
#endif // QT_NO_OPENSSL
|
|
||||||
|
|
|
@ -31,13 +31,9 @@
|
||||||
#ifndef HTTP_SERVER_H
|
#ifndef HTTP_SERVER_H
|
||||||
#define HTTP_SERVER_H
|
#define HTTP_SERVER_H
|
||||||
|
|
||||||
#include <QTcpServer>
|
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSslCipher>
|
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
#endif
|
#include <QTcpServer>
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
@ -50,13 +46,10 @@ namespace Http
|
||||||
Q_DISABLE_COPY(Server)
|
Q_DISABLE_COPY(Server)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server(IRequestHandler *requestHandler, QObject *parent = nullptr);
|
explicit Server(IRequestHandler *requestHandler, QObject *parent = nullptr);
|
||||||
~Server();
|
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
bool setupHttps(const QByteArray &certificates, const QByteArray &privateKey);
|
||||||
bool setupHttps(const QByteArray &certificates, const QByteArray &key);
|
|
||||||
void disableHttps();
|
void disableHttps();
|
||||||
#endif
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void dropTimedOutConnection();
|
void dropTimedOutConnection();
|
||||||
|
@ -67,13 +60,9 @@ namespace Http
|
||||||
IRequestHandler *m_requestHandler;
|
IRequestHandler *m_requestHandler;
|
||||||
QList<Connection *> m_connections; // for tracking persistent connections
|
QList<Connection *> m_connections; // for tracking persistent connections
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
QList<QSslCipher> safeCipherList() const;
|
|
||||||
|
|
||||||
bool m_https;
|
bool m_https;
|
||||||
QList<QSslCertificate> m_certificates;
|
QList<QSslCertificate> m_certificates;
|
||||||
QSslKey m_key;
|
QSslKey m_key;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "logger.h"
|
|
||||||
#include "settingsstorage.h"
|
#include "settingsstorage.h"
|
||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
#include "utils/misc.h"
|
#include "utils/misc.h"
|
||||||
|
@ -634,24 +633,24 @@ void Preferences::setWebUiHttpsEnabled(bool enabled)
|
||||||
setValue("Preferences/WebUI/HTTPS/Enabled", enabled);
|
setValue("Preferences/WebUI/HTTPS/Enabled", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Preferences::getWebUiHttpsCertificate() const
|
QString Preferences::getWebUIHttpsCertificatePath() const
|
||||||
{
|
{
|
||||||
return value("Preferences/WebUI/HTTPS/Certificate").toByteArray();
|
return value("Preferences/WebUI/HTTPS/CertificatePath").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setWebUiHttpsCertificate(const QByteArray &data)
|
void Preferences::setWebUIHttpsCertificatePath(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/WebUI/HTTPS/Certificate", data);
|
setValue("Preferences/WebUI/HTTPS/CertificatePath", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Preferences::getWebUiHttpsKey() const
|
QString Preferences::getWebUIHttpsKeyPath() const
|
||||||
{
|
{
|
||||||
return value("Preferences/WebUI/HTTPS/Key").toByteArray();
|
return value("Preferences/WebUI/HTTPS/KeyPath").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setWebUiHttpsKey(const QByteArray &data)
|
void Preferences::setWebUIHttpsKeyPath(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/WebUI/HTTPS/Key", data);
|
setValue("Preferences/WebUI/HTTPS/KeyPath", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isAltWebUiEnabled() const
|
bool Preferences::isAltWebUiEnabled() const
|
||||||
|
|
|
@ -31,18 +31,14 @@
|
||||||
#define PREFERENCES_H
|
#define PREFERENCES_H
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QNetworkCookie>
|
#include <QNetworkCookie>
|
||||||
#include <QReadWriteLock>
|
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QTimer>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
enum SchedulerDays
|
enum SchedulerDays
|
||||||
{
|
{
|
||||||
|
@ -207,10 +203,10 @@ public:
|
||||||
// HTTPS
|
// HTTPS
|
||||||
bool isWebUiHttpsEnabled() const;
|
bool isWebUiHttpsEnabled() const;
|
||||||
void setWebUiHttpsEnabled(bool enabled);
|
void setWebUiHttpsEnabled(bool enabled);
|
||||||
QByteArray getWebUiHttpsCertificate() const;
|
QString getWebUIHttpsCertificatePath() const;
|
||||||
void setWebUiHttpsCertificate(const QByteArray &data);
|
void setWebUIHttpsCertificatePath(const QString &path);
|
||||||
QByteArray getWebUiHttpsKey() const;
|
QString getWebUIHttpsKeyPath() const;
|
||||||
void setWebUiHttpsKey(const QByteArray &data);
|
void setWebUIHttpsKeyPath(const QString &path);
|
||||||
bool isAltWebUiEnabled() const;
|
bool isAltWebUiEnabled() const;
|
||||||
void setAltWebUiEnabled(bool enabled);
|
void setAltWebUiEnabled(bool enabled);
|
||||||
QString getWebUiRootFolder() const;
|
QString getWebUiRootFolder() const;
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include <QHostAddress>
|
#include <QSslCertificate>
|
||||||
|
#include <QSslKey>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
|
@ -89,5 +90,32 @@ namespace Utils
|
||||||
{
|
{
|
||||||
return subnet.first.toString() + '/' + QString::number(subnet.second);
|
return subnet.first.toString() + '/' + QString::number(subnet.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSslCertificate> loadSSLCertificate(const QByteArray &data)
|
||||||
|
{
|
||||||
|
const QList<QSslCertificate> certs {QSslCertificate::fromData(data)};
|
||||||
|
if (std::any_of(certs.cbegin(), certs.cend(), [](const QSslCertificate &c) { return c.isNull(); }))
|
||||||
|
return {};
|
||||||
|
return certs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSSLCertificatesValid(const QByteArray &data)
|
||||||
|
{
|
||||||
|
return !loadSSLCertificate(data).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSslKey loadSSLKey(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// try different formats
|
||||||
|
QSslKey key {data, QSsl::Rsa};
|
||||||
|
if (!key.isNull())
|
||||||
|
return key;
|
||||||
|
return QSslKey(data, QSsl::Ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSSLKeyValid(const QByteArray &data)
|
||||||
|
{
|
||||||
|
return !loadSSLKey(data).isNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,12 @@
|
||||||
#ifndef BASE_UTILS_NET_H
|
#ifndef BASE_UTILS_NET_H
|
||||||
#define BASE_UTILS_NET_H
|
#define BASE_UTILS_NET_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
|
||||||
class QHostAddress;
|
class QSslCertificate;
|
||||||
|
class QSslKey;
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
|
@ -47,6 +49,12 @@ namespace Utils
|
||||||
bool isLoopbackAddress(const QHostAddress &addr);
|
bool isLoopbackAddress(const QHostAddress &addr);
|
||||||
bool isIPInRange(const QHostAddress &addr, const QList<Subnet> &subnets);
|
bool isIPInRange(const QHostAddress &addr, const QList<Subnet> &subnets);
|
||||||
QString subnetToString(const Subnet &subnet);
|
QString subnetToString(const Subnet &subnet);
|
||||||
|
|
||||||
|
const int MAX_SSL_FILE_SIZE = 1024 * 1024;
|
||||||
|
QList<QSslCertificate> loadSSLCertificate(const QByteArray &data);
|
||||||
|
bool isSSLCertificatesValid(const QByteArray &data);
|
||||||
|
QSslKey loadSSLKey(const QByteArray &data);
|
||||||
|
bool isSSLKeyValid(const QByteArray &data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,13 +42,9 @@
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
#include <QSslCertificate>
|
|
||||||
#include <QSslKey>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/http/server.h"
|
||||||
#include "base/net/dnsupdater.h"
|
#include "base/net/dnsupdater.h"
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
#include "base/net/proxyconfigurationmanager.h"
|
#include "base/net/proxyconfigurationmanager.h"
|
||||||
|
@ -59,6 +55,7 @@
|
||||||
#include "base/torrentfileguard.h"
|
#include "base/torrentfileguard.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/utils/net.h"
|
||||||
#include "base/utils/password.h"
|
#include "base/utils/password.h"
|
||||||
#include "base/utils/random.h"
|
#include "base/utils/random.h"
|
||||||
#include "addnewtorrentdialog.h"
|
#include "addnewtorrentdialog.h"
|
||||||
|
@ -196,11 +193,6 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(QT_NO_OPENSSL)
|
|
||||||
m_ui->checkWebUiHttps->setVisible(false);
|
|
||||||
m_ui->checkSmtpSSL->setVisible(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
m_ui->checkStartup->setVisible(false);
|
m_ui->checkStartup->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
@ -390,14 +382,23 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
// Web UI tab
|
// Web UI tab
|
||||||
|
m_ui->textWebUIHttpsCert->setMode(FileSystemPathEdit::Mode::FileOpen);
|
||||||
|
m_ui->textWebUIHttpsCert->setFileNameFilter(tr("Certificate") + QLatin1String(" (*.cer *.crt *.pem)"));
|
||||||
|
m_ui->textWebUIHttpsCert->setDialogCaption(tr("Select certificate"));
|
||||||
|
m_ui->textWebUIHttpsKey->setMode(FileSystemPathEdit::Mode::FileOpen);
|
||||||
|
m_ui->textWebUIHttpsKey->setFileNameFilter(tr("Private key") + QLatin1String(" (*.key *.pem)"));
|
||||||
|
m_ui->textWebUIHttpsKey->setDialogCaption(tr("Select private key"));
|
||||||
|
|
||||||
connect(m_ui->textServerDomains, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textServerDomains, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkWebUi, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkWebUi, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->textWebUiAddress, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUiAddress, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->spinWebUiPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->spinWebUiPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkWebUIUPnP, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->btnWebUiKey, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->btnWebUiCrt, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUIHttpsCert, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsCertChanged(s, ShowError::Show); });
|
||||||
|
connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||||
|
connect(m_ui->textWebUIHttpsKey, &FileSystemPathLineEdit::selectedPathChanged, this, [this](const QString &s) { webUIHttpsKeyChanged(s, ShowError::Show); });
|
||||||
connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
|
@ -722,11 +723,8 @@ void OptionsDialog::saveOptions()
|
||||||
pref->setWebUiPort(m_ui->spinWebUiPort->value());
|
pref->setWebUiPort(m_ui->spinWebUiPort->value());
|
||||||
pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked());
|
pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked());
|
||||||
pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked());
|
pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked());
|
||||||
// HTTPS
|
pref->setWebUIHttpsCertificatePath(m_ui->textWebUIHttpsCert->selectedPath());
|
||||||
if (m_ui->checkWebUiHttps->isChecked()) {
|
pref->setWebUIHttpsKeyPath(m_ui->textWebUIHttpsKey->selectedPath());
|
||||||
pref->setWebUiHttpsCertificate(m_sslCert);
|
|
||||||
pref->setWebUiHttpsKey(m_sslKey);
|
|
||||||
}
|
|
||||||
// Authentication
|
// Authentication
|
||||||
pref->setWebUiUsername(webUiUsername());
|
pref->setWebUiUsername(webUiUsername());
|
||||||
if (!webUiPassword().isEmpty())
|
if (!webUiPassword().isEmpty())
|
||||||
|
@ -1089,8 +1087,8 @@ void OptionsDialog::loadOptions()
|
||||||
m_ui->spinWebUiPort->setValue(pref->getWebUiPort());
|
m_ui->spinWebUiPort->setValue(pref->getWebUiPort());
|
||||||
m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort());
|
m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort());
|
||||||
m_ui->checkWebUiHttps->setChecked(pref->isWebUiHttpsEnabled());
|
m_ui->checkWebUiHttps->setChecked(pref->isWebUiHttpsEnabled());
|
||||||
setSslCertificate(pref->getWebUiHttpsCertificate());
|
webUIHttpsCertChanged(pref->getWebUIHttpsCertificatePath(), ShowError::NotShow);
|
||||||
setSslKey(pref->getWebUiHttpsKey());
|
webUIHttpsKeyChanged(pref->getWebUIHttpsKeyPath(), ShowError::NotShow);
|
||||||
m_ui->textWebUiUsername->setText(pref->getWebUiUsername());
|
m_ui->textWebUiUsername->setText(pref->getWebUiUsername());
|
||||||
m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled());
|
m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled());
|
||||||
m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled());
|
m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled());
|
||||||
|
@ -1559,41 +1557,59 @@ QString OptionsDialog::webUiPassword() const
|
||||||
return m_ui->textWebUiPassword->text();
|
return m_ui->textWebUiPassword->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError showError)
|
||||||
|
{
|
||||||
|
m_ui->textWebUIHttpsCert->setSelectedPath(path);
|
||||||
|
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-low.svg", this, 24));
|
||||||
|
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
if (showError == ShowError::Show)
|
||||||
|
QMessageBox::warning(this, tr("Invalid path"), file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utils::Net::isSSLCertificatesValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) {
|
||||||
|
if (showError == ShowError::Show)
|
||||||
|
QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-high.svg", this, 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsDialog::webUIHttpsKeyChanged(const QString &path, const ShowError showError)
|
||||||
|
{
|
||||||
|
m_ui->textWebUIHttpsKey->setSelectedPath(path);
|
||||||
|
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-low.svg", this, 24));
|
||||||
|
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
if (showError == ShowError::Show)
|
||||||
|
QMessageBox::warning(this, tr("Invalid path"), file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utils::Net::isSSLKeyValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) {
|
||||||
|
if (showError == ShowError::Show)
|
||||||
|
QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-high.svg", this, 24));
|
||||||
|
}
|
||||||
|
|
||||||
void OptionsDialog::showConnectionTab()
|
void OptionsDialog::showConnectionTab()
|
||||||
{
|
{
|
||||||
m_ui->tabSelection->setCurrentRow(TAB_CONNECTION);
|
m_ui->tabSelection->setCurrentRow(TAB_CONNECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptionsDialog::on_btnWebUiCrt_clicked()
|
|
||||||
{
|
|
||||||
const QString filename = QFileDialog::getOpenFileName(this, tr("Import SSL certificate"), QString(), tr("SSL Certificate") + QLatin1String(" (*.crt *.pem)"));
|
|
||||||
if (filename.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile cert(filename);
|
|
||||||
if (!cert.open(QIODevice::ReadOnly))
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool success = setSslCertificate(cert.read(1024 * 1024));
|
|
||||||
if (!success)
|
|
||||||
QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionsDialog::on_btnWebUiKey_clicked()
|
|
||||||
{
|
|
||||||
const QString filename = QFileDialog::getOpenFileName(this, tr("Import SSL key"), QString(), tr("SSL key") + QLatin1String(" (*.key *.pem)"));
|
|
||||||
if (filename.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile key(filename);
|
|
||||||
if (!key.open(QIODevice::ReadOnly))
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool success = setSslKey(key.read(1024 * 1024));
|
|
||||||
if (!success)
|
|
||||||
QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionsDialog::on_registerDNSBtn_clicked()
|
void OptionsDialog::on_registerDNSBtn_clicked()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(m_ui->comboDNSService->currentIndex()));
|
QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(m_ui->comboDNSService->currentIndex()));
|
||||||
|
@ -1698,45 +1714,6 @@ QString OptionsDialog::languageToLocalizedString(const QLocale &locale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OptionsDialog::setSslKey(const QByteArray &key)
|
|
||||||
{
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
// try different formats
|
|
||||||
const bool isKeyValid = (!QSslKey(key, QSsl::Rsa).isNull() || !QSslKey(key, QSsl::Ec).isNull());
|
|
||||||
if (isKeyValid) {
|
|
||||||
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-high.svg", this, 24));
|
|
||||||
m_sslKey = key;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-low.svg", this, 24));
|
|
||||||
m_sslKey.clear();
|
|
||||||
}
|
|
||||||
return isKeyValid;
|
|
||||||
#else
|
|
||||||
Q_UNUSED(key);
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OptionsDialog::setSslCertificate(const QByteArray &cert)
|
|
||||||
{
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
const bool isCertValid = !QSslCertificate(cert).isNull();
|
|
||||||
if (isCertValid) {
|
|
||||||
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-high.svg", this, 24));
|
|
||||||
m_sslCert = cert;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmapSvg(":/icons/qbt-theme/security-low.svg", this, 24));
|
|
||||||
m_sslCert.clear();
|
|
||||||
}
|
|
||||||
return isCertValid;
|
|
||||||
#else
|
|
||||||
Q_UNUSED(cert);
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OptionsDialog::schedTimesOk()
|
bool OptionsDialog::schedTimesOk()
|
||||||
{
|
{
|
||||||
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) {
|
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) {
|
||||||
|
|
|
@ -60,7 +60,6 @@ class OptionsDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
using ThisType = OptionsDialog;
|
using ThisType = OptionsDialog;
|
||||||
|
|
||||||
private:
|
|
||||||
enum Tabs
|
enum Tabs
|
||||||
{
|
{
|
||||||
TAB_UI,
|
TAB_UI,
|
||||||
|
@ -73,6 +72,12 @@ private:
|
||||||
TAB_ADVANCED
|
TAB_ADVANCED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ShowError
|
||||||
|
{
|
||||||
|
NotShow,
|
||||||
|
Show
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor / Destructor
|
// Constructor / Destructor
|
||||||
OptionsDialog(QWidget *parent = nullptr);
|
OptionsDialog(QWidget *parent = nullptr);
|
||||||
|
@ -102,10 +107,10 @@ private slots:
|
||||||
void on_randomButton_clicked();
|
void on_randomButton_clicked();
|
||||||
void on_addScanFolderButton_clicked();
|
void on_addScanFolderButton_clicked();
|
||||||
void on_removeScanFolderButton_clicked();
|
void on_removeScanFolderButton_clicked();
|
||||||
void on_btnWebUiCrt_clicked();
|
|
||||||
void on_btnWebUiKey_clicked();
|
|
||||||
void on_registerDNSBtn_clicked();
|
void on_registerDNSBtn_clicked();
|
||||||
void setLocale(const QString &localeStr);
|
void setLocale(const QString &localeStr);
|
||||||
|
void webUIHttpsCertChanged(const QString &path, ShowError showError);
|
||||||
|
void webUIHttpsKeyChanged(const QString &path, ShowError showError);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Methods
|
// Methods
|
||||||
|
@ -164,17 +169,14 @@ private:
|
||||||
int getMaxActiveDownloads() const;
|
int getMaxActiveDownloads() const;
|
||||||
int getMaxActiveUploads() const;
|
int getMaxActiveUploads() const;
|
||||||
int getMaxActiveTorrents() const;
|
int getMaxActiveTorrents() const;
|
||||||
|
// WebUI
|
||||||
bool isWebUiEnabled() const;
|
bool isWebUiEnabled() const;
|
||||||
QString webUiUsername() const;
|
QString webUiUsername() const;
|
||||||
QString webUiPassword() const;
|
QString webUiPassword() const;
|
||||||
// WebUI SSL Cert / key
|
|
||||||
bool setSslKey(const QByteArray &key);
|
|
||||||
bool setSslCertificate(const QByteArray &cert);
|
|
||||||
bool schedTimesOk();
|
|
||||||
bool webUIAuthenticationOk();
|
bool webUIAuthenticationOk();
|
||||||
bool isAlternativeWebUIPathValid();
|
bool isAlternativeWebUIPathValid();
|
||||||
|
|
||||||
QByteArray m_sslCert, m_sslKey;
|
bool schedTimesOk();
|
||||||
|
|
||||||
Ui::OptionsDialog *m_ui;
|
Ui::OptionsDialog *m_ui;
|
||||||
QAbstractButton *m_applyButton;
|
QAbstractButton *m_applyButton;
|
||||||
|
|
|
@ -2966,80 +2966,26 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_11">
|
<layout class="QGridLayout" name="gridLayout_11">
|
||||||
<item row="0" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="lblSslCertStatus"/>
|
<widget class="QLabel" name="lblWebUiKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLabel" name="lblWebUiCrt">
|
<widget class="QLabel" name="lblWebUiCrt">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Certificate:</string>
|
<string>Certificate:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<item row="0" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<widget class="QLabel" name="lblSslCertStatus"/>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnWebUiCrt">
|
|
||||||
<property name="text">
|
|
||||||
<string>Import SSL Certificate</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_12">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>138</width>
|
|
||||||
<height>28</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="lblSslKeyStatus"/>
|
<widget class="QLabel" name="lblSslKeyStatus"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLabel" name="lblWebUiKey">
|
|
||||||
<property name="text">
|
|
||||||
<string>Key:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnWebUiKey">
|
|
||||||
<property name="text">
|
|
||||||
<string>Import SSL Key</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_13">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="3">
|
<item row="2" column="0" colspan="3">
|
||||||
<widget class="QLabel" name="lblWebUIInfo">
|
<widget class="QLabel" name="lblWebUIInfo">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -3050,6 +2996,12 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="FileSystemPathLineEdit" name="textWebUIHttpsCert" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="FileSystemPathLineEdit" name="textWebUIHttpsKey" native="true"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -3464,8 +3416,6 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
|
||||||
<tabstop>comboRatioLimitAct</tabstop>
|
<tabstop>comboRatioLimitAct</tabstop>
|
||||||
<tabstop>checkWebUIUPnP</tabstop>
|
<tabstop>checkWebUIUPnP</tabstop>
|
||||||
<tabstop>checkWebUiHttps</tabstop>
|
<tabstop>checkWebUiHttps</tabstop>
|
||||||
<tabstop>btnWebUiCrt</tabstop>
|
|
||||||
<tabstop>btnWebUiKey</tabstop>
|
|
||||||
<tabstop>checkBypassLocalAuth</tabstop>
|
<tabstop>checkBypassLocalAuth</tabstop>
|
||||||
<tabstop>checkBypassAuthSubnetWhitelist</tabstop>
|
<tabstop>checkBypassAuthSubnetWhitelist</tabstop>
|
||||||
<tabstop>IPSubnetWhitelistButton</tabstop>
|
<tabstop>IPSubnetWhitelistButton</tabstop>
|
||||||
|
|
|
@ -39,11 +39,6 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
#include <QSslCertificate>
|
|
||||||
#include <QSslKey>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
|
@ -223,8 +218,8 @@ void AppController::preferencesAction()
|
||||||
data["web_ui_port"] = pref->getWebUiPort();
|
data["web_ui_port"] = pref->getWebUiPort();
|
||||||
data["web_ui_upnp"] = pref->useUPnPForWebUIPort();
|
data["web_ui_upnp"] = pref->useUPnPForWebUIPort();
|
||||||
data["use_https"] = pref->isWebUiHttpsEnabled();
|
data["use_https"] = pref->isWebUiHttpsEnabled();
|
||||||
data["ssl_key"] = QString::fromLatin1(pref->getWebUiHttpsKey());
|
data["web_ui_https_cert_path"] = pref->getWebUIHttpsCertificatePath();
|
||||||
data["ssl_cert"] = QString::fromLatin1(pref->getWebUiHttpsCertificate());
|
data["web_ui_https_key_path"] = pref->getWebUIHttpsKeyPath();
|
||||||
// Authentication
|
// Authentication
|
||||||
data["web_ui_username"] = pref->getWebUiUsername();
|
data["web_ui_username"] = pref->getWebUiUsername();
|
||||||
data["bypass_local_auth"] = !pref->isWebUiLocalAuthEnabled();
|
data["bypass_local_auth"] = !pref->isWebUiLocalAuthEnabled();
|
||||||
|
@ -518,18 +513,10 @@ void AppController::setPreferencesAction()
|
||||||
pref->setUPnPForWebUIPort(m["web_ui_upnp"].toBool());
|
pref->setUPnPForWebUIPort(m["web_ui_upnp"].toBool());
|
||||||
if (m.contains("use_https"))
|
if (m.contains("use_https"))
|
||||||
pref->setWebUiHttpsEnabled(m["use_https"].toBool());
|
pref->setWebUiHttpsEnabled(m["use_https"].toBool());
|
||||||
#ifndef QT_NO_OPENSSL
|
if ((it = m.find(QLatin1String("web_ui_https_cert_path"))) != m.constEnd())
|
||||||
if (m.contains("ssl_key")) {
|
pref->setWebUIHttpsCertificatePath(it.value().toString());
|
||||||
QByteArray raw_key = m["ssl_key"].toString().toLatin1();
|
if ((it = m.find(QLatin1String("web_ui_https_key_path"))) != m.constEnd())
|
||||||
if (!QSslKey(raw_key, QSsl::Rsa).isNull())
|
pref->setWebUIHttpsKeyPath(it.value().toString());
|
||||||
pref->setWebUiHttpsKey(raw_key);
|
|
||||||
}
|
|
||||||
if (m.contains("ssl_cert")) {
|
|
||||||
QByteArray raw_cert = m["ssl_cert"].toString().toLatin1();
|
|
||||||
if (!QSslCertificate(raw_cert).isNull())
|
|
||||||
pref->setWebUiHttpsCertificate(raw_cert);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Authentication
|
// Authentication
|
||||||
if (m.contains("web_ui_username"))
|
if (m.contains("web_ui_username"))
|
||||||
pref->setWebUiUsername(m["web_ui_username"].toString());
|
pref->setWebUiUsername(m["web_ui_username"].toString());
|
||||||
|
|
|
@ -28,11 +28,14 @@
|
||||||
|
|
||||||
#include "webui.h"
|
#include "webui.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
#include "base/http/server.h"
|
#include "base/http/server.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/dnsupdater.h"
|
#include "base/net/dnsupdater.h"
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
|
#include "base/utils/net.h"
|
||||||
#include "webapplication.h"
|
#include "webapplication.h"
|
||||||
|
|
||||||
WebUI::WebUI()
|
WebUI::WebUI()
|
||||||
|
@ -77,11 +80,18 @@ void WebUI::configure()
|
||||||
m_httpServer->close();
|
m_httpServer->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
if (pref->isWebUiHttpsEnabled()) {
|
if (pref->isWebUiHttpsEnabled()) {
|
||||||
const QByteArray certs = pref->getWebUiHttpsCertificate();
|
const auto readData = [](const QString &path) -> QByteArray
|
||||||
const QByteArray key = pref->getWebUiHttpsKey();
|
{
|
||||||
bool success = m_httpServer->setupHttps(certs, key);
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
return {};
|
||||||
|
return file.read(Utils::Net::MAX_SSL_FILE_SIZE);
|
||||||
|
};
|
||||||
|
const QByteArray cert = readData(pref->getWebUIHttpsCertificatePath());
|
||||||
|
const QByteArray key = readData(pref->getWebUIHttpsKeyPath());
|
||||||
|
|
||||||
|
const bool success = m_httpServer->setupHttps(cert, key);
|
||||||
if (success)
|
if (success)
|
||||||
logger->addMessage(tr("Web UI: HTTPS setup successful"));
|
logger->addMessage(tr("Web UI: HTTPS setup successful"));
|
||||||
else
|
else
|
||||||
|
@ -90,7 +100,6 @@ void WebUI::configure()
|
||||||
else {
|
else {
|
||||||
m_httpServer->disableHttps();
|
m_httpServer->disableHttps();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!m_httpServer->isListening()) {
|
if (!m_httpServer->isListening()) {
|
||||||
const auto address = (serverAddressString == "*" || serverAddressString.isEmpty())
|
const auto address = (serverAddressString == "*" || serverAddressString.isEmpty())
|
||||||
|
|
|
@ -682,18 +682,18 @@
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label for="ssl_key_textarea">QBT_TR(Key:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
<label for="ssl_cert_text">QBT_TR(Certificate:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<textarea id="ssl_key_textarea" rows="5" cols="70"></textarea>
|
<input type="text" id="ssl_cert_text" style="width: 30em;" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label for="ssl_cert_textarea">QBT_TR(Certificate:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
<label for="ssl_key_text">QBT_TR(Key:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<textarea id="ssl_cert_textarea" rows="5" cols="70"></textarea>
|
<input type="text" id="ssl_key_text" style="width: 30em;" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1043,8 +1043,8 @@
|
||||||
// Web UI tab
|
// Web UI tab
|
||||||
var updateHttpsSettings = function() {
|
var updateHttpsSettings = function() {
|
||||||
var isUseHttpsEnabled = $('use_https_checkbox').getProperty('checked');
|
var isUseHttpsEnabled = $('use_https_checkbox').getProperty('checked');
|
||||||
$('ssl_key_textarea').setProperty('disabled', !isUseHttpsEnabled);
|
$('ssl_cert_text').setProperty('disabled', !isUseHttpsEnabled);
|
||||||
$('ssl_cert_textarea').setProperty('disabled', !isUseHttpsEnabled);
|
$('ssl_key_text').setProperty('disabled', !isUseHttpsEnabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateBypasssAuthSettings = function() {
|
var updateBypasssAuthSettings = function() {
|
||||||
|
@ -1330,8 +1330,8 @@
|
||||||
$('webui_port_value').setProperty('value', pref.web_ui_port);
|
$('webui_port_value').setProperty('value', pref.web_ui_port);
|
||||||
$('webui_upnp_checkbox').setProperty('checked', pref.web_ui_upnp);
|
$('webui_upnp_checkbox').setProperty('checked', pref.web_ui_upnp);
|
||||||
$('use_https_checkbox').setProperty('checked', pref.use_https);
|
$('use_https_checkbox').setProperty('checked', pref.use_https);
|
||||||
$('ssl_key_textarea').setProperty('value', pref.ssl_key);
|
$('ssl_cert_text').setProperty('value', pref.web_ui_https_cert_path);
|
||||||
$('ssl_cert_textarea').setProperty('value', pref.ssl_cert);
|
$('ssl_key_text').setProperty('value', pref.web_ui_https_key_path);
|
||||||
updateHttpsSettings();
|
updateHttpsSettings();
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
@ -1646,8 +1646,8 @@
|
||||||
settings.set('web_ui_port', web_ui_port);
|
settings.set('web_ui_port', web_ui_port);
|
||||||
settings.set('web_ui_upnp', $('webui_upnp_checkbox').getProperty('checked'));
|
settings.set('web_ui_upnp', $('webui_upnp_checkbox').getProperty('checked'));
|
||||||
settings.set('use_https', $('use_https_checkbox').getProperty('checked'));
|
settings.set('use_https', $('use_https_checkbox').getProperty('checked'));
|
||||||
settings.set('ssl_key', $('ssl_key_textarea').getProperty('value'));
|
settings.set('web_ui_https_cert_path', $('ssl_cert_text').getProperty('value'));
|
||||||
settings.set('ssl_cert', $('ssl_cert_textarea').getProperty('value'));
|
settings.set('web_ui_https_key_path', $('ssl_key_text').getProperty('value'));
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
var web_ui_username = $('webui_username_text').getProperty('value');
|
var web_ui_username = $('webui_username_text').getProperty('value');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue