|
|
|
|
@ -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
|
|
|
|
|
|