write_mac_qt/src/MainWindow.cpp

338 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}