This commit is contained in:
2025-10-13 18:34:48 +08:00
commit 37865d041f
116 changed files with 31168 additions and 0 deletions

262
common/sendemail.hpp Normal file
View File

@ -0,0 +1,262 @@
#pragma once
#include <iostream>
#include <string>
#include <chrono>
#include <random>
#include <ctime>
#include <cstring> // for memset
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/buffer.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
class SendEmail
{
// 请确保在链接器设置中加入 libssl.lib 与 libcrypto.lib或通过 vcpkg 自动集成)
public:
using ptr = std::shared_ptr<SendEmail>;
// 辅助函数:通过 SSL 发送数据,并检查返回值
bool sendSSL(SSL* ssl, const std::string& msg, const char* label)
{
int bytesSent = SSL_write(ssl, msg.c_str(), static_cast<int>(msg.length()));
if (bytesSent <= 0) {
std::cerr << "Failed to send " << label << " message via SSL." << std::endl;
ERR_print_errors_fp(stderr);
return false;
}
return true;
}
std::string base64_encode(const std::string& input)
{
BIO* bmem = BIO_new(BIO_s_mem());
BIO* b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64 = BIO_push(b64, bmem);
BIO_write(b64, input.c_str(), static_cast<int>(input.length()));
BIO_flush(b64);
BUF_MEM* bptr;
BIO_get_mem_ptr(b64, &bptr);
std::string result(bptr->data, bptr->length);
BIO_free_all(b64);
return result;
}
// 辅助函数:通过 SSL 接收数据,并打印输出
bool recvSSL(SSL* ssl, char* buff, int buffSize, const char* label)
{
int bytesReceived = SSL_read(ssl, buff, buffSize);
if (bytesReceived <= 0) {
std::cerr << "Failed to receive " << label << " message via SSL." << std::endl;
ERR_print_errors_fp(stderr);
return false;
}
buff[bytesReceived] = '\0';
std::cout << label << " response: " << buff << std::endl;
return true;
}
/// <summary>
/// 发送邮件到指定邮箱
/// </summary>
/// <param name="收件人邮箱"></param>
/// <returns>成功 = true失败 = false</returns>
bool SEND_email(const std::string& email)
{
// 1. DNS解析 smtp.qq.com使用端口 465
struct addrinfo hints = {};
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
struct addrinfo* addrResult = nullptr;
int ret = getaddrinfo("smtp.qq.com", "465", &hints, &addrResult);
if (ret != 0 || addrResult == nullptr) {
std::cerr << "DNS resolution failed: " << gai_strerror(ret) << std::endl;
return false;
}
// 2. 建立 TCP 连接
int sock = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
if (sock == -1) {
std::cerr << "Socket creation failed!" << std::endl;
freeaddrinfo(addrResult);
return false;
}
if (connect(sock, addrResult->ai_addr, addrResult->ai_addrlen) != 0) {
std::cerr << "Connection failed!" << std::endl;
close(sock);
freeaddrinfo(addrResult);
return false;
}
freeaddrinfo(addrResult);
// 3. 初始化 OpenSSL
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
const SSL_METHOD* method = TLS_client_method();
SSL_CTX* sslCtx = SSL_CTX_new(method);
if (!sslCtx) {
std::cerr << "Unable to create SSL context." << std::endl;
close(sock);
return false;
}
SSL* ssl = SSL_new(sslCtx);
if (!ssl) {
std::cerr << "Unable to create SSL structure." << std::endl;
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 绑定 socket 到 SSL
SSL_set_fd(ssl, sock);
if (SSL_connect(ssl) <= 0) {
std::cerr << "SSL_connect failed." << std::endl;
ERR_print_errors_fp(stderr);
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 4. 读取服务器欢迎信息
char buff[2048] = { 0 };
if (!recvSSL(ssl, buff, sizeof(buff) - 1, "Server")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 5. 发送 EHLO 命令
std::string sendMsg = "EHLO localhost\r\n";
if (!sendSSL(ssl, sendMsg, "EHLO") || !recvSSL(ssl, buff, sizeof(buff) - 1, "EHLO")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 6. 认证流程AUTH LOGIN
sendMsg = "AUTH LOGIN\r\n";
if (!sendSSL(ssl, sendMsg, "AUTH") || !recvSSL(ssl, buff, sizeof(buff) - 1, "AUTH")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 7. 发送用户名Base64编码后的
std::string username = base64_encode("zxiao_xin@qq.com") + "\r\n";
if (!sendSSL(ssl, username, "Username") || !recvSSL(ssl, buff, sizeof(buff) - 1, "Username")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 8. 发送密码Base64编码后的
std::string password = base64_encode("ydfslvabdryvejai") + "\r\n";
if (!sendSSL(ssl, password, "Password") || !recvSSL(ssl, buff, sizeof(buff) - 1, "Password")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 9. 设置发件人
sendMsg = "MAIL FROM:<zxiao_xin@qq.com>\r\n";
if (!sendSSL(ssl, sendMsg, "MAIL FROM") || !recvSSL(ssl, buff, sizeof(buff) - 1, "MAIL FROM")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 10. 设置收件人
sendMsg = "RCPT TO:<" + email + ">\r\n";
if (!sendSSL(ssl, sendMsg, "RCPT TO") || !recvSSL(ssl, buff, sizeof(buff) - 1, "RCPT TO")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 检查回复是否为成功代码
std::string s = buff;
s = s.substr(0, 3);
if (s != "250") {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 11. 命令 DATA
sendMsg = "DATA\r\n";
if (!sendSSL(ssl, sendMsg, "DATA") || !recvSSL(ssl, buff, sizeof(buff) - 1, "DATA")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 12. 生成验证码
std::default_random_engine engine(static_cast<unsigned int>(std::time(nullptr)));
std::uniform_int_distribution<int> distribution(10000, 99999);
std::string verificationCode = std::to_string(distribution(engine));
_verifyCode = verificationCode;
// 13. 构造邮件头和正文
sendMsg =
"From: \"Mysterious系统\" <zxiao_xin@qq.com>\r\n"
"Reply-To: \"请勿回复\" <no-reply@qq.com>\r\n"
"To: <" + email + ">\r\n"
"Subject: Mysterious验证码\r\n"
"Content-Type: text/plain; charset=UTF-8\r\n"
"\r\n"
"这是您的验证码: " + verificationCode + "\r\n"
"用于身份验证,请勿泄露。如非本人操作,请忽略此邮件。\r\n"
"\r\n.\r\n";
if (!sendSSL(ssl, sendMsg, "Email DATA") || !recvSSL(ssl, buff, sizeof(buff) - 1, "DATA send")) {
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return false;
}
// 14. 命令 QUIT
sendMsg = "QUIT\r\n";
sendSSL(ssl, sendMsg, "QUIT");
recvSSL(ssl, buff, sizeof(buff) - 1, "QUIT");
// 15. 清理资源
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(sslCtx);
close(sock);
return true;
}
std::string getVerifyCode() {
return _verifyCode;
}
private:
// std::string _email;
std::string _verifyCode;
};