Compare commits

...

3 Commits
main ... hqq

@ -1,9 +1,11 @@
QT += core gui
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

@ -1,10 +1,22 @@
#include "backend_interface.h"
#include <iostream>
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include<stack>
//#include <curl/curl.h>
#include <stack>
#include <chrono>
#include<QByteArray>
#include<QString>
#include<QTcpSocket>
#include <QSslSocket>
#include <QDebug>
#include <QDateTime>
#include <QSslConfiguration>
#include <QSslCertificate>
#include <QSslKey>
// #include <uuid/uuid.h> // 如果需要生成UUID
namespace exam_system {
@ -26,12 +38,11 @@ BackendImpl::BackendImpl() {
// 初始化 ExamSystem
exam_system_ = std::make_unique<ExamSystem>();
// 配置邮箱设置
email_config_.smtp_server = "smtp.163.com";
email_config_.smtp_port = 587; // 使用STARTTLS
email_config_.username = "your_email@163.com";
email_config_.password = "your_authorization_code"; // 注意:这是授权码,不是登录密码
email_config_.use_curl = true;
// 配置邮箱设置 - 使用qq邮箱
email_config_.smtp_server = "smtp.qq.com";
email_config_.smtp_port = 465; // 使用SSL端口
email_config_.username = "3315384591@qq.com"; // 替换为真实邮箱
email_config_.password = "jobgsqxruxtgdaig"; // 替换为真实授权码
// 初始化libcurl
// curl_global_init(CURL_GLOBAL_DEFAULT);
@ -39,13 +50,13 @@ BackendImpl::BackendImpl() {
Logger::Log(Logger::Level::INFO, "BackendImpl初始化完成");
}
// BackendImpl::~BackendImpl() {
// // curl_global_cleanup();
// }
BackendImpl::~BackendImpl() {
// curl_global_cleanup();
}
bool BackendImpl::SendVerificationCode(const std::string& email) {
if (email.empty() || email.find('@') == std::string::npos) {
Logger::Log(Logger::Level::ERROR, "邮箱地址格式无效: " + email);
Logger::Log(Logger::Level::WARNING, "邮箱地址格式无效: " + email);
return false;
}
@ -64,9 +75,9 @@ bool BackendImpl::SendVerificationCode(const std::string& email) {
// 发送邮件
std::string subject = "数学考试系统 - 验证码";
std::string body = "您的验证码是: " + code + "\n\n";
body += "该验证码有效期为5分钟请尽快使用。\n";
body += "如果不是您本人操作,请忽略此邮件。\n\n";
std::string body = "您的验证码是: " + code + "\r\n\r\n";
body += "该验证码有效期为5分钟请尽快使用。\r\n";
body += "如果不是您本人操作,请忽略此邮件。\r\n\r\n";
body += "数学考试系统团队";
bool send_success = SendEmail(email, subject, body);
@ -82,6 +93,254 @@ bool BackendImpl::SendVerificationCode(const std::string& email) {
}
}
bool waitForResponse(QSslSocket* socket) {
if (!socket->waitForReadyRead(15000)) { // 增加超时时间到15秒
qDebug() << "Timeout waiting for server response";
return false;
}
QByteArray response = socket->readAll();
qDebug() << "Server response:" << response.trimmed();
// 检查SMTP响应码
if (response.length() >= 3) {
bool ok = false;
int code = response.left(3).toInt(&ok);
if (ok) {
// SMTP成功响应码是2xx和3xx
if (code >= 400) {
qDebug() << "SMTP error response:" << response.trimmed();
return false;
}
return true;
}
}
qDebug() << "Invalid server response format";
return false;
}
bool sendCommand(QSslSocket* socket, const QByteArray& command) {
// 记录发送的命令(隐藏密码)
QByteArray logCommand = command;
if (logCommand.startsWith("AUTH LOGIN") ||
logCommand.contains("MjY3NDc0MzM5MEBxcS5jb20=") || // 隐藏特定base64
logCommand.contains("am9iZ3NxeHJ1eHRnZGFpZw==")) {
logCommand = "[SENSITIVE DATA HIDDEN]";
}
qDebug() << "Sending command:" << logCommand.trimmed();
qint64 bytesWritten = socket->write(command);
if (bytesWritten == -1) {
qDebug() << "Failed to write command:" << socket->errorString();
return false;
}
if (!socket->waitForBytesWritten(5000)) {
qDebug() << "Timeout waiting for bytes to be written";
return false;
}
return waitForResponse(socket);
}
bool BackendImpl::SendEmail(const std::string& recipient,
const std::string& subject,
const std::string& body) {
return SendEmailViaCurl(recipient, subject, body);
}
bool BackendImpl::SendEmailViaCurl(const std::string& recipient,
const std::string& subject,
const std::string& body) {
QSslSocket *socket = new QSslSocket();
// 设置SSL配置
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setProtocol(QSsl::TlsV1_2OrLater);
socket->setSslConfiguration(sslConfig);
// 连接信号来监控状态和调试
QObject::connect(socket, &QSslSocket::connected, []() {
qDebug() << "Connected to SMTP server";
});
QObject::connect(socket, &QSslSocket::encrypted, []() {
qDebug() << "SSL encryption established";
});
QObject::connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors),
[](const QList<QSslError> &errors) {
qDebug() << "SSL errors occurred:";
for (const QSslError &error : errors) {
qDebug() << " - " << error.errorString();
}
});
QString smtpServer = QString::fromStdString(email_config_.smtp_server);
QString username = QString::fromStdString(email_config_.username);
QString password = QString::fromStdString(email_config_.password);
QString recipientQStr = QString::fromStdString(recipient);
qDebug() << "Attempting to connect to:" << smtpServer << "on port 465";
// 连接服务器
socket->connectToHostEncrypted(smtpServer, 465);
if (!socket->waitForConnected(10000)) {
qDebug() << "Connection failed:" << socket->errorString();
delete socket;
return false;
}
if (!socket->waitForEncrypted(10000)) {
qDebug() << "SSL handshake failed:" << socket->errorString();
delete socket;
return false;
}
// 等待并读取服务器初始响应
if (!waitForResponse(socket)) {
qDebug() << "Failed to get initial response from server";
delete socket;
return false;
}
// SMTP协议流程
// 1. EHLO命令
if (!sendCommand(socket, "EHLO localhost\r\n")) {
delete socket;
return false;
}
// 2. AUTH LOGIN
if (!sendCommand(socket, "AUTH LOGIN\r\n")) {
delete socket;
return false;
}
// 3. 发送Base64编码的用户名
if (!sendCommand(socket, username.toUtf8().toBase64() + "\r\n")) {
delete socket;
return false;
}
// 4. 发送Base64编码的密码
if (!sendCommand(socket, password.toUtf8().toBase64() + "\r\n")) {
delete socket;
return false;
}
// 5. MAIL FROM命令
if (!sendCommand(socket, QString("MAIL FROM: <%1>\r\n").arg(username).toUtf8())) {
delete socket;
return false;
}
// 6. RCPT TO命令
if (!sendCommand(socket, QString("RCPT TO: <%1>\r\n").arg(recipientQStr).toUtf8())) {
delete socket;
return false;
}
// 7. DATA命令
if (!sendCommand(socket, "DATA\r\n")) {
delete socket;
return false;
}
// 8. 发送邮件内容(分块发送,避免超时)
QString emailData = BuildEmailPayload(recipient, subject, body);
QByteArray emailBytes = emailData.toUtf8();
// 分块发送邮件内容
int chunkSize = 1024; // 每次发送1KB
for (int i = 0; i < emailBytes.size(); i += chunkSize) {
QByteArray chunk = emailBytes.mid(i, chunkSize);
socket->write(chunk);
if (!socket->waitForBytesWritten(5000)) {
qDebug() << "Timeout writing email data chunk";
delete socket;
return false;
}
qDebug() << "Sent chunk:" << i << "of" << emailBytes.size();
}
// 发送结束标记
if (!sendCommand(socket, "\r\n.\r\n")) {
delete socket;
return false;
}
// 9. QUIT命令
sendCommand(socket, "QUIT\r\n");
qDebug() << "Email sent successfully!";
socket->disconnectFromHost();
if (socket->state() != QAbstractSocket::UnconnectedState) {
socket->waitForDisconnected(5000);
}
delete socket;
return true;
}
QString BackendImpl::BuildEmailPayload(const std::string& recipient,
const std::string& subject,
const std::string& body) {
QString from = QString::fromStdString(email_config_.username);
QString to = QString::fromStdString(recipient);
QString subjectQStr = QString::fromStdString(subject);
QString bodyQStr = QString::fromStdString(body);
QString payload;
// 邮件头
payload += QString("From: %1\r\n").arg(from);
payload += QString("To: %1\r\n").arg(to);
payload += QString("Subject: %1\r\n").arg(subjectQStr);
payload += "Date: " + QDateTime::currentDateTime().toString(Qt::RFC2822Date) + "\r\n";
payload += "MIME-Version: 1.0\r\n";
payload += "Content-Type: text/plain; charset=\"utf-8\"\r\n";
payload += "Content-Transfer-Encoding: base64\r\n"; // 使用base64编码确保中文字符正确
payload += "\r\n"; // 空行分隔头部和正文
// 邮件正文使用base64编码
QByteArray bodyUtf8 = bodyQStr.toUtf8();
payload += bodyUtf8.toBase64();
payload += "\r\n"; // 确保正文以CRLF结束
qDebug() << "Built email payload, total size:" << payload.toUtf8().size() << "bytes";
return payload;
}
// 生成消息ID
std::string BackendImpl::GenerateMessageId() {
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()).count();
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(1000, 9999);
return "math_exam_" + std::to_string(timestamp) + "_" + std::to_string(dis(gen));
}
// 获取当前日期时间字符串
std::string BackendImpl::GetCurrentDateTime() {
std::time_t now = std::time(nullptr);
std::tm* tm_info = std::localtime(&now);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S %z", tm_info);
return std::string(buffer);
}
bool BackendImpl::UserRegister(const std::string& username,
const std::string& password,
const std::string& email,
@ -138,7 +397,7 @@ bool BackendImpl::UserLogin(const std::string& username,
Logger::Log(Logger::Level::INFO, "用户登录成功: " + username);
return true;
} else {
Logger::Log(Logger::Level::ERROR,
Logger::Log(Logger::Level::ERROR,
"用户登录失败: " + username + ", 错误: " + response.error_message);
return false;
}
@ -147,10 +406,10 @@ bool BackendImpl::UserLogin(const std::string& username,
void BackendImpl::UserLogout() {
if (!current_user_.empty()) {
exam_system_->Logout(current_user_);
Logger::Log(Logger::Level::INFO, "用户登出: " + current_user_);
current_user_.clear();
user_questions_.erase(current_user_);
current_correct_answers_.clear();
Logger::Log(Logger::Level::INFO, "用户登出: " + current_user_);
}
}
@ -178,7 +437,7 @@ bool BackendImpl::ChangePassword(const std::string& old_password,
Logger::Log(Logger::Level::INFO, "密码修改成功: " + current_user_);
return true;
} else {
Logger::Log(Logger::Level::ERROR,
Logger::Log(Logger::Level::ERROR,
"密码修改失败: " + current_user_ + ", 错误: " + response.error_message);
return false;
}
@ -192,7 +451,11 @@ void BackendImpl::UserExit() {
std::vector<QuestionInfo> BackendImpl::GenerateQuestions(
const std::string& difficulty, int count) {
std::vector<QuestionInfo> questions;
std::map<std::string,std::string> difficulty_map = {
{"primary", "小学"},
{"junior", "初中"},
{"senior", "高中"}
};
if (current_user_.empty()) {
Logger::Log(Logger::Level::ERROR, "用户未登录,无法生成题目");
return questions;
@ -201,13 +464,13 @@ std::vector<QuestionInfo> BackendImpl::GenerateQuestions(
// 使用ExamSystem生成题目
GenerateRequest request;
request.username = current_user_;
request.difficulty = difficulty;
request.difficulty = difficulty_map[difficulty];
request.count = count;
GenerateResponse response = exam_system_->GenerateProblems(request);
if (!response.success) {
Logger::Log(Logger::Level::ERROR,
Logger::Log(Logger::Level::ERROR,
"生成题目失败: " + response.error_message);
return questions;
}
@ -282,9 +545,9 @@ exam_system::TestResult BackendImpl::SubmitAnswers(const std::vector<int>& user_
result.total_questions = questions.size();
if (user_answers.size() != questions.size()) {
Logger::Log(Logger::Level::ERROR,
"答案数量与题目数量不匹配: " +
std::to_string(user_answers.size()) + " vs " +
Logger::Log(Logger::Level::ERROR,
"答案数量与题目数量不匹配: " +
std::to_string(user_answers.size()) + " vs " +
std::to_string(questions.size()));
return result;
}
@ -320,104 +583,7 @@ std::string BackendImpl::GetCurrentUser() const {
return current_user_;
}
// ==================== 私有方法实现 ====================
bool BackendImpl::SendEmail(const std::string& recipient,
const std::string& subject,
const std::string& body) {
return true;
if (email_config_.use_curl) {
return SendEmailViaCurl(recipient, subject, body);
} else {
// 备用邮件发送方法
Logger::Log(Logger::Level::ERROR, "仅支持libcurl邮件发送");
return false;
}
}
bool BackendImpl::SendEmailViaCurl(const std::string& recipient,
const std::string& subject,
const std::string& body) {
// CURL* curl;
// CURLcode res = CURLE_OK;
// struct curl_slist* recipients = nullptr;
// std::string response_string;
// curl = curl_easy_init();
// if (!curl) {
// Logger::Log(Logger::Level::ERROR, "libcurl初始化失败");
// return false;
// }
// // 设置SMTP服务器
// curl_easy_setopt(curl, CURLOPT_URL, ("smtp://" + email_config_.smtp_server + ":" + std::to_string(email_config_.smtp_port)).c_str());
// // 设置用户名和密码
// curl_easy_setopt(curl, CURLOPT_USERNAME, email_config_.username.c_str());
// curl_easy_setopt(curl, CURLOPT_PASSWORD, email_config_.password.c_str());
// // 设置发件人和收件人
// curl_easy_setopt(curl, CURLOPT_MAIL_FROM, email_config_.username.c_str());
// recipients = curl_slist_append(recipients, recipient.c_str());
// curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
// // 设置邮件内容
// std::string email_data =
// "To: " + recipient + "\r\n"
// "From: " + email_config_.username + "\r\n"
// "Subject: " + subject + "\r\n"
// "\r\n" + body + "\r\n";
// curl_easy_setopt(curl, CURLOPT_READFUNCTION, [](char* buffer, size_t size, size_t nitems, void* instream) -> size_t {
// std::string* email_data = static_cast<std::string*>(instream);
// size_t buffer_size = size * nitems;
// if (email_data->empty()) {
// return 0;
// }
// size_t copy_size = std::min(buffer_size, email_data->size());
// memcpy(buffer, email_data->c_str(), copy_size);
// email_data->erase(0, copy_size);
// return copy_size;
// });
// curl_easy_setopt(curl, CURLOPT_READDATA, &email_data);
// curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
// // 启用TLS
// curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
// // 设置服务器证书验证生产环境应该设为1
// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// // 设置响应回调
// curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
// curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
// // 设置超时
// curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// // 发送邮件
// res = curl_easy_perform(curl);
// // 清理
// curl_slist_free_all(recipients);
// curl_easy_cleanup(curl);
// if (res != CURLE_OK) {
// Logger::Log(Logger::Level::ERROR,
// "邮件发送失败: " + std::string(curl_easy_strerror(res)));
// return false;
// }
// Logger::Log(Logger::Level::INFO, "邮件发送成功: " + recipient);
return true;
}
// 验证码相关方法保持不变
std::string BackendImpl::GenerateVerificationCode() {
std::random_device rd;
std::mt19937 gen(rd());
@ -466,7 +632,7 @@ void BackendImpl::CleanExpiredVerificationCodes() {
for (const auto& email : expired_emails) {
verification_codes_.erase(email);
Logger::Log(Logger::Level::INFO, "清理过期验证码: " + email);
Logger::Log(Logger::Level::WARNING, "清理过期验证码: " + email);
}
}
@ -525,4 +691,4 @@ double BackendImpl::CalculateScore(int correct_count, int total_count) {
return (static_cast<double>(correct_count) / total_count) * 100.0;
}
} // namespace exam_system
} // namespace exam_system

@ -7,6 +7,7 @@
#include <map>
#include <ctime>
#include "../backend/exam_system/exam_system.h"
#include <QString>
namespace exam_system {
@ -35,11 +36,10 @@ struct VerificationCode {
// 邮箱配置结构体
struct EmailConfig {
std::string smtp_server = "smtp.163.com";
int smtp_port = 25;
std::string username = "your_email@163.com";
std::string password = "your_password";
bool use_curl = true; // 使用libcurl发送邮件
std::string smtp_server = "smtp.qq.com";
int smtp_port = 465; // 默认使用465端口
std::string username = "3315384591@qq.com";
std::string password = "jobgsqxruxtgdaig";
};
// 前后端通信接口
@ -67,7 +67,7 @@ class BackendInterface {
class BackendImpl : public BackendInterface {
public:
BackendImpl();
~BackendImpl() override = default;
~BackendImpl() override;
BackendImpl(const BackendImpl&) = delete;
BackendImpl& operator=(const BackendImpl&) = delete;
@ -97,6 +97,12 @@ class BackendImpl : public BackendInterface {
const std::string& subject,
const std::string& body);
// 邮件内容生成
QString BuildEmailPayload(const std::string& recipient,const std::string& subject,const std::string& body);
std::string GenerateMessageId();
std::string GetCurrentDateTime();
// 验证码管理
std::string GenerateVerificationCode();
bool ValidateVerificationCode(const std::string& email,
@ -108,6 +114,9 @@ class BackendImpl : public BackendInterface {
std::vector<std::string> GenerateOptions(const std::string& correct_answer);
double CalculateScore(int correct_count, int total_count);
// 静态回调函数
static size_t PayloadSource(void* ptr, size_t size, size_t nmemb, void* userp);
// 成员变量
std::map<std::string, VerificationCode> verification_codes_;
std::map<std::string, std::vector<QuestionInfo>> user_questions_;
@ -119,4 +128,4 @@ class BackendImpl : public BackendInterface {
} // namespace exam_system
#endif // BACKEND_INTERFACE_H
#endif // BACKEND_INTERFACE_H

Loading…
Cancel
Save