From a008aab34c5f2066403cba317ed0f1ff46bdfe0f Mon Sep 17 00:00:00 2001 From: HuQiQiang <3315384591@qq.com> Date: Mon, 13 Oct 2025 13:06:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=95=B4=E5=AE=9E=E7=8E=B0=E9=82=AE?= =?UTF-8?q?=E4=BB=B6=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/backend_interface.cc | 414 ++++++++++++++++++++++---------- src/shared/backend_interface.h | 23 +- 2 files changed, 306 insertions(+), 131 deletions(-) diff --git a/src/shared/backend_interface.cc b/src/shared/backend_interface.cc index d9b8d01..113a904 100644 --- a/src/shared/backend_interface.cc +++ b/src/shared/backend_interface.cc @@ -1,10 +1,22 @@ #include "backend_interface.h" +#include #include #include #include #include -#include -#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include // 如果需要生成UUID namespace exam_system { @@ -26,26 +38,25 @@ BackendImpl::BackendImpl() { // 初始化 ExamSystem exam_system_ = std::make_unique(); - // 配置邮箱设置 - 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); + // 初始化libcurl + // curl_global_init(CURL_GLOBAL_DEFAULT); Logger::Log(Logger::Level::INFO, "BackendImpl初始化完成"); } BackendImpl::~BackendImpl() { - curl_global_cleanup(); + // 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 &>::of(&QSslSocket::sslErrors), + [](const QList &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( + now.time_since_epoch()).count(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution 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 BackendImpl::GenerateQuestions( const std::string& difficulty, int count) { std::vector questions; - + std::map difficulty_map = { + {"primary", "小学"}, + {"junior", "初中"}, + {"senior", "高中"} + }; if (current_user_.empty()) { Logger::Log(Logger::Level::ERROR, "用户未登录,无法生成题目"); return questions; @@ -201,13 +464,13 @@ std::vector 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& 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(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(correct_count) / total_count) * 100.0; } -} // namespace exam_system \ No newline at end of file +} // namespace exam_system diff --git a/src/shared/backend_interface.h b/src/shared/backend_interface.h index 6ae3b65..91877c2 100644 --- a/src/shared/backend_interface.h +++ b/src/shared/backend_interface.h @@ -7,6 +7,7 @@ #include #include #include "../backend/exam_system/exam_system.h" +#include 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 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 verification_codes_; std::map> user_questions_; @@ -119,4 +128,4 @@ class BackendImpl : public BackendInterface { } // namespace exam_system -#endif // BACKEND_INTERFACE_H \ No newline at end of file +#endif // BACKEND_INTERFACE_H