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.
Files changed (201) hide show
  1. package/EmailImapSmtp.podspec +29 -0
  2. package/LICENSE +20 -0
  3. package/README.md +121 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +117 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  8. package/android/src/main/java/com/margelo/nitro/emailimapsmtp/EmailImapSmtp.kt +1533 -0
  9. package/android/src/main/java/com/margelo/nitro/emailimapsmtp/EmailImapSmtpPackage.kt +22 -0
  10. package/ios/EmailImapSmtp.swift +5 -0
  11. package/lib/module/EmailImapSmtp.nitro.js +4 -0
  12. package/lib/module/EmailImapSmtp.nitro.js.map +1 -0
  13. package/lib/module/NativeEmailImapSmtp.js +14 -0
  14. package/lib/module/NativeEmailImapSmtp.js.map +1 -0
  15. package/lib/module/index.js +64 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/native.js +227 -0
  18. package/lib/module/native.js.map +1 -0
  19. package/lib/module/package.json +1 -0
  20. package/lib/module/types.js +2 -0
  21. package/lib/module/types.js.map +1 -0
  22. package/lib/module/web.js +107 -0
  23. package/lib/module/web.js.map +1 -0
  24. package/lib/module/windows.js +156 -0
  25. package/lib/module/windows.js.map +1 -0
  26. package/lib/typescript/package.json +1 -0
  27. package/lib/typescript/src/EmailImapSmtp.nitro.d.ts +268 -0
  28. package/lib/typescript/src/EmailImapSmtp.nitro.d.ts.map +1 -0
  29. package/lib/typescript/src/NativeEmailImapSmtp.d.ts +36 -0
  30. package/lib/typescript/src/NativeEmailImapSmtp.d.ts.map +1 -0
  31. package/lib/typescript/src/index.d.ts +33 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -0
  33. package/lib/typescript/src/native.d.ts +32 -0
  34. package/lib/typescript/src/native.d.ts.map +1 -0
  35. package/lib/typescript/src/types.d.ts +261 -0
  36. package/lib/typescript/src/types.d.ts.map +1 -0
  37. package/lib/typescript/src/web.d.ts +32 -0
  38. package/lib/typescript/src/web.d.ts.map +1 -0
  39. package/lib/typescript/src/windows.d.ts +32 -0
  40. package/lib/typescript/src/windows.d.ts.map +1 -0
  41. package/nitro.json +23 -0
  42. package/nitrogen/generated/android/c++/JAppendResult.hpp +65 -0
  43. package/nitrogen/generated/android/c++/JAttachmentInfo.hpp +96 -0
  44. package/nitrogen/generated/android/c++/JConnectionConfig.hpp +94 -0
  45. package/nitrogen/generated/android/c++/JConnectionResult.hpp +73 -0
  46. package/nitrogen/generated/android/c++/JDeleteResult.hpp +87 -0
  47. package/nitrogen/generated/android/c++/JEmailAddress.hpp +61 -0
  48. package/nitrogen/generated/android/c++/JEmailBody.hpp +84 -0
  49. package/nitrogen/generated/android/c++/JEmailMessage.hpp +273 -0
  50. package/nitrogen/generated/android/c++/JFetchEmailsOptions.hpp +110 -0
  51. package/nitrogen/generated/android/c++/JFolderInfo.hpp +126 -0
  52. package/nitrogen/generated/android/c++/JFunc_void_IdleNotification.hpp +79 -0
  53. package/nitrogen/generated/android/c++/JHybridEmailImapSmtpSpec.cpp +697 -0
  54. package/nitrogen/generated/android/c++/JHybridEmailImapSmtpSpec.hpp +92 -0
  55. package/nitrogen/generated/android/c++/JIdleNotification.hpp +78 -0
  56. package/nitrogen/generated/android/c++/JImapServerInfo.hpp +84 -0
  57. package/nitrogen/generated/android/c++/JMailboxInfo.hpp +134 -0
  58. package/nitrogen/generated/android/c++/JMailboxListItem.hpp +92 -0
  59. package/nitrogen/generated/android/c++/JQuotaInfo.hpp +78 -0
  60. package/nitrogen/generated/android/c++/JSearchCriteria.hpp +187 -0
  61. package/nitrogen/generated/android/c++/JSearchHeader.hpp +61 -0
  62. package/nitrogen/generated/android/c++/JSendMailOptions.hpp +229 -0
  63. package/nitrogen/generated/android/c++/JSendMailResult.hpp +88 -0
  64. package/nitrogen/generated/android/c++/JSmtpAttachment.hpp +80 -0
  65. package/nitrogen/generated/android/c++/JSmtpConnectionConfig.hpp +98 -0
  66. package/nitrogen/generated/android/c++/JSmtpServerInfo.hpp +102 -0
  67. package/nitrogen/generated/android/c++/JVariant_ImapServerInfo_SmtpServerInfo.cpp +26 -0
  68. package/nitrogen/generated/android/c++/JVariant_ImapServerInfo_SmtpServerInfo.hpp +73 -0
  69. package/nitrogen/generated/android/c++/JVariant_NullType_AttachmentInfo.cpp +26 -0
  70. package/nitrogen/generated/android/c++/JVariant_NullType_AttachmentInfo.hpp +75 -0
  71. package/nitrogen/generated/android/c++/JVariant_NullType_EmailMessage.cpp +26 -0
  72. package/nitrogen/generated/android/c++/JVariant_NullType_EmailMessage.hpp +83 -0
  73. package/nitrogen/generated/android/emailimapsmtp+autolinking.cmake +84 -0
  74. package/nitrogen/generated/android/emailimapsmtp+autolinking.gradle +27 -0
  75. package/nitrogen/generated/android/emailimapsmtpOnLoad.cpp +56 -0
  76. package/nitrogen/generated/android/emailimapsmtpOnLoad.hpp +34 -0
  77. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/AppendResult.kt +61 -0
  78. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/AttachmentInfo.kt +96 -0
  79. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionConfig.kt +96 -0
  80. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionResult.kt +61 -0
  81. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/DeleteResult.kt +61 -0
  82. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailAddress.kt +56 -0
  83. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailBody.kt +61 -0
  84. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/EmailMessage.kt +156 -0
  85. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/FetchEmailsOptions.kt +116 -0
  86. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/FolderInfo.kt +91 -0
  87. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Func_void_IdleNotification.kt +80 -0
  88. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/HybridEmailImapSmtpSpec.kt +178 -0
  89. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/IdleNotification.kt +76 -0
  90. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ImapServerInfo.kt +61 -0
  91. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/MailboxInfo.kt +101 -0
  92. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/MailboxListItem.kt +71 -0
  93. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/QuotaInfo.kt +76 -0
  94. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SearchCriteria.kt +196 -0
  95. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SearchHeader.kt +56 -0
  96. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SendMailOptions.kt +126 -0
  97. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SendMailResult.kt +86 -0
  98. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpAttachment.kt +76 -0
  99. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpConnectionConfig.kt +101 -0
  100. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/SmtpServerInfo.kt +61 -0
  101. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_ImapServerInfo_SmtpServerInfo.kt +62 -0
  102. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_NullType_AttachmentInfo.kt +62 -0
  103. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/Variant_NullType_EmailMessage.kt +62 -0
  104. package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/emailimapsmtpOnLoad.kt +35 -0
  105. package/nitrogen/generated/ios/EmailImapSmtp+autolinking.rb +62 -0
  106. package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Bridge.cpp +161 -0
  107. package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Bridge.hpp +1132 -0
  108. package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Umbrella.hpp +118 -0
  109. package/nitrogen/generated/ios/EmailImapSmtpAutolinking.mm +33 -0
  110. package/nitrogen/generated/ios/EmailImapSmtpAutolinking.swift +26 -0
  111. package/nitrogen/generated/ios/c++/HybridEmailImapSmtpSpecSwift.cpp +11 -0
  112. package/nitrogen/generated/ios/c++/HybridEmailImapSmtpSpecSwift.hpp +390 -0
  113. package/nitrogen/generated/ios/swift/AppendResult.swift +39 -0
  114. package/nitrogen/generated/ios/swift/AttachmentInfo.swift +113 -0
  115. package/nitrogen/generated/ios/swift/ConnectionConfig.swift +87 -0
  116. package/nitrogen/generated/ios/swift/ConnectionResult.swift +84 -0
  117. package/nitrogen/generated/ios/swift/DeleteResult.swift +64 -0
  118. package/nitrogen/generated/ios/swift/EmailAddress.swift +34 -0
  119. package/nitrogen/generated/ios/swift/EmailBody.swift +45 -0
  120. package/nitrogen/generated/ios/swift/EmailMessage.swift +184 -0
  121. package/nitrogen/generated/ios/swift/FetchEmailsOptions.swift +198 -0
  122. package/nitrogen/generated/ios/swift/FolderInfo.swift +81 -0
  123. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  124. package/nitrogen/generated/ios/swift/Func_void_AppendResult.swift +46 -0
  125. package/nitrogen/generated/ios/swift/Func_void_ConnectionResult.swift +46 -0
  126. package/nitrogen/generated/ios/swift/Func_void_DeleteResult.swift +46 -0
  127. package/nitrogen/generated/ios/swift/Func_void_IdleNotification.swift +46 -0
  128. package/nitrogen/generated/ios/swift/Func_void_MailboxInfo.swift +46 -0
  129. package/nitrogen/generated/ios/swift/Func_void_QuotaInfo.swift +46 -0
  130. package/nitrogen/generated/ios/swift/Func_void_SendMailResult.swift +46 -0
  131. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  132. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  133. package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__AttachmentInfo_.swift +58 -0
  134. package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__EmailMessage_.swift +58 -0
  135. package/nitrogen/generated/ios/swift/Func_void_std__vector_AttachmentInfo_.swift +46 -0
  136. package/nitrogen/generated/ios/swift/Func_void_std__vector_EmailMessage_.swift +46 -0
  137. package/nitrogen/generated/ios/swift/Func_void_std__vector_FolderInfo_.swift +46 -0
  138. package/nitrogen/generated/ios/swift/Func_void_std__vector_MailboxListItem_.swift +46 -0
  139. package/nitrogen/generated/ios/swift/HybridEmailImapSmtpSpec.swift +84 -0
  140. package/nitrogen/generated/ios/swift/HybridEmailImapSmtpSpec_cxx.swift +759 -0
  141. package/nitrogen/generated/ios/swift/IdleNotification.swift +93 -0
  142. package/nitrogen/generated/ios/swift/ImapServerInfo.swift +45 -0
  143. package/nitrogen/generated/ios/swift/MailboxInfo.swift +91 -0
  144. package/nitrogen/generated/ios/swift/MailboxListItem.swift +55 -0
  145. package/nitrogen/generated/ios/swift/QuotaInfo.swift +80 -0
  146. package/nitrogen/generated/ios/swift/SearchCriteria.swift +485 -0
  147. package/nitrogen/generated/ios/swift/SearchHeader.swift +34 -0
  148. package/nitrogen/generated/ios/swift/SendMailOptions.swift +310 -0
  149. package/nitrogen/generated/ios/swift/SendMailResult.swift +155 -0
  150. package/nitrogen/generated/ios/swift/SmtpAttachment.swift +80 -0
  151. package/nitrogen/generated/ios/swift/SmtpConnectionConfig.swift +105 -0
  152. package/nitrogen/generated/ios/swift/SmtpServerInfo.swift +51 -0
  153. package/nitrogen/generated/ios/swift/Variant_ImapServerInfo_SmtpServerInfo.swift +30 -0
  154. package/nitrogen/generated/ios/swift/Variant_NullType_AttachmentInfo.swift +30 -0
  155. package/nitrogen/generated/ios/swift/Variant_NullType_EmailMessage.swift +30 -0
  156. package/nitrogen/generated/shared/c++/AppendResult.hpp +91 -0
  157. package/nitrogen/generated/shared/c++/AttachmentInfo.hpp +121 -0
  158. package/nitrogen/generated/shared/c++/ConnectionConfig.hpp +120 -0
  159. package/nitrogen/generated/shared/c++/ConnectionResult.hpp +98 -0
  160. package/nitrogen/generated/shared/c++/DeleteResult.hpp +93 -0
  161. package/nitrogen/generated/shared/c++/EmailAddress.hpp +87 -0
  162. package/nitrogen/generated/shared/c++/EmailBody.hpp +92 -0
  163. package/nitrogen/generated/shared/c++/EmailMessage.hpp +177 -0
  164. package/nitrogen/generated/shared/c++/FetchEmailsOptions.hpp +136 -0
  165. package/nitrogen/generated/shared/c++/FolderInfo.hpp +116 -0
  166. package/nitrogen/generated/shared/c++/HybridEmailImapSmtpSpec.cpp +50 -0
  167. package/nitrogen/generated/shared/c++/HybridEmailImapSmtpSpec.hpp +145 -0
  168. package/nitrogen/generated/shared/c++/IdleNotification.hpp +104 -0
  169. package/nitrogen/generated/shared/c++/ImapServerInfo.hpp +92 -0
  170. package/nitrogen/generated/shared/c++/MailboxInfo.hpp +124 -0
  171. package/nitrogen/generated/shared/c++/MailboxListItem.hpp +100 -0
  172. package/nitrogen/generated/shared/c++/QuotaInfo.hpp +104 -0
  173. package/nitrogen/generated/shared/c++/SearchCriteria.hpp +203 -0
  174. package/nitrogen/generated/shared/c++/SearchHeader.hpp +87 -0
  175. package/nitrogen/generated/shared/c++/SendMailOptions.hpp +151 -0
  176. package/nitrogen/generated/shared/c++/SendMailResult.hpp +113 -0
  177. package/nitrogen/generated/shared/c++/SmtpAttachment.hpp +105 -0
  178. package/nitrogen/generated/shared/c++/SmtpConnectionConfig.hpp +124 -0
  179. package/nitrogen/generated/shared/c++/SmtpServerInfo.hpp +92 -0
  180. package/package.json +196 -0
  181. package/react-native.config.js +27 -0
  182. package/src/EmailImapSmtp.nitro.ts +333 -0
  183. package/src/NativeEmailImapSmtp.ts +100 -0
  184. package/src/index.tsx +166 -0
  185. package/src/native.ts +305 -0
  186. package/src/types.ts +328 -0
  187. package/src/web.ts +196 -0
  188. package/src/windows.ts +304 -0
  189. package/windows/EmailImapSmtp.cpp +735 -0
  190. package/windows/EmailImapSmtp.h +137 -0
  191. package/windows/EmailImapSmtpModule.cpp +10 -0
  192. package/windows/EmailImapSmtpModule.h +139 -0
  193. package/windows/EmailImapSmtpPackage.cpp +15 -0
  194. package/windows/EmailImapSmtpPackage.h +13 -0
  195. package/windows/ImapProtocol.cpp +1382 -0
  196. package/windows/ImapProtocol.h +97 -0
  197. package/windows/ProtocolDefs.h +300 -0
  198. package/windows/SmtpProtocol.cpp +334 -0
  199. package/windows/SmtpProtocol.h +36 -0
  200. package/windows/TlsSocket.cpp +818 -0
  201. 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