write_mac_qt/src/MainWindow.cpp

338 lines
12 KiB
C++
Raw Normal View History

#include "MainWindow.h"
#include "NetworkManager.h"
#include "SshClient.h"
#include "Logger.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QMessageBox>
#include <QFile>
#include <QRegularExpression>
#include <QDateTime>
#include <QCoreApplication>
#include <QScreen>
#include <QGuiApplication>
static const char *kInterfacesPath = "/etc/network/interfaces";
// 尝试在存在参考文件时使用其前缀以保持格式一致
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
ipInput(new QLineEdit(this)),
macInput(new QLineEdit(this)),
currentMacLabel(new QLabel("当前MAC: 未读取", this)),
modifyButton(new QPushButton("修改MAC", this)),
backupButton(new QPushButton("备份文件", this)),
logView(new QTextEdit(this)),
useSshCheck(new QCheckBox("使用SSH远程执行", this)),
sshUserInput(new QLineEdit(this)),
sshPassInput(new QLineEdit(this)),
presetPassCombo(new QComboBox(this)),
connectButton(new QPushButton("连接", this)),
refreshButton(new QPushButton("刷新", this)),
netMgr(new NetworkManager(this)),
sshClient(new SshClient(this)),
logger(new Logger(this))
{
setWindowTitle("MAC修改工具");
logView->setReadOnly(true);
// 根据屏幕分辨率自适应调整日志框大小
QScreen *screen = QGuiApplication::primaryScreen();
if (screen) {
QRect screenGeometry = screen->availableGeometry();
int screenWidth = screenGeometry.width();
int screenHeight = screenGeometry.height();
// 日志框宽度为屏幕宽度的 25-30%,最小 390px
int logWidth = qMax(390, static_cast<int>(screenWidth * 0.25));
// 日志框高度为屏幕高度的 40-50%,最小 410px
int logHeight = qMax(410, static_cast<int>(screenHeight * 0.4));
logView->setMinimumWidth(logWidth);
logView->setMinimumHeight(logHeight);
} else {
// 如果无法获取屏幕信息,使用默认值
logView->setMinimumWidth(390);
logView->setMinimumHeight(410);
}
sshPassInput->setEchoMode(QLineEdit::Password);
auto *central = new QWidget(this);
auto *layout = new QVBoxLayout(central);
auto *form = new QFormLayout();
form->addRow("SSH主机/IP", ipInput);
presetIpCombo = new QComboBox(this);
presetIpCombo->addItem("自定义");
presetIpCombo->addItem("圆通");
presetIpCombo->addItem("拼多多");
form->addRow("内置IP", presetIpCombo);
form->addRow("SSH用户", sshUserInput);
presetPassCombo->addItem("自定义");
presetPassCombo->addItem("圆通");
presetPassCombo->addItem("拼多多");
presetPassCombo->addItem("兔喜");
form->addRow("内置密码", presetPassCombo);
form->addRow("登录密码", sshPassInput);
form->addRow("修改MAC地址", macInput);
// 在“当前MAC”行右侧添加 连接/刷新 按钮
QWidget *macButtonsWidget = new QWidget(this);
auto *macBtnsLayout = new QHBoxLayout(macButtonsWidget);
macBtnsLayout->setContentsMargins(0,0,0,0);
macBtnsLayout->addWidget(connectButton);
macBtnsLayout->addWidget(refreshButton);
form->addRow(currentMacLabel, macButtonsWidget);
layout->addLayout(form);
auto *btns = new QHBoxLayout();
btns->addWidget(backupButton);
btns->addWidget(modifyButton);
layout->addLayout(btns);
layout->addWidget(useSshCheck);
useSshCheck->setChecked(true);
useSshCheck->hide();
connect(useSshCheck, &QCheckBox::toggled, this, &MainWindow::onSshModeToggled);
layout->addWidget(new QLabel("日志", this));
layout->addWidget(logView);
setCentralWidget(central);
connect(modifyButton, &QPushButton::clicked, this, &MainWindow::onModifyClicked);
connect(backupButton, &QPushButton::clicked, this, &MainWindow::onBackupClicked);
connect(connectButton, &QPushButton::clicked, this, &MainWindow::onConnectClicked);
connect(refreshButton, &QPushButton::clicked, this, &MainWindow::onRefreshClicked);
connect(presetPassCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onPresetPassChanged);
connect(presetIpCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::onPresetIpChanged);
// 默认使用SSH远程执行提供占位提示
useSshCheck->setChecked(true);
ipInput->setPlaceholderText("例: 10.10.12.12/hostname");
macInput->setText("90:A9:F7:30:00:00");
sshUserInput->setText("root");
sshPassInput->setPlaceholderText("登录密码");
updateCurrentMac();
}
MainWindow::~MainWindow() {}
void MainWindow::log(const QString &msg) {
QString ts = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QString line = ts + " - " + msg;
logger->append(msg);
logView->append(line);
logView->append("");
}
QString MainWindow::generateMacFromIp(const QString &ip) {
// 简单示例固定前缀90:A9:F7:30根据IP最后两段计算两个字节末尾固定00
QRegularExpression re("^(?:\\d{1,3}\\.){3}\\d{1,3}$");
if (!re.match(ip).hasMatch()) return {};
auto parts = ip.split('.');
if (parts.size() != 4) return {};
bool ok1=false, ok2=false;
int p2 = parts[2].toInt(&ok1);
int p3 = parts[3].toInt(&ok2);
if (!ok1 || !ok2) return {};
auto toHexByte = [](int v){ v = qBound(0, v, 255); return QString("%1").arg(v,2,16,QChar('0')).toUpper(); };
QString mac = QString("90:A9:F7:30:%1:%2").arg(toHexByte(p2))
.arg(toHexByte(p3));
return mac;
}
bool MainWindow::isValidMac(const QString &mac) const {
QRegularExpression mre("^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){5}$");
return mre.match(mac).hasMatch();
}
void MainWindow::updateCurrentMac() {
if (useSshCheck->isChecked() && !sshConnected) {
setCurrentMacStatus("当前MAC: 未连接主机", false);
return;
}
auto mac = netMgr->readCurrentMac(kInterfacesPath);
if (mac.isEmpty()) {
setCurrentMacStatus("当前MAC: 未找到或读取失败", false);
} else {
setCurrentMacStatus("当前MAC: " + mac, true);
}
}
void MainWindow::onBackupClicked() {
QString err;
// 检查是否使用 SSH 远程模式
if (useSshCheck->isChecked()) {
QString host = ipInput->text().trimmed();
QString user = sshUserInput->text().trimmed();
QString password = sshPassInput->text().trimmed();
if (host.isEmpty() || user.isEmpty()) {
QMessageBox::warning(this, "参数错误", "请先填写IP地址和SSH用户名");
return;
}
// 使用 SshClient 执行远程备份
SshClient ssh;
if (!ssh.backupRemoteFile(host, user, password, kInterfacesPath, &err)) {
QMessageBox::critical(this, "备份失败", err);
log("备份失败: " + err);
return;
}
QMessageBox::information(this, "备份完成", "已在远程服务器备份到 " + QString(kInterfacesPath) + ".bak");
log("已备份远程 interfaces 文件");
return;
}
// 本地模式
if (!netMgr->backupFile(kInterfacesPath, &err)) {
QMessageBox::critical(this, "备份失败", err);
log("备份失败: " + err);
return;
}
QMessageBox::information(this, "备份完成", "已备份到同目录 .bak 文件");
log("已备份 interfaces 文件");
}
void MainWindow::onModifyClicked() {
QString ip = ipInput->text().trimmed();
QString mac = macInput->text().trimmed();
if (mac.isEmpty()) mac = generateMacFromIp(ip);
if (!isValidMac(mac)) {
QMessageBox::warning(this, "输入错误", "MAC地址格式错误需形如AA:BB:CC:DD:EE:FF");
return;
}
if (useSshCheck->isChecked()) {
// 通过SSH远程执行修改
QString host = ipInput->text().trimmed();
QString user = sshUserInput->text().trimmed();
QString auth = QString("密码");
QString secret = sshPassInput->text();
QString err;
QString refPath = QCoreApplication::applicationDirPath() + "/interfaces";
QString useRef = QFile::exists(refPath) ? refPath : QString();
bool ok = sshClient->applyMacRemote(host, user, auth, secret, mac, kInterfacesPath, useRef, &err);
if (!ok) {
QMessageBox::critical(this, "远程修改失败", err);
log("远程修改失败: " + err);
return;
}
QMessageBox::information(this, "远程修改完成", "已通过SSH修改MAC");
log("远程修改完成");
if (!err.isEmpty()) log(err);
onRefreshClicked();
} else {
// 本地修改
QString err;
if (!netMgr->ensureRootPermission(&err)) {
QMessageBox::critical(this, "权限不足", err);
log("权限不足: " + err);
return;
}
QString refPath = QCoreApplication::applicationDirPath() + "/interfaces";
QString useRef = QFile::exists(refPath) ? refPath : QString();
if (!netMgr->applyMacLocal(kInterfacesPath, useRef, mac, &err)) {
QMessageBox::critical(this, "修改失败", err);
log("修改失败: " + err);
return;
}
QMessageBox::information(this, "修改完成", "MAC地址已更新");
log("本地修改完成更新MAC为: " + mac);
updateCurrentMac();
}
}
void MainWindow::onConnectClicked() {
// 连接尝试读取远程当前MAC
QString host = ipInput->text().trimmed();
QString user = sshUserInput->text().trimmed();
QString secret = sshPassInput->text();
QString err;
// 远端读取:复用 applyMacRemote 的读取函数(将实现)
QString remoteMac;
if (sshClient->readRemoteCurrentMac(host, user, secret, kInterfacesPath, &remoteMac, &err)) {
setCurrentMacStatus("当前MAC: " + remoteMac, true);
log("已连接并读取远程MAC: " + remoteMac);
sshConnected = true;
} else {
QMessageBox::critical(this, "连接失败", err);
log("连接失败: " + err);
sshConnected = false;
setCurrentMacStatus("当前MAC: 未连接主机", false);
}
}
void MainWindow::onRefreshClicked() {
// 刷新当前MAC根据是否使用SSH选择本地或远程
if (useSshCheck->isChecked()) {
QString host = ipInput->text().trimmed();
QString user = sshUserInput->text().trimmed();
QString secret = sshPassInput->text();
QString err;
QString remoteMac;
if (sshClient->readRemoteCurrentMac(host, user, secret, kInterfacesPath, &remoteMac, &err)) {
setCurrentMacStatus("当前MAC: " + remoteMac, true);
log("已刷新远程MAC: " + remoteMac);
sshConnected = true;
} else {
QMessageBox::warning(this, "刷新失败", err);
log("刷新失败: " + err);
sshConnected = false;
setCurrentMacStatus("当前MAC: 未连接主机", false);
}
} else {
updateCurrentMac();
log("已刷新本地MAC");
}
}
void MainWindow::onPresetPassChanged(int idx) {
switch (idx) {
case 1:
sshPassInput->setText("&Over#B0Ost!");
break;
case 2:
sshPassInput->setText("PddloTSecPwdOnly!");
break;
case 3:
sshPassInput->setText("TxApPwd#2025!");
break;
default:
// 自定义:不覆盖用户输入,可选择清空
// sshPassInput->clear();
break;
}
}
void MainWindow::onPresetIpChanged(int idx) {
switch (idx) {
case 1:
ipInput->setText("192.168.172.173");
break;
case 2:
ipInput->setText("10.10.12.12");
break;
default:
// 自定义:不强制覆盖,保留用户当前输入
break;
}
}
void MainWindow::onSshModeToggled(bool checked) {
sshConnected = false;
updateCurrentMac();
}
void MainWindow::setCurrentMacStatus(const QString &text, bool ok) {
currentMacLabel->setText(text);
QPalette pal = currentMacLabel->palette();
pal.setColor(QPalette::WindowText, ok ? QColor(0, 128, 0) : QColor(200, 0, 0));
currentMacLabel->setPalette(pal);
}