react-native-email-imap-smtp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EmailImapSmtp.podspec +29 -0
- package/LICENSE +20 -0
- package/README.md +121 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +117 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/emailimapsmtp/EmailImapSmtp.kt +1533 -0
- package/android/src/main/java/com/margelo/nitro/emailimapsmtp/EmailImapSmtpPackage.kt +22 -0
- package/ios/EmailImapSmtp.swift +5 -0
- package/lib/module/EmailImapSmtp.nitro.js +4 -0
- package/lib/module/EmailImapSmtp.nitro.js.map +1 -0
- package/lib/module/NativeEmailImapSmtp.js +14 -0
- package/lib/module/NativeEmailImapSmtp.js.map +1 -0
- package/lib/module/index.js +64 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native.js +227 -0
- package/lib/module/native.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/web.js +107 -0
- package/lib/module/web.js.map +1 -0
- package/lib/module/windows.js +156 -0
- package/lib/module/windows.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EmailImapSmtp.nitro.d.ts +268 -0
- package/lib/typescript/src/EmailImapSmtp.nitro.d.ts.map +1 -0
- package/lib/typescript/src/NativeEmailImapSmtp.d.ts +36 -0
- package/lib/typescript/src/NativeEmailImapSmtp.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +33 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native.d.ts +32 -0
- package/lib/typescript/src/native.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +261 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/web.d.ts +32 -0
- package/lib/typescript/src/web.d.ts.map +1 -0
- package/lib/typescript/src/windows.d.ts +32 -0
- package/lib/typescript/src/windows.d.ts.map +1 -0
- package/nitro.json +23 -0
- package/nitrogen/generated/android/c++/JAppendResult.hpp +65 -0
- package/nitrogen/generated/android/c++/JAttachmentInfo.hpp +96 -0
- package/nitrogen/generated/android/c++/JConnectionConfig.hpp +94 -0
- package/nitrogen/generated/android/c++/JConnectionResult.hpp +73 -0
- package/nitrogen/generated/android/c++/JDeleteResult.hpp +87 -0
- package/nitrogen/generated/android/c++/JEmailAddress.hpp +61 -0
- package/nitrogen/generated/android/c++/JEmailBody.hpp +84 -0
- package/nitrogen/generated/android/c++/JEmailMessage.hpp +273 -0
- package/nitrogen/generated/android/c++/JFetchEmailsOptions.hpp +110 -0
- package/nitrogen/generated/android/c++/JFolderInfo.hpp +126 -0
- package/nitrogen/generated/android/c++/JFunc_void_IdleNotification.hpp +79 -0
- package/nitrogen/generated/android/c++/JHybridEmailImapSmtpSpec.cpp +697 -0
- package/nitrogen/generated/android/c++/JHybridEmailImapSmtpSpec.hpp +92 -0
- package/nitrogen/generated/android/c++/JIdleNotification.hpp +78 -0
- package/nitrogen/generated/android/c++/JImapServerInfo.hpp +84 -0
- package/nitrogen/generated/android/c++/JMailboxInfo.hpp +134 -0
- package/nitrogen/generated/android/c++/JMailboxListItem.hpp +92 -0
- package/nitrogen/generated/android/c++/JQuotaInfo.hpp +78 -0
- package/nitrogen/generated/android/c++/JSearchCriteria.hpp +187 -0
- package/nitrogen/generated/android/c++/JSearchHeader.hpp +61 -0
- package/nitrogen/generated/android/c++/JSendMailOptions.hpp +229 -0
- package/nitrogen/generated/android/c++/JSendMailResult.hpp +88 -0
- package/nitrogen/generated/android/c++/JSmtpAttachment.hpp +80 -0
- package/nitrogen/generated/android/c++/JSmtpConnectionConfig.hpp +98 -0
- package/nitrogen/generated/android/c++/JSmtpServerInfo.hpp +102 -0
- package/nitrogen/generated/android/c++/JVariant_ImapServerInfo_SmtpServerInfo.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_ImapServerInfo_SmtpServerInfo.hpp +73 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AttachmentInfo.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AttachmentInfo.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_EmailMessage.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_EmailMessage.hpp +83 -0
- package/nitrogen/generated/android/emailimapsmtp+autolinking.cmake +84 -0
- package/nitrogen/generated/android/emailimapsmtp+autolinking.gradle +27 -0
- package/nitrogen/generated/android/emailimapsmtpOnLoad.cpp +56 -0
- package/nitrogen/generated/android/emailimapsmtpOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/AppendResult.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/AttachmentInfo.kt +96 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionConfig.kt +96 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionResult.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/DeleteResult.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailAddress.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailBody.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailMessage.kt +156 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/FetchEmailsOptions.kt +116 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/FolderInfo.kt +91 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Func_void_IdleNotification.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/HybridEmailImapSmtpSpec.kt +178 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/IdleNotification.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ImapServerInfo.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/MailboxInfo.kt +101 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/MailboxListItem.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/QuotaInfo.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SearchCriteria.kt +196 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SearchHeader.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SendMailOptions.kt +126 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SendMailResult.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpAttachment.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpConnectionConfig.kt +101 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpServerInfo.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_ImapServerInfo_SmtpServerInfo.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_NullType_AttachmentInfo.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_NullType_EmailMessage.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/emailimapsmtpOnLoad.kt +35 -0
- package/nitrogen/generated/ios/EmailImapSmtp+autolinking.rb +62 -0
- package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Bridge.cpp +161 -0
- package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Bridge.hpp +1132 -0
- package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Umbrella.hpp +118 -0
- package/nitrogen/generated/ios/EmailImapSmtpAutolinking.mm +33 -0
- package/nitrogen/generated/ios/EmailImapSmtpAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridEmailImapSmtpSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridEmailImapSmtpSpecSwift.hpp +390 -0
- package/nitrogen/generated/ios/swift/AppendResult.swift +39 -0
- package/nitrogen/generated/ios/swift/AttachmentInfo.swift +113 -0
- package/nitrogen/generated/ios/swift/ConnectionConfig.swift +87 -0
- package/nitrogen/generated/ios/swift/ConnectionResult.swift +84 -0
- package/nitrogen/generated/ios/swift/DeleteResult.swift +64 -0
- package/nitrogen/generated/ios/swift/EmailAddress.swift +34 -0
- package/nitrogen/generated/ios/swift/EmailBody.swift +45 -0
- package/nitrogen/generated/ios/swift/EmailMessage.swift +184 -0
- package/nitrogen/generated/ios/swift/FetchEmailsOptions.swift +198 -0
- package/nitrogen/generated/ios/swift/FolderInfo.swift +81 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_AppendResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_ConnectionResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_DeleteResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_IdleNotification.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_MailboxInfo.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_QuotaInfo.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_SendMailResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__AttachmentInfo_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__EmailMessage_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_AttachmentInfo_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_EmailMessage_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_FolderInfo_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_MailboxListItem_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridEmailImapSmtpSpec.swift +84 -0
- package/nitrogen/generated/ios/swift/HybridEmailImapSmtpSpec_cxx.swift +759 -0
- package/nitrogen/generated/ios/swift/IdleNotification.swift +93 -0
- package/nitrogen/generated/ios/swift/ImapServerInfo.swift +45 -0
- package/nitrogen/generated/ios/swift/MailboxInfo.swift +91 -0
- package/nitrogen/generated/ios/swift/MailboxListItem.swift +55 -0
- package/nitrogen/generated/ios/swift/QuotaInfo.swift +80 -0
- package/nitrogen/generated/ios/swift/SearchCriteria.swift +485 -0
- package/nitrogen/generated/ios/swift/SearchHeader.swift +34 -0
- package/nitrogen/generated/ios/swift/SendMailOptions.swift +310 -0
- package/nitrogen/generated/ios/swift/SendMailResult.swift +155 -0
- package/nitrogen/generated/ios/swift/SmtpAttachment.swift +80 -0
- package/nitrogen/generated/ios/swift/SmtpConnectionConfig.swift +105 -0
- package/nitrogen/generated/ios/swift/SmtpServerInfo.swift +51 -0
- package/nitrogen/generated/ios/swift/Variant_ImapServerInfo_SmtpServerInfo.swift +30 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_AttachmentInfo.swift +30 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_EmailMessage.swift +30 -0
- package/nitrogen/generated/shared/c++/AppendResult.hpp +91 -0
- package/nitrogen/generated/shared/c++/AttachmentInfo.hpp +121 -0
- package/nitrogen/generated/shared/c++/ConnectionConfig.hpp +120 -0
- package/nitrogen/generated/shared/c++/ConnectionResult.hpp +98 -0
- package/nitrogen/generated/shared/c++/DeleteResult.hpp +93 -0
- package/nitrogen/generated/shared/c++/EmailAddress.hpp +87 -0
- package/nitrogen/generated/shared/c++/EmailBody.hpp +92 -0
- package/nitrogen/generated/shared/c++/EmailMessage.hpp +177 -0
- package/nitrogen/generated/shared/c++/FetchEmailsOptions.hpp +136 -0
- package/nitrogen/generated/shared/c++/FolderInfo.hpp +116 -0
- package/nitrogen/generated/shared/c++/HybridEmailImapSmtpSpec.cpp +50 -0
- package/nitrogen/generated/shared/c++/HybridEmailImapSmtpSpec.hpp +145 -0
- package/nitrogen/generated/shared/c++/IdleNotification.hpp +104 -0
- package/nitrogen/generated/shared/c++/ImapServerInfo.hpp +92 -0
- package/nitrogen/generated/shared/c++/MailboxInfo.hpp +124 -0
- package/nitrogen/generated/shared/c++/MailboxListItem.hpp +100 -0
- package/nitrogen/generated/shared/c++/QuotaInfo.hpp +104 -0
- package/nitrogen/generated/shared/c++/SearchCriteria.hpp +203 -0
- package/nitrogen/generated/shared/c++/SearchHeader.hpp +87 -0
- package/nitrogen/generated/shared/c++/SendMailOptions.hpp +151 -0
- package/nitrogen/generated/shared/c++/SendMailResult.hpp +113 -0
- package/nitrogen/generated/shared/c++/SmtpAttachment.hpp +105 -0
- package/nitrogen/generated/shared/c++/SmtpConnectionConfig.hpp +124 -0
- package/nitrogen/generated/shared/c++/SmtpServerInfo.hpp +92 -0
- package/package.json +196 -0
- package/react-native.config.js +27 -0
- package/src/EmailImapSmtp.nitro.ts +333 -0
- package/src/NativeEmailImapSmtp.ts +100 -0
- package/src/index.tsx +166 -0
- package/src/native.ts +305 -0
- package/src/types.ts +328 -0
- package/src/web.ts +196 -0
- package/src/windows.ts +304 -0
- package/windows/EmailImapSmtp.cpp +735 -0
- package/windows/EmailImapSmtp.h +137 -0
- package/windows/EmailImapSmtpModule.cpp +10 -0
- package/windows/EmailImapSmtpModule.h +139 -0
- package/windows/EmailImapSmtpPackage.cpp +15 -0
- package/windows/EmailImapSmtpPackage.h +13 -0
- package/windows/ImapProtocol.cpp +1382 -0
- package/windows/ImapProtocol.h +97 -0
- package/windows/ProtocolDefs.h +300 -0
- package/windows/SmtpProtocol.cpp +334 -0
- package/windows/SmtpProtocol.h +36 -0
- package/windows/TlsSocket.cpp +818 -0
- package/windows/TlsSocket.h +87 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SmtpProtocol.cpp — 纯 SMTP 协议实现
|
|
3
|
+
// 从 EmailImapSmtp.cpp 拆分,移除所有 Nitro/JSI 依赖
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
#include "SmtpProtocol.h"
|
|
7
|
+
|
|
8
|
+
#include <sstream>
|
|
9
|
+
#include <chrono>
|
|
10
|
+
#include <ctime>
|
|
11
|
+
#include <cstdlib>
|
|
12
|
+
|
|
13
|
+
namespace emailimap {
|
|
14
|
+
|
|
15
|
+
SmtpClient::SmtpClient()
|
|
16
|
+
: m_socket(std::make_unique<TlsSocket>())
|
|
17
|
+
{
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
SmtpClient::~SmtpClient() {
|
|
21
|
+
disconnect();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
bool SmtpClient::sendRaw(const std::string& data) {
|
|
25
|
+
return m_socket->send(data);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
std::string SmtpClient::receiveLine() {
|
|
29
|
+
return m_socket->receiveLine(30000);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
int SmtpClient::parseResponseCode(const std::string& response) {
|
|
33
|
+
if (response.size() >= 3) {
|
|
34
|
+
return std::stoi(response.substr(0, 3));
|
|
35
|
+
}
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bool SmtpClient::sendCommand(const std::string& command, std::string& response, std::string& error) {
|
|
40
|
+
if (!m_socket->send(command + "\r\n")) {
|
|
41
|
+
error = "Failed to send command";
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
response = receiveLine();
|
|
46
|
+
if (response.empty()) {
|
|
47
|
+
error = "Connection closed";
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (response.size() >= 4 && response[3] == '-') {
|
|
52
|
+
std::string code = response.substr(0, 3);
|
|
53
|
+
while (true) {
|
|
54
|
+
std::string line = receiveLine();
|
|
55
|
+
if (line.empty()) break;
|
|
56
|
+
response += "\r\n" + line;
|
|
57
|
+
if (line.size() >= 4 && line[3] == ' ') break;
|
|
58
|
+
if (line.size() >= 3 && line.substr(0, 3) == code && line[3] == ' ') break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
int code = parseResponseCode(response);
|
|
63
|
+
if (code >= 200 && code < 400) return true;
|
|
64
|
+
|
|
65
|
+
error = response;
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
bool SmtpClient::connect(const SmtpConnectionConfig& config, std::string& error) {
|
|
70
|
+
m_config = config;
|
|
71
|
+
|
|
72
|
+
if (!m_socket->connect(config.host, static_cast<int>(config.port),
|
|
73
|
+
static_cast<int>(config.timeout), config.checkCertificate)) {
|
|
74
|
+
error = "Failed to connect to " + config.host + ":" + std::to_string(static_cast<int>(config.port));
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std::string greeting = receiveLine();
|
|
79
|
+
if (greeting.empty()) { error = "No SMTP greeting"; disconnect(); return false; }
|
|
80
|
+
|
|
81
|
+
int code = parseResponseCode(greeting);
|
|
82
|
+
if (code != 220) { error = greeting; disconnect(); return false; }
|
|
83
|
+
|
|
84
|
+
std::string helloName = config.helloName.value_or("localhost");
|
|
85
|
+
std::string response;
|
|
86
|
+
if (!sendCommand("EHLO " + helloName, response, error)) {
|
|
87
|
+
if (!sendCommand("HELO " + helloName, response, error)) {
|
|
88
|
+
disconnect();
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// STARTTLS if needed
|
|
94
|
+
if (config.tls && config.port != 465) {
|
|
95
|
+
if (response.find("STARTTLS") != std::string::npos) {
|
|
96
|
+
if (!sendCommand("STARTTLS", response, error)) { disconnect(); return false; }
|
|
97
|
+
m_socket->disconnect();
|
|
98
|
+
if (!m_socket->connect(config.host, static_cast<int>(config.port),
|
|
99
|
+
static_cast<int>(config.timeout), config.checkCertificate)) {
|
|
100
|
+
error = "Failed to establish TLS"; return false;
|
|
101
|
+
}
|
|
102
|
+
greeting = receiveLine();
|
|
103
|
+
if (greeting.empty()) { error = "No greeting after STARTTLS"; disconnect(); return false; }
|
|
104
|
+
if (!sendCommand("EHLO " + helloName, response, error)) { disconnect(); return false; }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Authenticate
|
|
109
|
+
if (toUpper(config.authType) == "OAUTH2" && config.accessToken.has_value()) {
|
|
110
|
+
if (!sendCommand("AUTH XOAUTH2 " + encodeBase64Text(
|
|
111
|
+
"user=" + config.username + "\x01auth=Bearer " + config.accessToken.value() + "\x01\x01"),
|
|
112
|
+
response, error)) {
|
|
113
|
+
disconnect();
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
if (response.find("AUTH LOGIN") != std::string::npos ||
|
|
118
|
+
response.find("AUTH=LOGIN") != std::string::npos) {
|
|
119
|
+
if (!sendCommand("AUTH LOGIN", response, error)) {
|
|
120
|
+
std::string authStr = std::string() + '\x00' + config.username + '\x00' + config.password;
|
|
121
|
+
if (!sendCommand("AUTH PLAIN " + encodeBase64Text(authStr), response, error)) {
|
|
122
|
+
disconnect();
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
if (!sendCommand(encodeBase64Text(config.username), response, error) ||
|
|
127
|
+
!sendCommand(encodeBase64Text(config.password), response, error)) {
|
|
128
|
+
disconnect();
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
std::string authStr = std::string() + '\x00' + config.username + '\x00' + config.password;
|
|
134
|
+
if (!sendCommand("AUTH PLAIN " + encodeBase64Text(authStr), response, error)) {
|
|
135
|
+
disconnect();
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
void SmtpClient::disconnect() {
|
|
145
|
+
std::string response, error;
|
|
146
|
+
sendCommand("QUIT", response, error);
|
|
147
|
+
m_socket->disconnect();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
bool SmtpClient::isConnected() const {
|
|
151
|
+
return m_socket->isConnected();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
bool SmtpClient::sendMail(const SendMailOptions& options, SendMailResult& result, std::string& error) {
|
|
155
|
+
std::string response;
|
|
156
|
+
|
|
157
|
+
// MAIL FROM
|
|
158
|
+
if (!sendCommand("MAIL FROM:<" + options.from.email + ">", response, error)) {
|
|
159
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// RCPT TO
|
|
163
|
+
for (const auto& addr : options.to) {
|
|
164
|
+
if (!sendCommand("RCPT TO:<" + addr.email + ">", response, error)) {
|
|
165
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (options.cc.has_value()) {
|
|
170
|
+
for (const auto& addr : options.cc.value()) {
|
|
171
|
+
std::string rcpResp;
|
|
172
|
+
if (!sendCommand("RCPT TO:<" + addr.email + ">", rcpResp, error)) {
|
|
173
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (options.bcc.has_value()) {
|
|
179
|
+
for (const auto& addr : options.bcc.value()) {
|
|
180
|
+
std::string rcpResp;
|
|
181
|
+
if (!sendCommand("RCPT TO:<" + addr.email + ">", rcpResp, error)) {
|
|
182
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// DATA
|
|
188
|
+
if (!sendCommand("DATA", response, error)) {
|
|
189
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Build MIME message
|
|
193
|
+
std::string message;
|
|
194
|
+
std::string boundary = "=_Part_" + std::to_string(std::time(nullptr)) + "_" +
|
|
195
|
+
std::to_string(rand());
|
|
196
|
+
|
|
197
|
+
auto encodeHeader = [](const std::string& text) -> std::string {
|
|
198
|
+
bool needsEncoding = false;
|
|
199
|
+
for (unsigned char c : text) {
|
|
200
|
+
if (c > 127) { needsEncoding = true; break; }
|
|
201
|
+
}
|
|
202
|
+
if (needsEncoding) {
|
|
203
|
+
std::vector<char> utf8Bytes(text.begin(), text.end());
|
|
204
|
+
return "=?UTF-8?B?" + encodeBase64(utf8Bytes) + "?=";
|
|
205
|
+
}
|
|
206
|
+
return text;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// Headers
|
|
210
|
+
message += "From: " + encodeHeader(options.from.name) + " <" + options.from.email + ">\r\n";
|
|
211
|
+
for (const auto& addr : options.to) {
|
|
212
|
+
message += "To: " + encodeHeader(addr.name) + " <" + addr.email + ">\r\n";
|
|
213
|
+
}
|
|
214
|
+
if (options.cc.has_value()) {
|
|
215
|
+
for (const auto& addr : options.cc.value()) {
|
|
216
|
+
message += "Cc: " + encodeHeader(addr.name) + " <" + addr.email + ">\r\n";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
message += "Subject: " + encodeHeader(options.subject) + "\r\n";
|
|
220
|
+
message += "Date: " + formatDate(std::time(nullptr)) + "\r\n";
|
|
221
|
+
message += "MIME-Version: 1.0\r\n";
|
|
222
|
+
|
|
223
|
+
if (options.priority.has_value()) {
|
|
224
|
+
if (toUpper(options.priority.value()) == "HIGH") {
|
|
225
|
+
message += "X-Priority: 1\r\nPriority: urgent\r\n";
|
|
226
|
+
} else if (toUpper(options.priority.value()) == "LOW") {
|
|
227
|
+
message += "X-Priority: 5\r\nPriority: non-urgent\r\n";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (options.headers.has_value()) {
|
|
232
|
+
for (const auto& [key, value] : options.headers.value()) {
|
|
233
|
+
message += key + ": " + value + "\r\n";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (options.inReplyTo.has_value()) message += "In-Reply-To: <" + options.inReplyTo.value() + ">\r\n";
|
|
238
|
+
if (options.references.has_value()) message += "References: " + options.references.value() + "\r\n";
|
|
239
|
+
if (options.readReceiptTo.has_value()) {
|
|
240
|
+
message += "Disposition-Notification-To: <" + options.readReceiptTo.value().email + ">\r\n";
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
bool hasText = options.textBody.has_value() && !options.textBody.value().empty();
|
|
244
|
+
bool hasHtml = options.htmlBody.has_value() && !options.htmlBody.value().empty();
|
|
245
|
+
bool hasAttachments = options.attachments.has_value() && !options.attachments.value().empty();
|
|
246
|
+
|
|
247
|
+
if (hasAttachments) {
|
|
248
|
+
message += "Content-Type: multipart/mixed; boundary=\"" + boundary + "\"\r\n";
|
|
249
|
+
message += "\r\n--" + boundary + "\r\n";
|
|
250
|
+
|
|
251
|
+
if (hasText || hasHtml) {
|
|
252
|
+
std::string altBoundary = boundary + "_alt";
|
|
253
|
+
message += "Content-Type: multipart/alternative; boundary=\"" + altBoundary + "\"\r\n\r\n";
|
|
254
|
+
if (hasText) {
|
|
255
|
+
message += "--" + altBoundary + "\r\nContent-Type: text/plain; charset=\"UTF-8\"\r\n"
|
|
256
|
+
"Content-Transfer-Encoding: base64\r\n\r\n";
|
|
257
|
+
message += encodeBase64Text(options.textBody.value()) + "\r\n\r\n";
|
|
258
|
+
}
|
|
259
|
+
if (hasHtml) {
|
|
260
|
+
message += "--" + altBoundary + "\r\nContent-Type: text/html; charset=\"UTF-8\"\r\n"
|
|
261
|
+
"Content-Transfer-Encoding: base64\r\n\r\n";
|
|
262
|
+
message += encodeBase64Text(options.htmlBody.value()) + "\r\n\r\n";
|
|
263
|
+
}
|
|
264
|
+
message += "--" + altBoundary + "--\r\n";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (const auto& att : options.attachments.value()) {
|
|
268
|
+
message += "\r\n--" + boundary + "\r\n";
|
|
269
|
+
message += "Content-Type: " + att.mimeType + "; name=\"" + att.filename + "\"\r\n";
|
|
270
|
+
message += "Content-Disposition: " +
|
|
271
|
+
(att.isInline ? "inline" : (att.disposition.has_value() ? att.disposition.value() : "attachment")) +
|
|
272
|
+
"; filename=\"" + att.filename + "\"\r\n";
|
|
273
|
+
if (att.contentId.has_value()) message += "Content-ID: <" + att.contentId.value() + ">\r\n";
|
|
274
|
+
message += "Content-Transfer-Encoding: base64\r\n\r\n";
|
|
275
|
+
|
|
276
|
+
if (!att.data.empty()) {
|
|
277
|
+
std::string rawData(att.data.begin(), att.data.end());
|
|
278
|
+
message += encodeBase64Text(rawData) + "\r\n";
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
message += "\r\n--" + boundary + "--\r\n";
|
|
282
|
+
} else if (hasText && hasHtml) {
|
|
283
|
+
std::string altBoundary = boundary + "_alt";
|
|
284
|
+
message += "Content-Type: multipart/alternative; boundary=\"" + altBoundary + "\"\r\n\r\n";
|
|
285
|
+
message += "--" + altBoundary + "\r\nContent-Type: text/plain; charset=\"UTF-8\"\r\n"
|
|
286
|
+
"Content-Transfer-Encoding: base64\r\n\r\n";
|
|
287
|
+
message += encodeBase64Text(options.textBody.value()) + "\r\n\r\n";
|
|
288
|
+
message += "--" + altBoundary + "\r\nContent-Type: text/html; charset=\"UTF-8\"\r\n"
|
|
289
|
+
"Content-Transfer-Encoding: base64\r\n\r\n";
|
|
290
|
+
message += encodeBase64Text(options.htmlBody.value()) + "\r\n\r\n";
|
|
291
|
+
message += "--" + altBoundary + "--\r\n";
|
|
292
|
+
} else if (hasHtml) {
|
|
293
|
+
message += "Content-Type: text/html; charset=\"UTF-8\"\r\nContent-Transfer-Encoding: base64\r\n\r\n";
|
|
294
|
+
message += encodeBase64Text(options.htmlBody.value()) + "\r\n";
|
|
295
|
+
} else {
|
|
296
|
+
message += "Content-Type: text/plain; charset=\"UTF-8\"\r\nContent-Transfer-Encoding: base64\r\n\r\n";
|
|
297
|
+
message += encodeBase64Text(options.textBody.value_or("")) + "\r\n";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
message += "\r\n.";
|
|
301
|
+
|
|
302
|
+
if (!m_socket->send(message + "\r\n")) {
|
|
303
|
+
error = "Failed to send message data";
|
|
304
|
+
result.success = false; result.error = error; result.retryable = true; return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
response = receiveLine();
|
|
308
|
+
if (response.empty()) {
|
|
309
|
+
error = "Connection closed during DATA";
|
|
310
|
+
result.success = false; result.error = error; result.retryable = false; return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
int code = parseResponseCode(response);
|
|
314
|
+
if (code >= 200 && code < 400) {
|
|
315
|
+
result.success = true;
|
|
316
|
+
result.messageId = "<" + std::to_string(std::time(nullptr)) + "." +
|
|
317
|
+
std::to_string(rand()) + "@" + m_config.host + ">";
|
|
318
|
+
result.date = currentTimestamp();
|
|
319
|
+
result.serverResponseCode = static_cast<double>(code);
|
|
320
|
+
result.retryable = false;
|
|
321
|
+
|
|
322
|
+
std::string rawMime = message;
|
|
323
|
+
result.rawMimeData = std::vector<uint8_t>(rawMime.begin(), rawMime.end());
|
|
324
|
+
} else {
|
|
325
|
+
result.success = false;
|
|
326
|
+
result.error = response;
|
|
327
|
+
result.serverResponseCode = static_cast<double>(code);
|
|
328
|
+
result.retryable = (code >= 400 && code < 500);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return result.success;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
} // namespace emailimap
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SmtpProtocol.h — 纯 SMTP 协议实现,无 Nitro/JSI 依赖
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <memory>
|
|
9
|
+
|
|
10
|
+
#include "ProtocolDefs.h"
|
|
11
|
+
#include "TlsSocket.h"
|
|
12
|
+
|
|
13
|
+
namespace emailimap {
|
|
14
|
+
|
|
15
|
+
class SmtpClient {
|
|
16
|
+
public:
|
|
17
|
+
SmtpClient();
|
|
18
|
+
~SmtpClient();
|
|
19
|
+
|
|
20
|
+
bool connect(const SmtpConnectionConfig& config, std::string& error);
|
|
21
|
+
void disconnect();
|
|
22
|
+
bool isConnected() const;
|
|
23
|
+
|
|
24
|
+
bool sendMail(const SendMailOptions& options, SendMailResult& result, std::string& error);
|
|
25
|
+
|
|
26
|
+
private:
|
|
27
|
+
std::unique_ptr<TlsSocket> m_socket;
|
|
28
|
+
SmtpConnectionConfig m_config;
|
|
29
|
+
|
|
30
|
+
bool sendCommand(const std::string& command, std::string& response, std::string& error);
|
|
31
|
+
bool sendRaw(const std::string& data);
|
|
32
|
+
std::string receiveLine();
|
|
33
|
+
int parseResponseCode(const std::string& response);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
} // namespace emailimap
|