404 lines
13 KiB
C++
404 lines
13 KiB
C++
|
#include "stdafx.h"
|
||
|
#include "TestProxyDialog.h"
|
||
|
#include <QtConcurrent>
|
||
|
|
||
|
constexpr auto TestProgressMax = 90;
|
||
|
|
||
|
CTestProxyDialog::CTestProxyDialog(const QString& IP, const QString& Port, COptionsWindow::EAuthMode AuthMode, const QString& Username, const QString& Password, QWidget* parent)
|
||
|
|
||
|
: QDialog(parent)
|
||
|
{
|
||
|
m_ProxyIP = IP;
|
||
|
m_ProxyPort = Port;
|
||
|
m_ProxyUsername = Username;
|
||
|
m_ProxyPass = Password;
|
||
|
m_AuthMode = AuthMode;
|
||
|
|
||
|
m_TestShouldCancel = 0;
|
||
|
m_Watcher = new QFutureWatcher<bool>(this);
|
||
|
|
||
|
Qt::WindowFlags flags = windowFlags();
|
||
|
setWindowFlags(flags);
|
||
|
|
||
|
ui.setupUi(this);
|
||
|
RestoreDefaults();
|
||
|
this->setWindowTitle(tr("Sandboxie-Plus - Test Proxy"));
|
||
|
this->setFixedSize(this->size());
|
||
|
ui.stackedWidget->setCurrentIndex(0);
|
||
|
|
||
|
ui.labelAddressOut->setText(IP + ":" + Port);
|
||
|
ui.labelAuthOut->setText(COptionsWindow::GetAuthModeStr(AuthMode).toUpper());
|
||
|
if (AuthMode == COptionsWindow::EAuthMode::eAuthEnabled)
|
||
|
{
|
||
|
ui.labelUsernameOut->setText(Username);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ui.labelUsernameOut->setText(tr("N/A"));
|
||
|
ui.labelUsernameOut->hide();
|
||
|
ui.labelUsername->hide();
|
||
|
}
|
||
|
|
||
|
ui.labelTestResults->setText(tr("Testing..."));
|
||
|
ui.progressBar->setValue(0);
|
||
|
ui.progressBar->setMinimum(0);
|
||
|
ui.progressBar->setMaximum(TestProgressMax);
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setFocus();
|
||
|
|
||
|
connect(ui.buttonBoxTest->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,&CTestProxyDialog::accept);
|
||
|
connect(ui.buttonBoxTest->button(QDialogButtonBox::Retry), &QPushButton::clicked, this, &CTestProxyDialog::OnRetry);
|
||
|
connect(ui.btnTestCustomize, &QPushButton::clicked, this, &CTestProxyDialog::OnTestCustomize);
|
||
|
connect(ui.buttonBoxSettings->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &CTestProxyDialog::OnTestSettingsSave);
|
||
|
connect(ui.buttonBoxSettings->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &CTestProxyDialog::OnTestSettingsCancel);
|
||
|
connect(ui.buttonBoxSettings->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &CTestProxyDialog::OnTestSettingsRestoreDefaults);
|
||
|
connect(this, &CTestProxyDialog::emitTestMessage, this, [this](const QString& message) { ui.textBrowser->append(message); });
|
||
|
connect(this, &CTestProxyDialog::emitUpdateProgress, this, [this](int value) { ui.progressBar->setValue(value); });
|
||
|
connect(m_Watcher, &QFutureWatcher<bool>::finished, this, &CTestProxyDialog::OnTestFinished);
|
||
|
connect(ui.checkBoxTest2, &QCheckBox::clicked, this, [this]() { Test2EnableParams(ui.checkBoxTest2->isChecked()); });
|
||
|
connect(ui.checkBoxTest1, &QCheckBox::clicked, this, [this]() {
|
||
|
if (!ui.checkBoxTest1->isChecked())
|
||
|
{
|
||
|
ui.checkBoxTest1->setChecked(true);
|
||
|
QMessageBox::warning(this, tr("Sandboxie-Plus - Test Proxy"), tr("This test cannot be disabled."));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::showEvent(QShowEvent* event)
|
||
|
{
|
||
|
QDialog::showEvent(event);
|
||
|
ui.textBrowser->clear();
|
||
|
OnRetry();
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::RunTest1(bool& failed, int& progress, int segment)
|
||
|
{
|
||
|
const constexpr int pollInterval = 1000;
|
||
|
|
||
|
QThread::msleep(500);
|
||
|
QString time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
emit emitTestMessage(tr("[%1] Starting Test 1: Connection to the Proxy Server").arg(time));
|
||
|
emit emitTestMessage(tr("[%1] IP Address: %2").arg(time).arg(m_ProxyIP));
|
||
|
|
||
|
QScopedPointer<QTcpSocket> socket(new QTcpSocket(this));
|
||
|
|
||
|
bool success = false;
|
||
|
for (int elapsed = 0; !success && elapsed < m_TestTimeout; elapsed += pollInterval)
|
||
|
{
|
||
|
socket->connectToHost(m_ProxyIP, m_ProxyPort.toInt());
|
||
|
success = socket->waitForConnected(pollInterval);
|
||
|
if (m_TestShouldCancel.loadAcquire()) return;
|
||
|
}
|
||
|
|
||
|
time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
if (success)
|
||
|
{
|
||
|
emit emitUpdateProgress(progress += segment);
|
||
|
emit emitTestMessage(tr("[%1] Connection established.").arg(time));
|
||
|
emit emitTestMessage(tr("[%1] Test passed.").arg(time));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
failed = true;
|
||
|
emit emitTestMessage(tr("[%1] Connection to proxy server failed: %2.").arg(time).arg(socket->errorString()));
|
||
|
emit emitTestMessage(tr("[%1] Test failed.").arg(time));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::RunTest2(bool& failed, int& progress, int segment, bool loadPage)
|
||
|
{
|
||
|
const constexpr int pollInterval = 1000;
|
||
|
|
||
|
QThread::msleep(500);
|
||
|
QString time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
emit emitTestMessage(tr("[%1] Starting Test 2: Connection through the Proxy Server").arg(time));
|
||
|
|
||
|
QNetworkProxy proxy;
|
||
|
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||
|
proxy.setHostName(m_ProxyIP);
|
||
|
proxy.setPort(m_ProxyPort.toInt());
|
||
|
if (m_AuthMode == COptionsWindow::EAuthMode::eAuthEnabled) {
|
||
|
proxy.setUser(m_ProxyUsername);
|
||
|
proxy.setPassword(m_ProxyPass);
|
||
|
}
|
||
|
|
||
|
QScopedPointer<QTcpSocket> socket(new QTcpSocket(this));
|
||
|
socket->setProxy(proxy);
|
||
|
|
||
|
bool success = false;
|
||
|
for (int elapsed = 0; !success && elapsed < m_TestTimeout; elapsed += pollInterval)
|
||
|
{
|
||
|
socket->connectToHost(m_TestHost, m_TestPort);
|
||
|
success = socket->waitForConnected(pollInterval);
|
||
|
if (m_TestShouldCancel.loadAcquire()) return;
|
||
|
}
|
||
|
|
||
|
time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
if (success)
|
||
|
{
|
||
|
segment /= loadPage + 1;
|
||
|
emit emitUpdateProgress(progress += segment);
|
||
|
emit emitTestMessage(tr("[%1] Authentication was successful.").arg(time));
|
||
|
emit emitTestMessage(tr("[%1] Connection to %2 established through the proxy server.").arg(time).arg(m_TestHost + ":" + QString::number(m_TestPort)));
|
||
|
|
||
|
if (loadPage)
|
||
|
{
|
||
|
emit emitTestMessage(tr("[%1] Loading a web page to test the proxy server.").arg(time));
|
||
|
RunTest2LoadPage(proxy, failed);
|
||
|
if (m_TestShouldCancel.loadAcquire()) return;
|
||
|
emit emitUpdateProgress(progress += segment);
|
||
|
}
|
||
|
|
||
|
emit emitTestMessage(tr("[%1] %2.").arg(time).arg(!failed ? "Test passed" : "Test failed"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
failed = true;
|
||
|
emit emitTestMessage(tr("[%1] Connection through proxy server failed: %2.").arg(time).arg(socket->errorString()));
|
||
|
emit emitTestMessage(tr("[%1] Test failed.").arg(time));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::RunTest2LoadPage(const QNetworkProxy& proxy, bool& failed)
|
||
|
{
|
||
|
const constexpr int pollInterval = 100;
|
||
|
|
||
|
QScopedPointer<QNetworkAccessManager> manager(new QNetworkAccessManager(this));
|
||
|
manager->setProxy(proxy);
|
||
|
|
||
|
QEventLoop loop;
|
||
|
QNetworkRequest request(QUrl("http://" + m_TestHost + ":" + QString::number(m_TestPort)));
|
||
|
QScopedPointer<QNetworkReply> reply(manager->get(request));
|
||
|
|
||
|
QTimer timer;
|
||
|
timer.setInterval(pollInterval);
|
||
|
int elapsed = 0;
|
||
|
|
||
|
connect(&timer, &QTimer::timeout, this, [&]() {
|
||
|
elapsed += pollInterval;
|
||
|
if (elapsed >= m_TestTimeout && !reply->isFinished()) reply->abort();
|
||
|
if (m_TestShouldCancel.loadAcquire())
|
||
|
{
|
||
|
loop.quit();
|
||
|
timer.stop();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
connect(reply.data(), &QNetworkReply::finished, this, [&]() {
|
||
|
QString time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
if (reply->error() == QNetworkReply::NoError)
|
||
|
{
|
||
|
emit emitTestMessage(tr("[%1] Web page loaded successfully.").arg(time));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
failed = true;
|
||
|
QString error = reply->error() == QNetworkReply::OperationCanceledError ? tr("Timeout") : reply->errorString();
|
||
|
emit emitTestMessage(tr("[%1] Failed to load web page: %2.").arg(time).arg(error));
|
||
|
}
|
||
|
loop.quit();
|
||
|
timer.stop();
|
||
|
});
|
||
|
|
||
|
timer.start();
|
||
|
loop.exec();
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::RunTest3(bool& failed, int& progress, int segment)
|
||
|
{
|
||
|
const constexpr int pollInterval = 100;
|
||
|
const int totalTimeout = m_TestTimeout * m_TestPingCount;
|
||
|
|
||
|
QThread::msleep(500);
|
||
|
QString time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
emit emitTestMessage(tr("[%1] Starting Test 3: Proxy Server latency").arg(time));
|
||
|
|
||
|
bool finished = false;
|
||
|
QScopedPointer<QProcess> pingProc(new QProcess(this));
|
||
|
QString program = "ping";
|
||
|
QStringList args = { "-n", QString::number(m_TestPingCount), "-w", QString::number(m_TestTimeout), m_ProxyIP };
|
||
|
connect(pingProc.data(),
|
||
|
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||
|
this,
|
||
|
[&finished](int, QProcess::ExitStatus) { finished = true; });
|
||
|
pingProc->start(program, args);
|
||
|
|
||
|
for (int elapsed = 0; !finished && elapsed < totalTimeout; elapsed += pollInterval)
|
||
|
{
|
||
|
finished = pingProc->waitForFinished(pollInterval);
|
||
|
if (m_TestShouldCancel.loadAcquire())
|
||
|
{
|
||
|
pingProc->kill();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QString pingOutput = pingProc->readAllStandardOutput();
|
||
|
time = QTime::currentTime().toString("hh:mm:ss");
|
||
|
if (pingProc->exitStatus() == QProcess::NormalExit && pingProc->exitCode() == 0)
|
||
|
{
|
||
|
QRegularExpression re("Average = (\\d+)ms");
|
||
|
if (re.match(pingOutput).hasMatch())
|
||
|
{
|
||
|
int elapsed = re.match(pingOutput).captured(1).toInt();
|
||
|
emit emitUpdateProgress(progress += segment);
|
||
|
emit emitTestMessage(tr("[%1] Latency through proxy server: %2ms.").arg(time).arg(elapsed));
|
||
|
emit emitTestMessage(tr("[%1] Test passed.").arg(time));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
failed = true;
|
||
|
emit emitTestMessage(tr("[%1] Failed to get proxy server latency: Request timeout.").arg(time));
|
||
|
emit emitTestMessage(tr("[%1] Test failed.").arg(time));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
failed = true;
|
||
|
emit emitTestMessage(tr("[%1] Failed to get proxy server latency.").arg(time));
|
||
|
emit emitTestMessage(tr("[%1] Test failed.").arg(time));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::Test2EnableParams(bool enable)
|
||
|
{
|
||
|
ui.checkBoxTest2Load->setEnabled(enable);
|
||
|
ui.lineEditHost->setEnabled(enable);
|
||
|
ui.lineEditPort->setEnabled(enable);
|
||
|
ui.labelHost->setEnabled(enable);
|
||
|
ui.labelPort->setEnabled(enable);
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::TestProxy()
|
||
|
{
|
||
|
QFuture<bool> future = QtConcurrent::run([this]() {
|
||
|
bool test1 = ui.checkBoxTest1->isChecked();
|
||
|
bool test2 = ui.checkBoxTest2->isChecked();
|
||
|
bool test2LoadPage = ui.checkBoxTest2Load->isChecked();
|
||
|
bool test3 = ui.checkBoxTest3->isChecked();
|
||
|
bool failed = false;
|
||
|
int segment = TestProgressMax / (test1 + test2 + test3);
|
||
|
int progress = 0;
|
||
|
|
||
|
if (test1 && !m_TestShouldCancel.loadAcquire()) RunTest1(failed, progress, segment);
|
||
|
if (test2 && !failed && !m_TestShouldCancel.loadAcquire()) RunTest2(failed, progress, segment, test2LoadPage);
|
||
|
if (test3 && !failed && !m_TestShouldCancel.loadAcquire()) RunTest3(failed, progress, segment);
|
||
|
|
||
|
emit emitTestMessage(tr("[%1] Test Finished.").arg(QTime::currentTime().toString("hh:mm:ss")));
|
||
|
return !failed;
|
||
|
});
|
||
|
|
||
|
m_Watcher->setFuture(future);
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::OnRetry()
|
||
|
{
|
||
|
if (m_Watcher->isRunning())
|
||
|
{
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setEnabled(false);
|
||
|
m_TestShouldCancel.storeRelease(1);
|
||
|
ui.labelTestResults->setText(tr("Stopped"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ui.progressBar->setValue(0);
|
||
|
ui.btnTestCustomize->setEnabled(false);
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setEnabled(true);
|
||
|
ui.labelTestResults->setStyleSheet("");
|
||
|
ui.labelTestResults->setText(tr("Testing..."));
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setText(tr("Stop"));
|
||
|
|
||
|
ui.textBrowser->append(
|
||
|
tr("[%1] Testing started...\n"
|
||
|
"\tProxy Server\n"
|
||
|
"\tAddress:\t\t%2\n"
|
||
|
"\tProtocol:\t\t%3\n"
|
||
|
"\tAuthentication:\t%4%5")
|
||
|
.arg(QTime::currentTime().toString("hh:mm:ss"))
|
||
|
.arg(m_ProxyIP + ":" + m_ProxyPort)
|
||
|
.arg("SOCKS 5")
|
||
|
.arg(COptionsWindow::GetAuthModeStr(m_AuthMode).toUpper())
|
||
|
.arg(m_AuthMode == COptionsWindow::eAuthEnabled ? QString("\n\tUsername:\t\t%1").arg(m_ProxyUsername) : QString()));
|
||
|
|
||
|
TestProxy();
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::OnTestFinished()
|
||
|
{
|
||
|
bool success = m_Watcher->future().result();
|
||
|
|
||
|
ui.progressBar->setValue(TestProgressMax);
|
||
|
ui.btnTestCustomize->setEnabled(true);
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setText(tr("Retry"));
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Retry)->setEnabled(true);
|
||
|
|
||
|
if (success) {
|
||
|
if (m_TestShouldCancel.loadAcquire())
|
||
|
ui.labelTestResults->setText(tr("Stopped"));
|
||
|
else
|
||
|
{
|
||
|
ui.labelTestResults->setStyleSheet("color: green;");
|
||
|
ui.labelTestResults->setText(tr("Test Passed"));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ui.labelTestResults->setStyleSheet("color: red;");
|
||
|
ui.labelTestResults->setText(tr("Test Failed"));
|
||
|
}
|
||
|
ui.buttonBoxTest->button(QDialogButtonBox::Ok)->setFocus();
|
||
|
|
||
|
m_TestShouldCancel.storeRelease(0);
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::OnTestSettingsSave()
|
||
|
{
|
||
|
bool ok;
|
||
|
m_TestTimeout = ui.lineEditTimeout->text().toInt(&ok);
|
||
|
if (!ok || m_TestTimeout < 1 || m_TestTimeout > 60)
|
||
|
{
|
||
|
QMessageBox::warning(this, tr("Sandboxie-Plus - Test Proxy"), tr("Invalid Timeout value. Please enter a value between 1 and 60."));
|
||
|
return;
|
||
|
}
|
||
|
m_TestTimeout *= 1000; // Convert to ms
|
||
|
m_TestPort = ui.lineEditPort->text().toInt(&ok);
|
||
|
if (!ok || m_TestPort < 1 || m_TestPort > 65535)
|
||
|
{
|
||
|
QMessageBox::warning(this, tr("Sandboxie-Plus - Test Proxy"), tr("Invalid Port value. Please enter a value between 1 and 65535."));
|
||
|
return;
|
||
|
}
|
||
|
m_TestHost = ui.lineEditHost->text();
|
||
|
if (m_TestHost.isEmpty() || m_TestHost.contains(QRegularExpression("http[s]?://")))
|
||
|
{
|
||
|
QMessageBox::warning(this, tr("Sandboxie-Plus - Test Proxy"), tr("Invalid Host value. Please enter a valid host name excluding 'http[s]://'."));
|
||
|
return;
|
||
|
}
|
||
|
m_TestPingCount = ui.spinBoxPingCount->value();
|
||
|
if (m_TestPingCount < 1 || m_TestPingCount > 10)
|
||
|
{
|
||
|
QMessageBox::warning(this, tr("Sandboxie-Plus - Test Proxy"), tr("Invalid Ping Count value. Please enter a value between 1 and 10."));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ui.stackedWidget->setCurrentIndex(0);
|
||
|
}
|
||
|
|
||
|
void CTestProxyDialog::RestoreDefaults()
|
||
|
{
|
||
|
m_TestTimeout = 5 * 1000; // 5000ms
|
||
|
m_TestHost = QString("www.google.com");
|
||
|
m_TestPort = 80;
|
||
|
m_TestPingCount = 4;
|
||
|
|
||
|
ui.lineEditTimeout->setText(QString::number(5));
|
||
|
ui.checkBoxTest1->setChecked(true);
|
||
|
ui.checkBoxTest2->setChecked(true);
|
||
|
ui.checkBoxTest3->setChecked(true);
|
||
|
ui.checkBoxTest2Load->setChecked(true);
|
||
|
ui.lineEditHost->setText(m_TestHost);
|
||
|
ui.lineEditPort->setText(QString::number(m_TestPort));
|
||
|
ui.spinBoxPingCount->setValue(m_TestPingCount);
|
||
|
|
||
|
Test2EnableParams(true);
|
||
|
}
|