mirror of
https://github.com/chenx-dust/EZ4Connect.git
synced 2026-09-20 10:23:33 +08:00
refactor: 统一管理连接会话生命周期
This commit is contained in:
@@ -35,8 +35,10 @@ set(SOURCE_FILES
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
zjuconnectmode.cpp
|
||||
application/connectionsession.cpp
|
||||
core/corecommandbuilder.cpp
|
||||
core/coreoutputparser.cpp
|
||||
core/connectionsessionstate.cpp
|
||||
infrastructure/settingsprofileloader.cpp
|
||||
zjuconnectcontroller/zjuconnectcontroller.cpp
|
||||
extrasettingwindow/extrasettingwindow.cpp
|
||||
@@ -136,4 +138,12 @@ if(BUILD_TESTING)
|
||||
target_include_directories(settingsprofileloader_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(settingsprofileloader_test Qt::Core)
|
||||
add_test(NAME settingsprofileloader_test COMMAND settingsprofileloader_test)
|
||||
|
||||
add_executable(connectionsessionstate_test
|
||||
tests/connectionsessionstate_test.cpp
|
||||
core/connectionsessionstate.cpp
|
||||
)
|
||||
target_include_directories(connectionsessionstate_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(connectionsessionstate_test Qt::Core)
|
||||
add_test(NAME connectionsessionstate_test COMMAND connectionsessionstate_test)
|
||||
endif()
|
||||
|
||||
141
application/connectionsession.cpp
Normal file
141
application/connectionsession.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "connectionsession.h"
|
||||
|
||||
ConnectionSession::ConnectionSession(QObject *parent)
|
||||
: QObject(parent),
|
||||
controller(new ZjuConnectController(this))
|
||||
{
|
||||
reconnectTimer.setSingleShot(true);
|
||||
|
||||
connect(controller, &ZjuConnectController::outputRead, this, &ConnectionSession::outputRead);
|
||||
connect(controller, &ZjuConnectController::graphCaptcha, this, &ConnectionSession::graphCaptcha);
|
||||
connect(controller, &ZjuConnectController::smsCode, this, &ConnectionSession::smsCode);
|
||||
connect(controller, &ZjuConnectController::totpCode, this, &ConnectionSession::totpCode);
|
||||
connect(controller, &ZjuConnectController::ssoAuth, this, &ConnectionSession::ssoAuth);
|
||||
connect(controller, &ZjuConnectController::askSudoPass,
|
||||
this, &ConnectionSession::handleSudoPasswordRequest);
|
||||
|
||||
connect(controller, &ZjuConnectController::error, this, [this](ZJU_ERROR error)
|
||||
{
|
||||
sessionState.recordError(error);
|
||||
});
|
||||
connect(controller, &ZjuConnectController::started, this, [this]()
|
||||
{
|
||||
sessionState.processStarted();
|
||||
emit stateChanged(sessionState.state());
|
||||
});
|
||||
connect(controller, &ZjuConnectController::finished,
|
||||
this, &ConnectionSession::handleCoreFinished);
|
||||
connect(&reconnectTimer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
if (sessionState.state() != ConnectionState::Reconnecting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
sessionState.beginReconnect();
|
||||
emit stateChanged(sessionState.state());
|
||||
startCore();
|
||||
});
|
||||
}
|
||||
|
||||
bool ConnectionSession::start(const ConnectionProfile &profile, const ReconnectPolicy &policy)
|
||||
{
|
||||
if (!sessionState.requestStart(policy))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currentProfile = profile;
|
||||
emit stateChanged(sessionState.state());
|
||||
startCore();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionSession::stop()
|
||||
{
|
||||
const ConnectionState previousState = sessionState.state();
|
||||
const bool processNeedsStop = sessionState.requestStop();
|
||||
if (previousState == ConnectionState::Reconnecting)
|
||||
{
|
||||
reconnectTimer.stop();
|
||||
emit stateChanged(sessionState.state());
|
||||
emit finished(sessionState.error());
|
||||
return;
|
||||
}
|
||||
|
||||
if (processNeedsStop)
|
||||
{
|
||||
emit stateChanged(sessionState.state());
|
||||
controller->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSession::submitInput(const QByteArray &data)
|
||||
{
|
||||
controller->writeInput(data);
|
||||
}
|
||||
|
||||
void ConnectionSession::submitSudoPassword(const QString &password, bool remember)
|
||||
{
|
||||
if (password.isEmpty())
|
||||
{
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (remember)
|
||||
{
|
||||
savedSudoPassword = password;
|
||||
}
|
||||
sudoPasswordSubmitted = true;
|
||||
submitInput(password.toUtf8() + "\n");
|
||||
}
|
||||
|
||||
ConnectionState ConnectionSession::state() const
|
||||
{
|
||||
return sessionState.state();
|
||||
}
|
||||
|
||||
bool ConnectionSession::isActive() const
|
||||
{
|
||||
return sessionState.isActive();
|
||||
}
|
||||
|
||||
void ConnectionSession::startCore()
|
||||
{
|
||||
sudoPasswordSubmitted = false;
|
||||
controller->start(currentProfile);
|
||||
}
|
||||
|
||||
void ConnectionSession::handleCoreFinished()
|
||||
{
|
||||
if (sessionState.processFinished() == ProcessFinishAction::Reconnect)
|
||||
{
|
||||
emit stateChanged(sessionState.state());
|
||||
emit reconnectScheduled(sessionState.reconnectDelayMs());
|
||||
reconnectTimer.start(sessionState.reconnectDelayMs());
|
||||
return;
|
||||
}
|
||||
|
||||
emit stateChanged(sessionState.state());
|
||||
emit finished(sessionState.error());
|
||||
}
|
||||
|
||||
void ConnectionSession::handleSudoPasswordRequest()
|
||||
{
|
||||
if (savedSudoPassword.isEmpty())
|
||||
{
|
||||
emit askSudoPass();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sudoPasswordSubmitted)
|
||||
{
|
||||
savedSudoPassword.clear();
|
||||
emit savedSudoPasswordRejected();
|
||||
emit askSudoPass();
|
||||
return;
|
||||
}
|
||||
|
||||
sudoPasswordSubmitted = true;
|
||||
submitInput(savedSudoPassword.toUtf8() + "\n");
|
||||
}
|
||||
51
application/connectionsession.h
Normal file
51
application/connectionsession.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef CONNECTIONSESSION_H
|
||||
#define CONNECTIONSESSION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/connectionprofile.h"
|
||||
#include "core/connectionsessionstate.h"
|
||||
#include "zjuconnectcontroller/zjuconnectcontroller.h"
|
||||
|
||||
class ConnectionSession : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectionSession(QObject *parent = nullptr);
|
||||
|
||||
bool start(const ConnectionProfile &profile, const ReconnectPolicy &policy);
|
||||
void stop();
|
||||
void submitInput(const QByteArray &data);
|
||||
void submitSudoPassword(const QString &password, bool remember);
|
||||
|
||||
ConnectionState state() const;
|
||||
bool isActive() const;
|
||||
|
||||
signals:
|
||||
void stateChanged(ConnectionState state);
|
||||
void outputRead(const QString &output);
|
||||
void graphCaptcha(const QString &graphFile);
|
||||
void smsCode(bool showSkipSecondaryAuthOption);
|
||||
void totpCode();
|
||||
void ssoAuth();
|
||||
void askSudoPass();
|
||||
void savedSudoPasswordRejected();
|
||||
void reconnectScheduled(int delayMs);
|
||||
void finished(ZJU_ERROR error);
|
||||
|
||||
private:
|
||||
void startCore();
|
||||
void handleCoreFinished();
|
||||
void handleSudoPasswordRequest();
|
||||
|
||||
ZjuConnectController *controller;
|
||||
QTimer reconnectTimer;
|
||||
ConnectionSessionState sessionState;
|
||||
ConnectionProfile currentProfile;
|
||||
QString savedSudoPassword;
|
||||
bool sudoPasswordSubmitted = false;
|
||||
};
|
||||
|
||||
#endif // CONNECTIONSESSION_H
|
||||
21
core/connectionerror.h
Normal file
21
core/connectionerror.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CONNECTIONERROR_H
|
||||
#define CONNECTIONERROR_H
|
||||
|
||||
enum class ZJU_ERROR
|
||||
{
|
||||
NONE,
|
||||
INVALID_DETAIL,
|
||||
BRUTE_FORCE,
|
||||
OTHER_LOGIN_FAILED,
|
||||
ACCESS_DENIED,
|
||||
LISTEN_FAILED,
|
||||
CLIENT_FAILED,
|
||||
CAPTCHA_FAILED,
|
||||
PROGRAM_NOT_FOUND,
|
||||
INTERACTIVE_ERROR,
|
||||
AUTH_NOT_AVAILABLE,
|
||||
AUTH_EXPIRED,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
#endif // CONNECTIONERROR_H
|
||||
106
core/connectionsessionstate.cpp
Normal file
106
core/connectionsessionstate.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "connectionsessionstate.h"
|
||||
|
||||
ConnectionState ConnectionSessionState::state() const
|
||||
{
|
||||
return currentState;
|
||||
}
|
||||
|
||||
ZJU_ERROR ConnectionSessionState::error() const
|
||||
{
|
||||
return currentError;
|
||||
}
|
||||
|
||||
bool ConnectionSessionState::isActive() const
|
||||
{
|
||||
return currentState == ConnectionState::Starting
|
||||
|| currentState == ConnectionState::Running
|
||||
|| currentState == ConnectionState::Stopping
|
||||
|| currentState == ConnectionState::Reconnecting;
|
||||
}
|
||||
|
||||
bool ConnectionSessionState::wantsConnection() const
|
||||
{
|
||||
return desiredConnected;
|
||||
}
|
||||
|
||||
int ConnectionSessionState::reconnectDelayMs() const
|
||||
{
|
||||
return reconnectPolicy.delayMs;
|
||||
}
|
||||
|
||||
bool ConnectionSessionState::requestStart(const ReconnectPolicy &policy)
|
||||
{
|
||||
if (isActive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
reconnectPolicy = policy;
|
||||
desiredConnected = true;
|
||||
currentError = ZJU_ERROR::NONE;
|
||||
currentState = ConnectionState::Starting;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionSessionState::processStarted()
|
||||
{
|
||||
if (currentState == ConnectionState::Starting)
|
||||
{
|
||||
currentState = ConnectionState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSessionState::recordError(ZJU_ERROR error)
|
||||
{
|
||||
if (currentError == ZJU_ERROR::NONE)
|
||||
{
|
||||
currentError = error;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionSessionState::requestStop()
|
||||
{
|
||||
desiredConnected = false;
|
||||
if (currentState == ConnectionState::Reconnecting)
|
||||
{
|
||||
currentState = ConnectionState::Disconnected;
|
||||
return false;
|
||||
}
|
||||
if (currentState == ConnectionState::Starting || currentState == ConnectionState::Running)
|
||||
{
|
||||
currentState = ConnectionState::Stopping;
|
||||
return true;
|
||||
}
|
||||
return currentState == ConnectionState::Stopping;
|
||||
}
|
||||
|
||||
ProcessFinishAction ConnectionSessionState::processFinished()
|
||||
{
|
||||
if (desiredConnected
|
||||
&& reconnectPolicy.enabled
|
||||
&& isReconnectable(currentError))
|
||||
{
|
||||
currentState = ConnectionState::Reconnecting;
|
||||
return ProcessFinishAction::Reconnect;
|
||||
}
|
||||
|
||||
desiredConnected = false;
|
||||
currentState = currentError == ZJU_ERROR::NONE
|
||||
? ConnectionState::Disconnected
|
||||
: ConnectionState::Failed;
|
||||
return ProcessFinishAction::Complete;
|
||||
}
|
||||
|
||||
void ConnectionSessionState::beginReconnect()
|
||||
{
|
||||
if (currentState == ConnectionState::Reconnecting && desiredConnected)
|
||||
{
|
||||
currentError = ZJU_ERROR::NONE;
|
||||
currentState = ConnectionState::Starting;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionSessionState::isReconnectable(ZJU_ERROR error)
|
||||
{
|
||||
return error == ZJU_ERROR::AUTH_EXPIRED || error == ZJU_ERROR::OTHER;
|
||||
}
|
||||
53
core/connectionsessionstate.h
Normal file
53
core/connectionsessionstate.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef CONNECTIONSESSIONSTATE_H
|
||||
#define CONNECTIONSESSIONSTATE_H
|
||||
|
||||
#include "connectionerror.h"
|
||||
|
||||
enum class ConnectionState
|
||||
{
|
||||
Disconnected,
|
||||
Starting,
|
||||
Running,
|
||||
Stopping,
|
||||
Reconnecting,
|
||||
Failed,
|
||||
};
|
||||
|
||||
struct ReconnectPolicy
|
||||
{
|
||||
bool enabled = false;
|
||||
int delayMs = 1000;
|
||||
};
|
||||
|
||||
enum class ProcessFinishAction
|
||||
{
|
||||
Complete,
|
||||
Reconnect,
|
||||
};
|
||||
|
||||
class ConnectionSessionState
|
||||
{
|
||||
public:
|
||||
ConnectionState state() const;
|
||||
ZJU_ERROR error() const;
|
||||
bool isActive() const;
|
||||
bool wantsConnection() const;
|
||||
int reconnectDelayMs() const;
|
||||
|
||||
bool requestStart(const ReconnectPolicy &policy);
|
||||
void processStarted();
|
||||
void recordError(ZJU_ERROR error);
|
||||
bool requestStop();
|
||||
ProcessFinishAction processFinished();
|
||||
void beginReconnect();
|
||||
|
||||
private:
|
||||
static bool isReconnectable(ZJU_ERROR error);
|
||||
|
||||
ConnectionState currentState = ConnectionState::Disconnected;
|
||||
ZJU_ERROR currentError = ZJU_ERROR::NONE;
|
||||
ReconnectPolicy reconnectPolicy;
|
||||
bool desiredConnected = false;
|
||||
};
|
||||
|
||||
#endif // CONNECTIONSESSIONSTATE_H
|
||||
@@ -37,9 +37,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
upgradeSettings();
|
||||
|
||||
isFirstTimeSetMode = true;
|
||||
isZjuConnectLinked = false;
|
||||
isSystemProxySet = false;
|
||||
zjuConnectError = ZJU_ERROR::NONE;
|
||||
|
||||
ui->setupUi(this);
|
||||
setupTrayIcon();
|
||||
@@ -353,7 +351,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (isZjuConnectLinked)
|
||||
if (connectionSession != nullptr && connectionSession->isActive())
|
||||
{
|
||||
event->ignore();
|
||||
hide();
|
||||
@@ -402,8 +400,6 @@ void MainWindow::clearLog()
|
||||
|
||||
void MainWindow::resetZjuConnectUi()
|
||||
{
|
||||
isZjuConnectLinked = false;
|
||||
zjuConnectError = ZJU_ERROR::NONE;
|
||||
ui->pushButton1->setText("连接服务器");
|
||||
trayConnectAction->setText("连接服务器");
|
||||
ui->pushButton2->setText("设置系统代理");
|
||||
@@ -581,7 +577,7 @@ bool MainWindow::switchProfile(const QString &profileId)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isZjuConnectLinked)
|
||||
if (connectionSession != nullptr && connectionSession->isActive())
|
||||
{
|
||||
QMessageBox::warning(this, "切换失败", "请先断开 VPN 连接,再切换配置。");
|
||||
refreshProfileMenu();
|
||||
@@ -657,7 +653,7 @@ void MainWindow::renameCurrentProfile()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (isZjuConnectLinked)
|
||||
if (connectionSession != nullptr && connectionSession->isActive())
|
||||
{
|
||||
QMessageBox::warning(this, "重命名失败", "请先断开 VPN 连接,再重命名配置。");
|
||||
return;
|
||||
@@ -824,9 +820,10 @@ void MainWindow::cleanUpWhenQuit()
|
||||
|
||||
void MainWindow::gracefullyQuit()
|
||||
{
|
||||
if (isZjuConnectLinked)
|
||||
if (connectionSession != nullptr && connectionSession->isActive())
|
||||
{
|
||||
connect(zjuConnectController, &ZjuConnectController::finished, qApp, QApplication::quit);
|
||||
connect(connectionSession, &ConnectionSession::finished, qApp,
|
||||
[](ZJU_ERROR) { QApplication::quit(); });
|
||||
ui->pushButton1->click();
|
||||
}
|
||||
else
|
||||
@@ -848,10 +845,11 @@ MainWindow::~MainWindow()
|
||||
delete profileManager;
|
||||
}
|
||||
|
||||
if (zjuConnectController != nullptr)
|
||||
if (connectionSession != nullptr)
|
||||
{
|
||||
disconnect(zjuConnectController, &ZjuConnectController::finished, nullptr, nullptr);
|
||||
delete zjuConnectController;
|
||||
disconnect(connectionSession, nullptr, this, nullptr);
|
||||
delete connectionSession;
|
||||
connectionSession = nullptr;
|
||||
}
|
||||
|
||||
delete ui;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QSettings>
|
||||
#include <QPointer>
|
||||
|
||||
#include "application/connectionsession.h"
|
||||
#include "loginwindow/loginwindow.h"
|
||||
#include "sudowindow/sudowindow.h"
|
||||
#include "ssologinwebview/ssologinwebview.h"
|
||||
@@ -41,8 +42,6 @@ signals:
|
||||
|
||||
void SetModeFinished();
|
||||
|
||||
void WriteToProcess(const QByteArray &data);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *e) override;
|
||||
|
||||
@@ -98,7 +97,7 @@ private:
|
||||
QAction *newProfileAction;
|
||||
QAction *renameProfileAction;
|
||||
QAction *deleteProfileAction;
|
||||
ZjuConnectController *zjuConnectController = nullptr;
|
||||
ConnectionSession *connectionSession = nullptr;
|
||||
QNetworkAccessManager *checkUpdateNAM;
|
||||
QNetworkAccessManager *checkCoreUpdateNAM;
|
||||
QSettings *settings;
|
||||
@@ -115,10 +114,7 @@ private:
|
||||
|
||||
bool isFirstTimeSetMode;
|
||||
|
||||
bool isZjuConnectLinked;
|
||||
bool isSystemProxySet;
|
||||
bool isAutoReconnecting = false;
|
||||
ZJU_ERROR zjuConnectError;
|
||||
};
|
||||
|
||||
#endif //MAINWINDOW_H
|
||||
|
||||
108
tests/connectionsessionstate_test.cpp
Normal file
108
tests/connectionsessionstate_test.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#include "core/connectionsessionstate.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool normalLifecycle()
|
||||
{
|
||||
ConnectionSessionState session;
|
||||
if (!session.requestStart({false, 1000})
|
||||
|| session.state() != ConnectionState::Starting
|
||||
|| !session.isActive())
|
||||
{
|
||||
qCritical() << "normalLifecycle failed at start";
|
||||
return false;
|
||||
}
|
||||
|
||||
session.processStarted();
|
||||
if (session.state() != ConnectionState::Running)
|
||||
{
|
||||
qCritical() << "normalLifecycle failed at running";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!session.requestStop() || session.state() != ConnectionState::Stopping)
|
||||
{
|
||||
qCritical() << "normalLifecycle failed at stopping";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (session.processFinished() != ProcessFinishAction::Complete
|
||||
|| session.state() != ConnectionState::Disconnected
|
||||
|| session.isActive())
|
||||
{
|
||||
qCritical() << "normalLifecycle failed at completion";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reconnectsOnlyEligibleFailures()
|
||||
{
|
||||
ConnectionSessionState session;
|
||||
session.requestStart({true, 2500});
|
||||
session.processStarted();
|
||||
session.recordError(ZJU_ERROR::AUTH_EXPIRED);
|
||||
|
||||
if (session.processFinished() != ProcessFinishAction::Reconnect
|
||||
|| session.state() != ConnectionState::Reconnecting
|
||||
|| session.reconnectDelayMs() != 2500)
|
||||
{
|
||||
qCritical() << "reconnectsOnlyEligibleFailures failed at scheduling";
|
||||
return false;
|
||||
}
|
||||
|
||||
session.beginReconnect();
|
||||
if (session.state() != ConnectionState::Starting || session.error() != ZJU_ERROR::NONE)
|
||||
{
|
||||
qCritical() << "reconnectsOnlyEligibleFailures failed at restart";
|
||||
return false;
|
||||
}
|
||||
|
||||
session.processStarted();
|
||||
session.recordError(ZJU_ERROR::INVALID_DETAIL);
|
||||
if (session.processFinished() != ProcessFinishAction::Complete
|
||||
|| session.state() != ConnectionState::Failed)
|
||||
{
|
||||
qCritical() << "reconnectsOnlyEligibleFailures reconnected an ineligible error";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool keepsFirstErrorAndCancelsPendingReconnect()
|
||||
{
|
||||
ConnectionSessionState session;
|
||||
session.requestStart({true, 1000});
|
||||
session.processStarted();
|
||||
session.recordError(ZJU_ERROR::AUTH_EXPIRED);
|
||||
session.recordError(ZJU_ERROR::OTHER);
|
||||
if (session.error() != ZJU_ERROR::AUTH_EXPIRED)
|
||||
{
|
||||
qCritical() << "keepsFirstErrorAndCancelsPendingReconnect did not keep first error";
|
||||
return false;
|
||||
}
|
||||
|
||||
session.processFinished();
|
||||
if (session.requestStop()
|
||||
|| session.state() != ConnectionState::Disconnected
|
||||
|| session.wantsConnection())
|
||||
{
|
||||
qCritical() << "keepsFirstErrorAndCancelsPendingReconnect failed to cancel reconnect";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
return normalLifecycle()
|
||||
&& reconnectsOnlyEligibleFailures()
|
||||
&& keepsFirstErrorAndCancelsPendingReconnect()
|
||||
? 0
|
||||
: 1;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "zjuconnectcontroller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "core/corecommandbuilder.h"
|
||||
#include "core/coreoutputparser.h"
|
||||
#include "utils/utils.h"
|
||||
@@ -10,7 +9,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
|
||||
ZjuConnectController::ZjuConnectController(QWidget* parent) : QObject(parent)
|
||||
ZjuConnectController::ZjuConnectController(QObject *parent) : QObject(parent)
|
||||
{
|
||||
zjuConnectProcess = new QProcess(this);
|
||||
|
||||
@@ -21,7 +20,7 @@ ZjuConnectController::ZjuConnectController(QWidget* parent) : QObject(parent)
|
||||
logStream = new QTextStream(logFile);
|
||||
logStream->setEncoding(QStringConverter::Utf8);
|
||||
QString startMsg = "=== Log started at " + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") +
|
||||
" with " + QApplication::applicationDisplayName() + " " + QApplication::applicationVersion() +
|
||||
" with " + QCoreApplication::applicationName() + " " + QCoreApplication::applicationVersion() +
|
||||
" ===\n";
|
||||
*logStream << startMsg;
|
||||
logStream->flush();
|
||||
@@ -138,10 +137,6 @@ ZjuConnectController::ZjuConnectController(QWidget* parent) : QObject(parent)
|
||||
emit outputRead(timeString + " 退出原因:" "进程已结束");
|
||||
emit finished();
|
||||
});
|
||||
|
||||
|
||||
connect(qobject_cast<MainWindow *>(parent), &MainWindow::WriteToProcess, this,
|
||||
[&](const QByteArray &data) { zjuConnectProcess->write(data); });
|
||||
}
|
||||
|
||||
QString ZjuConnectController::copyCoreForAppImage(const QString &programPath)
|
||||
@@ -232,7 +227,6 @@ void ZjuConnectController::start(const ConnectionProfile &profile)
|
||||
sudoArgs << programToStart << finalArgs;
|
||||
programToStart = "sudo";
|
||||
finalArgs = sudoArgs;
|
||||
enteredSudoPassword = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -242,6 +236,10 @@ void ZjuConnectController::start(const ConnectionProfile &profile)
|
||||
{
|
||||
emit finished();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit started();
|
||||
}
|
||||
}
|
||||
|
||||
void ZjuConnectController::stop()
|
||||
@@ -262,6 +260,11 @@ void ZjuConnectController::stop()
|
||||
}
|
||||
}
|
||||
|
||||
void ZjuConnectController::writeInput(const QByteArray &data)
|
||||
{
|
||||
zjuConnectProcess->write(data);
|
||||
}
|
||||
|
||||
ZjuConnectController::~ZjuConnectController()
|
||||
{
|
||||
stop();
|
||||
|
||||
@@ -3,31 +3,15 @@
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
#include "core/connectionerror.h"
|
||||
#include "core/connectionprofile.h"
|
||||
|
||||
enum class ZJU_ERROR
|
||||
{
|
||||
NONE,
|
||||
INVALID_DETAIL,
|
||||
BRUTE_FORCE,
|
||||
OTHER_LOGIN_FAILED,
|
||||
ACCESS_DENIED,
|
||||
LISTEN_FAILED,
|
||||
CLIENT_FAILED,
|
||||
CAPTCHA_FAILED,
|
||||
PROGRAM_NOT_FOUND,
|
||||
INTERACTIVE_ERROR,
|
||||
AUTH_NOT_AVAILABLE,
|
||||
AUTH_EXPIRED,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
class ZjuConnectController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ZjuConnectController(QWidget* parent);
|
||||
explicit ZjuConnectController(QObject *parent = nullptr);
|
||||
|
||||
~ZjuConnectController() override;
|
||||
|
||||
@@ -35,6 +19,7 @@ public:
|
||||
|
||||
void stop();
|
||||
|
||||
void writeInput(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
|
||||
@@ -52,9 +37,9 @@ signals:
|
||||
|
||||
void askSudoPass();
|
||||
|
||||
void finished();
|
||||
void started();
|
||||
|
||||
void write(const QByteArray &data);
|
||||
void finished();
|
||||
|
||||
private:
|
||||
QString copyCoreForAppImage(const QString &programPath);
|
||||
@@ -69,10 +54,6 @@ private:
|
||||
QTextStream *logStream = nullptr;
|
||||
bool stopRequested = false;
|
||||
|
||||
public:
|
||||
bool savedSudoPassword;
|
||||
bool enteredSudoPassword;
|
||||
QString sudoPassword;
|
||||
};
|
||||
|
||||
#endif //ZJUCONNECTCONTROLLER_H
|
||||
|
||||
@@ -17,72 +17,38 @@
|
||||
|
||||
void MainWindow::initZjuConnect()
|
||||
{
|
||||
if (zjuConnectController != nullptr)
|
||||
if (connectionSession != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
clearLog();
|
||||
|
||||
zjuConnectController = new ZjuConnectController(this);
|
||||
connectionSession = new ConnectionSession(this);
|
||||
resetZjuConnectUi();
|
||||
|
||||
// 连接服务器
|
||||
connect(zjuConnectController, &ZjuConnectController::outputRead, this,
|
||||
connect(connectionSession, &ConnectionSession::outputRead, this,
|
||||
[&](const QString &output)
|
||||
{
|
||||
ui->logPlainTextEdit->appendPlainText(output.trimmed());
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::error, this,
|
||||
[&](ZJU_ERROR err)
|
||||
{
|
||||
if (zjuConnectError == ZJU_ERROR::NONE)
|
||||
{
|
||||
zjuConnectError = err;
|
||||
}
|
||||
});
|
||||
connect(connectionSession, &ConnectionSession::savedSudoPasswordRejected, this,
|
||||
[&]() { addLog("sudo 密码可能有误,不使用记住的密码"); });
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::askSudoPass, this,
|
||||
connect(connectionSession, &ConnectionSession::askSudoPass, this,
|
||||
[&]()
|
||||
{
|
||||
if (zjuConnectController->savedSudoPassword)
|
||||
{
|
||||
if (zjuConnectController->enteredSudoPassword)
|
||||
{
|
||||
addLog("sudo 密码可能有误,不使用记住的密码");
|
||||
zjuConnectController->savedSudoPassword = false;
|
||||
zjuConnectController->sudoPassword.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
zjuConnectController->enteredSudoPassword = true;
|
||||
emit WriteToProcess(zjuConnectController->sudoPassword.toUtf8() + "\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sudoWindow = new SudoWindow(this);
|
||||
connect(sudoWindow, &SudoWindow::sudo, this, [&](const QString &password, bool save)
|
||||
{
|
||||
if (password.isEmpty())
|
||||
{
|
||||
zjuConnectController->stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (save)
|
||||
{
|
||||
zjuConnectController->savedSudoPassword = true;
|
||||
zjuConnectController->sudoPassword = password;
|
||||
}
|
||||
zjuConnectController->enteredSudoPassword = true;
|
||||
emit WriteToProcess(password.toUtf8() + "\n");
|
||||
}
|
||||
connectionSession->submitSudoPassword(password, save);
|
||||
});
|
||||
sudoWindow->show();
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::graphCaptcha, this,
|
||||
connect(connectionSession, &ConnectionSession::graphCaptcha, this,
|
||||
[&](const QString &graphFile) {
|
||||
addLog("需要图形验证码");
|
||||
graphCaptchaWindow = new GraphCaptchaWindow(this);
|
||||
@@ -90,11 +56,11 @@ void MainWindow::initZjuConnect()
|
||||
graphCaptchaWindow->show();
|
||||
connect(graphCaptchaWindow, &GraphCaptchaWindow::finishCaptcha, this, [&](const QByteArray &captcha) {
|
||||
addLog("图形验证码用户输入:" + captcha);
|
||||
emit WriteToProcess(captcha + "\n");
|
||||
connectionSession->submitInput(captcha + "\n");
|
||||
});
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::smsCode, this, [&](bool showSkipSecondaryAuthOption) {
|
||||
connect(connectionSession, &ConnectionSession::smsCode, this, [&](bool showSkipSecondaryAuthOption) {
|
||||
addLog("需要短信验证码");
|
||||
|
||||
QDialog smsCodeDialog(this);
|
||||
@@ -130,20 +96,20 @@ void MainWindow::initZjuConnect()
|
||||
{
|
||||
smsCodeInput.prepend('$');
|
||||
}
|
||||
emit WriteToProcess(smsCodeInput + "\n");
|
||||
connectionSession->submitInput(smsCodeInput + "\n");
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::totpCode, this, [&]() {
|
||||
connect(connectionSession, &ConnectionSession::totpCode, this, [&]() {
|
||||
addLog("需要 TOTP 验证码");
|
||||
QString totp = QInputDialog::getText(this, "TOTP 验证码", "请输入 TOTP 验证码:");
|
||||
addLog("TOTP 验证码用户输入:" + totp);
|
||||
emit WriteToProcess(totp.toLocal8Bit() + "\n");
|
||||
connectionSession->submitInput(totp.toLocal8Bit() + "\n");
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::ssoAuth, this, [&]() {
|
||||
connect(connectionSession, &ConnectionSession::ssoAuth, this, [&]() {
|
||||
ssoLoginWebView = new SsoLoginWebView(this);
|
||||
connect(ssoLoginWebView, &SsoLoginWebView::loginCompleted,
|
||||
[=](const QString &url) { emit WriteToProcess(url.toLocal8Bit() + "\n"); });
|
||||
[=](const QString &url) { connectionSession->submitInput(url.toLocal8Bit() + "\n"); });
|
||||
|
||||
QString serverHost = settings->value("ZJUConnect/ServerAddress", "trust.hitsz.edu.cn").toString();
|
||||
int serverPort = settings->value("ZJUConnect/ServerPort", 443).toInt();
|
||||
@@ -164,36 +130,18 @@ void MainWindow::initZjuConnect()
|
||||
ssoLoginWebView->show();
|
||||
});
|
||||
|
||||
connect(zjuConnectController, &ZjuConnectController::finished, this, [&]()
|
||||
{
|
||||
addLog("VPN 断开!");
|
||||
if (
|
||||
(zjuConnectError == ZJU_ERROR::AUTH_EXPIRED || zjuConnectError == ZJU_ERROR::OTHER) &&
|
||||
settings->value("Common/AutoReconnect", false).toBool() &&
|
||||
isZjuConnectLinked
|
||||
)
|
||||
{
|
||||
QTimer::singleShot(settings->value("Common/ReconnectTime", 1).toInt() * 1000, this, [&]()
|
||||
{
|
||||
if (isZjuConnectLinked)
|
||||
connect(connectionSession, &ConnectionSession::reconnectScheduled, this, [&](int)
|
||||
{
|
||||
addLog("正在尝试重新连接...");
|
||||
zjuConnectController->stop();
|
||||
|
||||
isZjuConnectLinked = false;
|
||||
isAutoReconnecting = true;
|
||||
ui->pushButton1->click();
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (zjuConnectError != ZJU_ERROR::NONE)
|
||||
connect(connectionSession, &ConnectionSession::finished, this, [&](ZJU_ERROR error)
|
||||
{
|
||||
addLog("VPN 断开!");
|
||||
if (error != ZJU_ERROR::NONE)
|
||||
{
|
||||
showNotification("VPN", "VPN 意外断开!", QSystemTrayIcon::MessageIcon::Warning);
|
||||
}
|
||||
isZjuConnectLinked = false;
|
||||
ui->pushButton1->setText("连接服务器");
|
||||
trayConnectAction->setText("连接服务器");
|
||||
if (isSystemProxySet)
|
||||
@@ -202,7 +150,7 @@ void MainWindow::initZjuConnect()
|
||||
}
|
||||
ui->pushButton2->hide();
|
||||
|
||||
switch (zjuConnectError)
|
||||
switch (error)
|
||||
{
|
||||
case ZJU_ERROR::INVALID_DETAIL:
|
||||
QMessageBox::critical(this, "错误", "登录失败!\n请检查设置中的网络账号和密码是否设置正确。");
|
||||
@@ -244,13 +192,12 @@ void MainWindow::initZjuConnect()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
zjuConnectError = ZJU_ERROR::NONE;
|
||||
});
|
||||
|
||||
connect(ui->pushButton1, &QPushButton::clicked,
|
||||
[&]()
|
||||
{
|
||||
if (!isZjuConnectLinked)
|
||||
if (!connectionSession->isActive())
|
||||
{
|
||||
if (settings->contains("ZJUConnect/ServerAddress") &&
|
||||
settings->value("ZJUConnect/ServerAddress").toString().isEmpty())
|
||||
@@ -281,22 +228,23 @@ void MainWindow::initZjuConnect()
|
||||
#endif
|
||||
|
||||
auto startZjuConnect = [this](const QString &username, const QString &password) {
|
||||
isZjuConnectLinked = true;
|
||||
zjuConnectError = ZJU_ERROR::NONE;
|
||||
ui->pushButton1->setText("断开服务器");
|
||||
trayConnectAction->setText("断开服务器");
|
||||
ui->pushButton2->show();
|
||||
|
||||
if (!isAutoReconnecting && settings->value("Common/AutoSetProxy", false).toBool())
|
||||
if (settings->value("Common/AutoSetProxy", false).toBool())
|
||||
{
|
||||
ui->pushButton2->click();
|
||||
}
|
||||
isAutoReconnecting = false;
|
||||
|
||||
ConnectionProfile profile =
|
||||
SettingsProfileLoader::load(*settings, currentProfileId, username, password);
|
||||
profile.program = Utils::getCorePath();
|
||||
zjuConnectController->start(profile);
|
||||
const ReconnectPolicy reconnectPolicy{
|
||||
settings->value("Common/AutoReconnect", false).toBool(),
|
||||
settings->value("Common/ReconnectTime", 1).toInt() * 1000
|
||||
};
|
||||
connectionSession->start(profile, reconnectPolicy);
|
||||
};
|
||||
|
||||
if (((protocol == "atrust" && authtype == "psw") ||
|
||||
@@ -327,7 +275,7 @@ void MainWindow::initZjuConnect()
|
||||
}
|
||||
else
|
||||
{
|
||||
zjuConnectController->stop();
|
||||
connectionSession->stop();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -377,7 +325,7 @@ void MainWindow::initZjuConnect()
|
||||
Utils::clearSystemProxy();
|
||||
ui->pushButton2->setText("设置系统代理");
|
||||
isSystemProxySet = false;
|
||||
if (!isZjuConnectLinked)
|
||||
if (!connectionSession->isActive())
|
||||
{
|
||||
ui->pushButton2->hide();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user