From 6098811be83e7cd41ba4eafdfba23ea1a7adabf1 Mon Sep 17 00:00:00 2001 From: ParticleG Date: Sun, 5 Feb 2023 11:35:03 +0000 Subject: [PATCH] - Fix [26F-Studio/Techmino#845](https://github.com/26F-Studio/Techmino/issues/845) --- CMakeLists.txt | 2 + plugins/EmailManager.cc | 314 +++++---------------------------------- plugins/EmailManager.h | 69 +-------- plugins/PlayerManager.cc | 13 +- vcpkg | 2 +- vcpkg.json | 1 + 6 files changed, 54 insertions(+), 347 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ce2a25..1d3a4a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(${PROJECT_NAME} main.cc types/Products.h) find_package(cryptopp CONFIG REQUIRED) find_package(Drogon CONFIG REQUIRED) find_package(magic_enum CONFIG REQUIRED) +find_package(mailio CONFIG REQUIRED) find_package(range-v3 CONFIG REQUIRED) target_link_libraries(${PROJECT_NAME} @@ -36,6 +37,7 @@ target_link_libraries(${PROJECT_NAME} cryptopp::cryptopp Drogon::Drogon magic_enum::magic_enum + mailio range-v3 range-v3-meta range-v3::meta diff --git a/plugins/EmailManager.cc b/plugins/EmailManager.cc index f668642..6a32aa4 100644 --- a/plugins/EmailManager.cc +++ b/plugins/EmailManager.cc @@ -4,227 +4,19 @@ #include #include +#include #include -#include #include using namespace drogon; +using namespace mailio; using namespace magic_enum; using namespace std; -using namespace trantor; +using namespace studio26f::helpers; using namespace studio26f::plugins; using namespace studio26f::structures; using namespace studio26f::types; -void EmailManager::messageHandler( - const TcpConnectionPtr &connPtr, - MsgBuffer *msgBuffer, - const shared_ptr &email, - const function &callback -) { - std::string receivedMsg; - while (msgBuffer->readableBytes() > 0) { - string buf(msgBuffer->peek(), msgBuffer->readableBytes()); - receivedMsg.append(buf); - msgBuffer->retrieveAll(); - } - LOG_TRACE << "receive: " << receivedMsg; - uint32_t responseCode = stoul(receivedMsg.substr(0, 3)); - - if (email->state == EmailState::Close) { - callback(true, receivedMsg); - connPtr->forceClose(); - return; - } - try { - switch (responseCode) { - case 220: - switch (email->state) { - case EmailState::Init: { - string outMsg("EHLO smtpclient.qw\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::HandShake; - break; - } - case EmailState::HandShake: { - string outMsg("EHLO smtpclient.qw\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->startClientEncryption([connPtr, out]() { - LOG_TRACE << "SSL established"; - connPtr->send(out); - }, false, false); - - email->state = EmailState::Auth; - break; - } - default: - throw EmailException("Unsupported state"); - } - break; - case 235: - switch (email->state) { - case EmailState::Mail: { - string outMsg("MAIL FROM:<" + email->_senderEmail + ">\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Recipient; - break; - } - default: - throw EmailException("Unsupported state"); - } - break; - case 250: - switch (email->state) { - case EmailState::HandShake: { - string outMsg("STARTTLS\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::HandShake; - break; - } - case EmailState::Auth: { - string outMsg("AUTH LOGIN\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::User; - break; - } - case EmailState::Recipient: { - string outMsg("RCPT TO:<" + email->_receiverEmail + ">\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Data; - break; - } - case EmailState::Data: { - string outMsg("DATA\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Body; - break; - } - case EmailState::Quit: { - string outMsg("QUIT\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - email->state = EmailState::Close; - break; - } - default: - throw EmailException("Unsupported state"); - } - break; - case 334: - switch (email->state) { - case EmailState::User: { - string outMsg(drogon::utils::base64Encode( - reinterpret_cast(email->_account.c_str()), - email->_account.length() - ) + "\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Pass; - break; - } - case EmailState::Pass: { - string outMsg(drogon::utils::base64Encode( - reinterpret_cast(email->_password.c_str()), - email->_password.length() - ) + "\r\n"); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Mail; - break; - } - default: - throw EmailException("Unsupported state"); - } - break; - case 354: - switch (email->state) { - case EmailState::Body: { - string outMsg( - "To: " + email->_receiverEmail + "\r\n" + - "From: " + email->_senderEmail + "\r\n" - ); - if (email->isHTML) { - outMsg.append("Content-Type: text/html;\r\n"); - } - outMsg.append( - "Subject: " + email->_subject + "\r\n\r\n" + - email->_content + "\r\n.\r\n" - ); - MsgBuffer out; - out.append(outMsg.data(), outMsg.size()); - - connPtr->send(std::move(out)); - - email->state = EmailState::Quit; - break; - } - default: - throw EmailException("Unsupported state"); - } - break; - default: - throw EmailException("Unsupported state"); - } - } catch (const EmailException &e) { - email->state = EmailState::Close; - callback(false, receivedMsg); - } -} - -EmailManager::Email::Email( - std::string account, - std::string password, - std::string senderEmail, - std::string senderName, - string receiverEmail, - string subject, - string content, - bool isHTML, - std::shared_ptr socket -) : _account(std::move(account)), - _password(std::move(password)), - _senderEmail(std::move(senderEmail)), - _senderName(std::move(senderName)), - _receiverEmail(std::move(receiverEmail)), - _subject(std::move(subject)), - _content(std::move(content)), - isHTML(isHTML), - _socket(std::move(socket)) {} - void EmailManager::initAndStart(const Json::Value &config) { if (!( config["server"].isString() && @@ -254,68 +46,44 @@ void EmailManager::shutdown() { void EmailManager::smtp( const string &receiverEmail, const string &subject, - const string &content, - bool isHTML, - const function &callback -) noexcept { - const auto resolver = app().getResolver(); - resolver->resolve(_server, [=, this](const InetAddress &resolvedAddr) { - const auto threadNum = app().getThreadNum(); - EventLoop *ioLoop; - for (size_t threadIndex = 0; threadIndex < threadNum; ++threadIndex) { - ioLoop = app().getIOLoop(threadIndex); - if (ioLoop == nullptr || ioLoop->isInLoopThread()) { - continue; - } else { - break; - } - } - InetAddress smtpAddress(resolvedAddr.toIp(), _port, false); - const auto emailUuid = drogon::utils::getUuid(); - const auto tcpSocket = make_shared(ioLoop, smtpAddress, "SMTPMail"); - _emailMap.emplace(emailUuid, make_shared( - _account, - _password, - _senderEmail, - _senderName, - receiverEmail, - subject, - content, - isHTML, - tcpSocket - )); - tcpSocket->setConnectionCallback([emailUuid, this](const TcpConnectionPtr &connPtr) { - if (connPtr->connected()) { - LOG_TRACE << "Connection established!"; - } else { - LOG_TRACE << "Connection disconnect"; - _emailMap.erase(emailUuid); - } - }); - tcpSocket->setConnectionErrorCallback([emailUuid, this]() { - LOG_ERROR << "Bad Server address"; - _emailMap.erase(emailUuid); - }); - tcpSocket->setMessageCallback( - [emailUuid, callback, this](const TcpConnectionPtr &connPtr, MsgBuffer *msg) { - messageHandler(connPtr, msg, _emailMap[emailUuid], callback); - } - ); - tcpSocket->connect(); - }); -} - -pair EmailManager::smtp( - const string &receiverEmail, - const string &subject, - const string &content, - bool isHTML + const string &content ) { - promise> resultPromise; - auto resultFuture = resultPromise.get_future(); - smtp(receiverEmail, subject, content, isHTML, [&resultPromise](bool success, const string &msg) { - resultPromise.set_value({success, msg}); - }); - return resultFuture.get(); + try { + message msg; + msg.header_codec(message::header_codec_t::BASE64); + msg.from({_senderName, _senderEmail}); + msg.add_recipient({_senderName, receiverEmail}); + msg.subject(subject); + msg.content_transfer_encoding(mime::content_transfer_encoding_t::BINARY); + msg.content_type(message::media_type_t::TEXT, "html", "utf-8"); + msg.content(content); + smtps conn(_server, _port); + conn.authenticate(_account, _password, smtps::auth_method_t::START_TLS); + conn.submit(msg); + } catch (smtp_error &e) { + LOG_WARN << "SMTP Error: " << e.what(); + throw ResponseException( + i18n("emailError"), + e, + ResultCode::EmailError, + k503ServiceUnavailable + ); + } catch (dialog_error &e) { + LOG_WARN << "Dialog Error: " << e.what(); + throw ResponseException( + i18n("emailError"), + e, + ResultCode::EmailError, + k503ServiceUnavailable + ); + } catch (message_error &e) { + LOG_WARN << "Message Error: " << e.what(); + throw ResponseException( + i18n("emailError"), + e, + ResultCode::EmailError, + k503ServiceUnavailable + ); + } } diff --git a/plugins/EmailManager.h b/plugins/EmailManager.h index 5a86521..1881c3f 100644 --- a/plugins/EmailManager.h +++ b/plugins/EmailManager.h @@ -5,52 +5,14 @@ #pragma once #include -#include +#include namespace studio26f::plugins { - class EmailManager : public drogon::Plugin { - private: - enum class EmailState { - Init, - HandShake, - Tls [[maybe_unused]], - Auth, - User, - Pass, - Mail, - Recipient, - Data, - Body, - Quit, - Close - }; - - struct Email { - const std::string _account, - _password, - _senderEmail, - _senderName, - _receiverEmail, - _subject, - _content; - std::atomic isHTML{false}; - std::atomic state{EmailState::Init}; - std::shared_ptr _socket; - - Email( - std::string account, - std::string password, - std::string senderEmail, - std::string senderName, - std::string receiverEmail, - std::string subject, - std::string content, - bool isHTML, - std::shared_ptr socket - ); - - ~Email() = default; - }; + class EmailManager : + public drogon::Plugin, + public helpers::I18nHelper { + public: + static constexpr char projectName[] = CMAKE_PROJECT_NAME; public: void initAndStart(const Json::Value &config) override; @@ -60,29 +22,12 @@ namespace studio26f::plugins { void smtp( const std::string &receiverEmail, const std::string &subject, - const std::string &content, - bool isHTML, - const std::function &callback - ) noexcept; - - std::pair smtp( - const std::string &receiverEmail, - const std::string &subject, - const std::string &content, - bool isHTML + const std::string &content ); private: std::string _server; uint32_t _port; std::string _account, _password, _senderEmail, _senderName; - std::unordered_map> _emailMap; - - static void messageHandler( - const trantor::TcpConnectionPtr &connPtr, - trantor::MsgBuffer *msgBuffer, - const std::shared_ptr &email, - const std::function &callback - ); }; } diff --git a/plugins/PlayerManager.cc b/plugins/PlayerManager.cc index 6bdd9f6..36fb441 100644 --- a/plugins/PlayerManager.cc +++ b/plugins/PlayerManager.cc @@ -218,20 +218,11 @@ void PlayerManager::verifyEmail(const string &email) { code ); mailContent = regex_replace(mailContent, regex{R"((\s*[\r\n]+\s*|\s+))"}, " "); - const auto [result, receivedMessage] = app().getPlugin()->smtp( + app().getPlugin()->smtp( email, "[26F Studio] Verification Code/验证码", - mailContent, - true + mailContent ); - if (!result) { - throw ResponseException( - i18n("emailSendError"), - internal::BaseException(receivedMessage), - ResultCode::EmailError, - k500InternalServerError - ); - } } string PlayerManager::seedEmail(const string &email) { diff --git a/vcpkg b/vcpkg index a0cd246..0ba60bf 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit a0cd246334a33107cdef4f701b0b532a71514b19 +Subproject commit 0ba60bfef5dea4cb2599daa7ad8364e309835a68 diff --git a/vcpkg.json b/vcpkg.json index faf7b8d..8e6221a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -20,6 +20,7 @@ "redis" ] }, + "mailio", "magic-enum", "range-v3" ]