mirror of
https://gitee.com/Zhaoxin59/my-chat_-server.git
synced 2026-02-14 01:21:50 +08:00
263 lines
7.5 KiB
C++
263 lines
7.5 KiB
C++
#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;
|
||
};
|
||
|