#pragma once #include #include #include #include #include #include // for memset #include #include #include #include #include #include class SendEmail { // 请确保在链接器设置中加入 libssl.lib 与 libcrypto.lib(或通过 vcpkg 自动集成) public: using ptr = std::shared_ptr; // 辅助函数:通过 SSL 发送数据,并检查返回值 bool sendSSL(SSL* ssl, const std::string& msg, const char* label) { int bytesSent = SSL_write(ssl, msg.c_str(), static_cast(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(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; } /// /// 发送邮件到指定邮箱 /// /// /// 成功 = true,失败 = false 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:\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(std::time(nullptr))); std::uniform_int_distribution distribution(10000, 99999); std::string verificationCode = std::to_string(distribution(engine)); _verifyCode = verificationCode; // 13. 构造邮件头和正文 sendMsg = "From: \"Mysterious系统\" \r\n" "Reply-To: \"请勿回复\" \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; };