react-native-email-imap-smtp 0.3.29 → 0.3.31
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/android/src/main/java/com/margelo/nitro/emailimapsmtp/EmailImapSmtp.kt +157 -103
- package/lib/module/native.js +2 -1
- package/lib/module/native.js.map +1 -1
- package/lib/typescript/server/src/imapHandler.d.ts.map +1 -1
- package/lib/typescript/src/EmailImapSmtp.nitro.d.ts +2 -0
- package/lib/typescript/src/EmailImapSmtp.nitro.d.ts.map +1 -1
- package/lib/typescript/src/native.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +6 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JConnectionConfig.hpp +7 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionConfig.kt +9 -4
- package/nitrogen/generated/ios/EmailImapSmtp-Swift-Cxx-Bridge.hpp +15 -15
- package/nitrogen/generated/ios/swift/ConnectionConfig.swift +20 -2
- package/nitrogen/generated/shared/c++/ConnectionConfig.hpp +6 -2
- package/package.json +1 -1
- package/server/lib/imapHandler.d.ts.map +1 -1
- package/server/lib/imapHandler.js +179 -55
- package/server/lib/imapHandler.js.map +1 -1
- package/server/package-lock.json +1 -0
- package/server/package.json +1 -0
- package/server/src/imapHandler.ts +201 -53
- package/src/EmailImapSmtp.nitro.ts +2 -0
- package/src/native.ts +1 -0
- package/src/types.ts +6 -0
package/nitrogen/generated/android/kotlin/com/margelo/nitro/emailimapsmtp/ConnectionConfig.kt
CHANGED
|
@@ -47,7 +47,10 @@ data class ConnectionConfig(
|
|
|
47
47
|
val checkCertificate: Boolean,
|
|
48
48
|
@DoNotStrip
|
|
49
49
|
@Keep
|
|
50
|
-
val debug: Boolean
|
|
50
|
+
val debug: Boolean,
|
|
51
|
+
@DoNotStrip
|
|
52
|
+
@Keep
|
|
53
|
+
val downloadChunkSize: Double?
|
|
51
54
|
) {
|
|
52
55
|
/* primary constructor */
|
|
53
56
|
|
|
@@ -64,6 +67,7 @@ data class ConnectionConfig(
|
|
|
64
67
|
&& Objects.deepEquals(this.timeout, other.timeout)
|
|
65
68
|
&& Objects.deepEquals(this.checkCertificate, other.checkCertificate)
|
|
66
69
|
&& Objects.deepEquals(this.debug, other.debug)
|
|
70
|
+
&& Objects.deepEquals(this.downloadChunkSize, other.downloadChunkSize)
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
override fun hashCode(): Int {
|
|
@@ -77,7 +81,8 @@ data class ConnectionConfig(
|
|
|
77
81
|
accessToken,
|
|
78
82
|
timeout,
|
|
79
83
|
checkCertificate,
|
|
80
|
-
debug
|
|
84
|
+
debug,
|
|
85
|
+
downloadChunkSize
|
|
81
86
|
).contentDeepHashCode()
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -89,8 +94,8 @@ data class ConnectionConfig(
|
|
|
89
94
|
@Keep
|
|
90
95
|
@Suppress("unused")
|
|
91
96
|
@JvmStatic
|
|
92
|
-
private fun fromCpp(host: String, port: Double, username: String, password: String, tls: Boolean, authType: String, accessToken: String?, timeout: Double, checkCertificate: Boolean, debug: Boolean): ConnectionConfig {
|
|
93
|
-
return ConnectionConfig(host, port, username, password, tls, authType, accessToken, timeout, checkCertificate, debug)
|
|
97
|
+
private fun fromCpp(host: String, port: Double, username: String, password: String, tls: Boolean, authType: String, accessToken: String?, timeout: Double, checkCertificate: Boolean, debug: Boolean, downloadChunkSize: Double?): ConnectionConfig {
|
|
98
|
+
return ConnectionConfig(host, port, username, password, tls, authType, accessToken, timeout, checkCertificate, debug, downloadChunkSize)
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
}
|
|
@@ -217,6 +217,21 @@ namespace margelo::nitro::emailimapsmtp::bridge::swift {
|
|
|
217
217
|
return Func_void_std__exception_ptr_Wrapper(std::move(value));
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// pragma MARK: std::optional<double>
|
|
221
|
+
/**
|
|
222
|
+
* Specialized version of `std::optional<double>`.
|
|
223
|
+
*/
|
|
224
|
+
using std__optional_double_ = std::optional<double>;
|
|
225
|
+
inline std::optional<double> create_std__optional_double_(const double& value) noexcept {
|
|
226
|
+
return std::optional<double>(value);
|
|
227
|
+
}
|
|
228
|
+
inline bool has_value_std__optional_double_(const std::optional<double>& optional) noexcept {
|
|
229
|
+
return optional.has_value();
|
|
230
|
+
}
|
|
231
|
+
inline double get_std__optional_double_(const std::optional<double>& optional) noexcept {
|
|
232
|
+
return optional.value();
|
|
233
|
+
}
|
|
234
|
+
|
|
220
235
|
// pragma MARK: std::shared_ptr<Promise<void>>
|
|
221
236
|
/**
|
|
222
237
|
* Specialized version of `std::shared_ptr<Promise<void>>`.
|
|
@@ -467,21 +482,6 @@ namespace margelo::nitro::emailimapsmtp::bridge::swift {
|
|
|
467
482
|
return Func_void_std__vector_EmailMessage__Wrapper(std::move(value));
|
|
468
483
|
}
|
|
469
484
|
|
|
470
|
-
// pragma MARK: std::optional<double>
|
|
471
|
-
/**
|
|
472
|
-
* Specialized version of `std::optional<double>`.
|
|
473
|
-
*/
|
|
474
|
-
using std__optional_double_ = std::optional<double>;
|
|
475
|
-
inline std::optional<double> create_std__optional_double_(const double& value) noexcept {
|
|
476
|
-
return std::optional<double>(value);
|
|
477
|
-
}
|
|
478
|
-
inline bool has_value_std__optional_double_(const std::optional<double>& optional) noexcept {
|
|
479
|
-
return optional.has_value();
|
|
480
|
-
}
|
|
481
|
-
inline double get_std__optional_double_(const std::optional<double>& optional) noexcept {
|
|
482
|
-
return optional.value();
|
|
483
|
-
}
|
|
484
|
-
|
|
485
485
|
// pragma MARK: std::variant<nitro::NullType, EmailMessage>
|
|
486
486
|
/**
|
|
487
487
|
* Wrapper struct for `std::variant<nitro::NullType, EmailMessage>`.
|
|
@@ -18,14 +18,20 @@ public extension ConnectionConfig {
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a new instance of `ConnectionConfig`.
|
|
20
20
|
*/
|
|
21
|
-
init(host: String, port: Double, username: String, password: String, tls: Bool, authType: String, accessToken: String?, timeout: Double, checkCertificate: Bool, debug: Bool) {
|
|
21
|
+
init(host: String, port: Double, username: String, password: String, tls: Bool, authType: String, accessToken: String?, timeout: Double, checkCertificate: Bool, debug: Bool, downloadChunkSize: Double?) {
|
|
22
22
|
self.init(std.string(host), port, std.string(username), std.string(password), tls, std.string(authType), { () -> bridge.std__optional_std__string_ in
|
|
23
23
|
if let __unwrappedValue = accessToken {
|
|
24
24
|
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
25
25
|
} else {
|
|
26
26
|
return .init()
|
|
27
27
|
}
|
|
28
|
-
}(), timeout, checkCertificate, debug)
|
|
28
|
+
}(), timeout, checkCertificate, debug, { () -> bridge.std__optional_double_ in
|
|
29
|
+
if let __unwrappedValue = downloadChunkSize {
|
|
30
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
31
|
+
} else {
|
|
32
|
+
return .init()
|
|
33
|
+
}
|
|
34
|
+
}())
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
@inline(__always)
|
|
@@ -84,4 +90,16 @@ public extension ConnectionConfig {
|
|
|
84
90
|
var debug: Bool {
|
|
85
91
|
return self.__debug
|
|
86
92
|
}
|
|
93
|
+
|
|
94
|
+
@inline(__always)
|
|
95
|
+
var downloadChunkSize: Double? {
|
|
96
|
+
return { () -> Double? in
|
|
97
|
+
if bridge.has_value_std__optional_double_(self.__downloadChunkSize) {
|
|
98
|
+
let __unwrapped = bridge.get_std__optional_double_(self.__downloadChunkSize)
|
|
99
|
+
return __unwrapped
|
|
100
|
+
} else {
|
|
101
|
+
return nil
|
|
102
|
+
}
|
|
103
|
+
}()
|
|
104
|
+
}
|
|
87
105
|
}
|
|
@@ -50,10 +50,11 @@ namespace margelo::nitro::emailimapsmtp {
|
|
|
50
50
|
double timeout SWIFT_PRIVATE;
|
|
51
51
|
bool checkCertificate SWIFT_PRIVATE;
|
|
52
52
|
bool debug SWIFT_PRIVATE;
|
|
53
|
+
std::optional<double> downloadChunkSize SWIFT_PRIVATE;
|
|
53
54
|
|
|
54
55
|
public:
|
|
55
56
|
ConnectionConfig() = default;
|
|
56
|
-
explicit ConnectionConfig(std::string host, double port, std::string username, std::string password, bool tls, std::string authType, std::optional<std::string> accessToken, double timeout, bool checkCertificate, bool debug): host(host), port(port), username(username), password(password), tls(tls), authType(authType), accessToken(accessToken), timeout(timeout), checkCertificate(checkCertificate), debug(debug) {}
|
|
57
|
+
explicit ConnectionConfig(std::string host, double port, std::string username, std::string password, bool tls, std::string authType, std::optional<std::string> accessToken, double timeout, bool checkCertificate, bool debug, std::optional<double> downloadChunkSize): host(host), port(port), username(username), password(password), tls(tls), authType(authType), accessToken(accessToken), timeout(timeout), checkCertificate(checkCertificate), debug(debug), downloadChunkSize(downloadChunkSize) {}
|
|
57
58
|
|
|
58
59
|
public:
|
|
59
60
|
friend bool operator==(const ConnectionConfig& lhs, const ConnectionConfig& rhs) = default;
|
|
@@ -78,7 +79,8 @@ namespace margelo::nitro {
|
|
|
78
79
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "accessToken"))),
|
|
79
80
|
JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "timeout"))),
|
|
80
81
|
JSIConverter<bool>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "checkCertificate"))),
|
|
81
|
-
JSIConverter<bool>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "debug")))
|
|
82
|
+
JSIConverter<bool>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "debug"))),
|
|
83
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "downloadChunkSize")))
|
|
82
84
|
);
|
|
83
85
|
}
|
|
84
86
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::emailimapsmtp::ConnectionConfig& arg) {
|
|
@@ -93,6 +95,7 @@ namespace margelo::nitro {
|
|
|
93
95
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "timeout"), JSIConverter<double>::toJSI(runtime, arg.timeout));
|
|
94
96
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "checkCertificate"), JSIConverter<bool>::toJSI(runtime, arg.checkCertificate));
|
|
95
97
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "debug"), JSIConverter<bool>::toJSI(runtime, arg.debug));
|
|
98
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "downloadChunkSize"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.downloadChunkSize));
|
|
96
99
|
return obj;
|
|
97
100
|
}
|
|
98
101
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -113,6 +116,7 @@ namespace margelo::nitro {
|
|
|
113
116
|
if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "timeout")))) return false;
|
|
114
117
|
if (!JSIConverter<bool>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "checkCertificate")))) return false;
|
|
115
118
|
if (!JSIConverter<bool>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "debug")))) return false;
|
|
119
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "downloadChunkSize")))) return false;
|
|
116
120
|
return true;
|
|
117
121
|
}
|
|
118
122
|
};
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imapHandler.d.ts","sourceRoot":"","sources":["../src/imapHandler.ts"],"names":[],"mappings":"AAoJA,wBAAsB,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CA8CvD;AAID,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE;AAID,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAUrE;AAID,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,GAAG,CAAC,CAyCd;AAID,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAwCpE;AAID,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAuCpE;AAID,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;
|
|
1
|
+
{"version":3,"file":"imapHandler.d.ts","sourceRoot":"","sources":["../src/imapHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -366,12 +366,57 @@ async function unsubscribeFolder(sessionId, folderName) {
|
|
|
366
366
|
await s.client.mailboxUnsubscribe(folderName);
|
|
367
367
|
}
|
|
368
368
|
// ─── 获取邮件 ──────────────────────────────────────────────
|
|
369
|
+
/** 从 bodyStructure 推导 text/plain 与 text/html 的 part key 与字符集(与 fetchAllAttachments 的 walk 同构) */
|
|
370
|
+
function findTextParts(bs) {
|
|
371
|
+
const out = {};
|
|
372
|
+
function walk(node, prefix) {
|
|
373
|
+
if (node.childNodes && node.childNodes.length > 0) {
|
|
374
|
+
node.childNodes.forEach((child, i) => {
|
|
375
|
+
walk(child, prefix ? `${prefix}.${i + 1}` : `${i + 1}`);
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const type = String(node.type || '').toLowerCase();
|
|
380
|
+
const sub = String(node.subType || '').toLowerCase();
|
|
381
|
+
const charset = node.parameters?.charset;
|
|
382
|
+
if (type === 'text' && sub === 'plain' && !out.plain) {
|
|
383
|
+
out.plain = { key: prefix || '1', charset };
|
|
384
|
+
}
|
|
385
|
+
else if (type === 'text' && sub === 'html' && !out.html) {
|
|
386
|
+
out.html = { key: prefix || '1', charset };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
walk(bs, '');
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
/** 解码 text part 内容(传输编码已由 imapflow 解码;这里处理字符集,GBK 等用 iconv-lite) */
|
|
393
|
+
function decodePartText(buf, charset) {
|
|
394
|
+
const cs = String(charset || 'utf-8').toLowerCase().replace(/"/g, '').trim();
|
|
395
|
+
try {
|
|
396
|
+
const iconv = require('iconv-lite');
|
|
397
|
+
if (iconv.encodingExists(cs))
|
|
398
|
+
return iconv.decode(buf, cs);
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
/* 降级到 Buffer.toString */
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
return buf.toString(cs);
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return buf.toString('utf-8');
|
|
408
|
+
}
|
|
409
|
+
}
|
|
369
410
|
async function fetchEmails(sessionId, options) {
|
|
370
411
|
const s = getSession(sessionId);
|
|
371
412
|
const mailbox = options.mailbox ?? 'INBOX';
|
|
372
413
|
const lock = await s.client.getMailboxLock(mailbox);
|
|
373
414
|
try {
|
|
374
|
-
|
|
415
|
+
// 已选中同一邮箱时跳过 SELECT,避免重复往返
|
|
416
|
+
const alreadySelected = s.client.mailbox && s.client.mailbox.path === mailbox;
|
|
417
|
+
const mbox = alreadySelected
|
|
418
|
+
? s.client.mailbox
|
|
419
|
+
: await s.client.mailboxOpen(mailbox);
|
|
375
420
|
s.currentMailbox = mailbox;
|
|
376
421
|
const total = mbox.exists ?? 0;
|
|
377
422
|
// 构建查询范围
|
|
@@ -417,7 +462,8 @@ async function fetchEmails(sessionId, options) {
|
|
|
417
462
|
df.before = new Date(options.before);
|
|
418
463
|
range = { ...range, ...df };
|
|
419
464
|
}
|
|
420
|
-
// 构建 fetch fields
|
|
465
|
+
// 构建 fetch fields(默认不拉全文;正文用第二段只取 text 部分,跳过附件下载)
|
|
466
|
+
const needBody = options.fetchBody !== false;
|
|
421
467
|
const fields = {
|
|
422
468
|
uid: true,
|
|
423
469
|
flags: true,
|
|
@@ -427,19 +473,57 @@ async function fetchEmails(sessionId, options) {
|
|
|
427
473
|
internalDate: true,
|
|
428
474
|
headers: true,
|
|
429
475
|
};
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const messages = [];
|
|
476
|
+
// 第一段:元数据(含 bodyStructure),随后推导正文 part key
|
|
477
|
+
const entries = [];
|
|
478
|
+
const byUid = new Map();
|
|
434
479
|
let seq = 0;
|
|
480
|
+
let textParts = null;
|
|
435
481
|
for await (const msg of s.client.fetch(range, fields, fetchOpts)) {
|
|
436
482
|
seq++;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
483
|
+
const entry = { msg, seq };
|
|
484
|
+
entries.push(entry);
|
|
485
|
+
byUid.set(msg.uid, entry);
|
|
486
|
+
if (needBody && !textParts && msg.bodyStructure) {
|
|
487
|
+
textParts = findTextParts(msg.bodyStructure);
|
|
440
488
|
}
|
|
441
|
-
messages.push(buildEmailObject(msg, seq, body));
|
|
442
489
|
}
|
|
490
|
+
// 第二段:只取 text/plain 与 text/html 正文(不下载附件)
|
|
491
|
+
if (needBody && textParts && (textParts.plain || textParts.html)) {
|
|
492
|
+
const bodyParts = [];
|
|
493
|
+
if (textParts.plain)
|
|
494
|
+
bodyParts.push({ key: textParts.plain.key });
|
|
495
|
+
if (textParts.html)
|
|
496
|
+
bodyParts.push({ key: textParts.html.key });
|
|
497
|
+
for await (const b of s.client.fetch(range, { uid: true, bodyParts }, fetchOpts)) {
|
|
498
|
+
const entry = byUid.get(b.uid);
|
|
499
|
+
if (entry && b.bodyParts) {
|
|
500
|
+
entry.bodyPartsMap = b.bodyParts;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const messages = entries.map(({ msg, seq: seqNo, bodyPartsMap }) => {
|
|
505
|
+
let body;
|
|
506
|
+
if (needBody && bodyPartsMap && textParts) {
|
|
507
|
+
const getPart = (key) => bodyPartsMap.get(key) ?? bodyPartsMap.get(key.toUpperCase());
|
|
508
|
+
if (textParts.plain) {
|
|
509
|
+
const buf = getPart(textParts.plain.key);
|
|
510
|
+
if (buf)
|
|
511
|
+
body = {
|
|
512
|
+
...(body ?? {}),
|
|
513
|
+
text: decodePartText(buf, textParts.plain.charset),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
if (textParts.html) {
|
|
517
|
+
const buf = getPart(textParts.html.key);
|
|
518
|
+
if (buf)
|
|
519
|
+
body = {
|
|
520
|
+
...(body ?? {}),
|
|
521
|
+
html: decodePartText(buf, textParts.html.charset),
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return buildEmailObject(msg, seqNo, body);
|
|
526
|
+
});
|
|
443
527
|
messages.sort((a, b) => b.uid - a.uid);
|
|
444
528
|
return messages;
|
|
445
529
|
}
|
|
@@ -471,7 +555,11 @@ async function searchEmails(sessionId, criteria) {
|
|
|
471
555
|
const mailbox = criteria.mailbox ?? 'INBOX';
|
|
472
556
|
const lock = await s.client.getMailboxLock(mailbox);
|
|
473
557
|
try {
|
|
474
|
-
|
|
558
|
+
// 已选中同一邮箱时跳过 SELECT
|
|
559
|
+
const alreadySelected = s.client.mailbox && s.client.mailbox.path === mailbox;
|
|
560
|
+
if (!alreadySelected) {
|
|
561
|
+
await s.client.mailboxOpen(mailbox);
|
|
562
|
+
}
|
|
475
563
|
const searchQuery = {};
|
|
476
564
|
if (criteria.text)
|
|
477
565
|
searchQuery.text = criteria.text;
|
|
@@ -529,6 +617,8 @@ async function searchEmails(sessionId, criteria) {
|
|
|
529
617
|
if (criteria.limit && criteria.limit > 0) {
|
|
530
618
|
uids = uids.slice(0, criteria.limit);
|
|
531
619
|
}
|
|
620
|
+
// 只取元数据 + 只拉 text 正文(不下载附件),与 fetchEmails 一致
|
|
621
|
+
const needBody = criteria.fetchBody !== false;
|
|
532
622
|
const fields = {
|
|
533
623
|
uid: true,
|
|
534
624
|
flags: true,
|
|
@@ -538,15 +628,57 @@ async function searchEmails(sessionId, criteria) {
|
|
|
538
628
|
internalDate: true,
|
|
539
629
|
headers: true,
|
|
540
630
|
};
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const
|
|
631
|
+
// 第一段:元数据(含 bodyStructure),随后推导正文 part key
|
|
632
|
+
const entries = [];
|
|
633
|
+
const byUid = new Map();
|
|
544
634
|
let seq = 0;
|
|
635
|
+
let textParts = null;
|
|
545
636
|
for await (const msg of s.client.fetch({ uid: uids.join(',') }, fields, { uid: true })) {
|
|
546
637
|
seq++;
|
|
547
|
-
const
|
|
548
|
-
|
|
638
|
+
const entry = { msg, seq };
|
|
639
|
+
entries.push(entry);
|
|
640
|
+
byUid.set(msg.uid, entry);
|
|
641
|
+
if (needBody && !textParts && msg.bodyStructure) {
|
|
642
|
+
textParts = findTextParts(msg.bodyStructure);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// 第二段:只取 text/plain 与 text/html 正文(不下载附件)
|
|
646
|
+
if (needBody && textParts && (textParts.plain || textParts.html)) {
|
|
647
|
+
const bodyParts = [];
|
|
648
|
+
if (textParts.plain)
|
|
649
|
+
bodyParts.push({ key: textParts.plain.key });
|
|
650
|
+
if (textParts.html)
|
|
651
|
+
bodyParts.push({ key: textParts.html.key });
|
|
652
|
+
for await (const b of s.client.fetch({ uid: uids.join(',') }, { uid: true, bodyParts }, { uid: true })) {
|
|
653
|
+
const entry = byUid.get(b.uid);
|
|
654
|
+
if (entry && b.bodyParts) {
|
|
655
|
+
entry.bodyPartsMap = b.bodyParts;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
549
658
|
}
|
|
659
|
+
const messages = entries.map(({ msg, seq: seqNo, bodyPartsMap }) => {
|
|
660
|
+
let body;
|
|
661
|
+
if (needBody && bodyPartsMap && textParts) {
|
|
662
|
+
const getPart = (key) => bodyPartsMap.get(key) ?? bodyPartsMap.get(key.toUpperCase());
|
|
663
|
+
if (textParts.plain) {
|
|
664
|
+
const buf = getPart(textParts.plain.key);
|
|
665
|
+
if (buf)
|
|
666
|
+
body = {
|
|
667
|
+
...(body ?? {}),
|
|
668
|
+
text: decodePartText(buf, textParts.plain.charset),
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
if (textParts.html) {
|
|
672
|
+
const buf = getPart(textParts.html.key);
|
|
673
|
+
if (buf)
|
|
674
|
+
body = {
|
|
675
|
+
...(body ?? {}),
|
|
676
|
+
html: decodePartText(buf, textParts.html.charset),
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return buildEmailObject(msg, seqNo, body);
|
|
681
|
+
});
|
|
550
682
|
return messages;
|
|
551
683
|
}
|
|
552
684
|
finally {
|
|
@@ -556,58 +688,48 @@ async function searchEmails(sessionId, criteria) {
|
|
|
556
688
|
// ─── 标记操作 ──────────────────────────────────────────────
|
|
557
689
|
async function markAsRead(sessionId, uids, silent) {
|
|
558
690
|
const s = getSession(sessionId);
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
691
|
+
// 批量:一条 range 命令完成所有 UID,避免逐封往返
|
|
692
|
+
await s.client.messageFlagsAdd({ uid: uids.join(',') }, ['\\Seen'], {
|
|
693
|
+
silent,
|
|
694
|
+
uid: true,
|
|
695
|
+
});
|
|
565
696
|
}
|
|
566
697
|
async function markAsFlagged(sessionId, uids, flagged) {
|
|
567
698
|
const s = getSession(sessionId);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
await s.client.messageFlagsRemove({ uid: String(uid) }, ['\\Flagged'], {
|
|
576
|
-
uid: true,
|
|
577
|
-
});
|
|
578
|
-
}
|
|
699
|
+
const range = { uid: uids.join(',') };
|
|
700
|
+
if (flagged) {
|
|
701
|
+
await s.client.messageFlagsAdd(range, ['\\Flagged'], { uid: true });
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
await s.client.messageFlagsRemove(range, ['\\Flagged'], { uid: true });
|
|
579
705
|
}
|
|
580
706
|
}
|
|
581
707
|
async function moveEmails(sessionId, uids, destinationMailbox) {
|
|
582
708
|
const s = getSession(sessionId);
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
});
|
|
587
|
-
}
|
|
709
|
+
await s.client.messageMove({ uid: uids.join(',') }, destinationMailbox, {
|
|
710
|
+
uid: true,
|
|
711
|
+
});
|
|
588
712
|
}
|
|
589
713
|
async function copyEmails(sessionId, uids, destinationMailbox) {
|
|
590
714
|
const s = getSession(sessionId);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
});
|
|
595
|
-
}
|
|
715
|
+
await s.client.messageCopy({ uid: uids.join(',') }, destinationMailbox, {
|
|
716
|
+
uid: true,
|
|
717
|
+
});
|
|
596
718
|
}
|
|
597
719
|
async function deleteEmails(sessionId, uids) {
|
|
598
720
|
const s = getSession(sessionId);
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
failed
|
|
608
|
-
|
|
721
|
+
// 批量:一次 messageDelete 完成;失败则整批计入 failed(不再逐封往返)
|
|
722
|
+
try {
|
|
723
|
+
await s.client.messageDelete({ uid: uids.join(',') }, { uid: true });
|
|
724
|
+
return { success: uids, failed: [] };
|
|
725
|
+
}
|
|
726
|
+
catch (err) {
|
|
727
|
+
return {
|
|
728
|
+
success: [],
|
|
729
|
+
failed: uids,
|
|
730
|
+
error: err?.message ?? 'Delete failed',
|
|
731
|
+
};
|
|
609
732
|
}
|
|
610
|
-
return { success, failed };
|
|
611
733
|
}
|
|
612
734
|
async function expunge(sessionId) {
|
|
613
735
|
const s = getSession(sessionId);
|
|
@@ -677,7 +799,9 @@ async function fetchAttachment(sessionId, uid, partId) {
|
|
|
677
799
|
// 同时降低被 20s 超时误判的风险。不要设到接近无上限——imapflow 靠
|
|
678
800
|
// 「块是否被填满」(chunk.length >= chunkSize) 判断是否还有更多,
|
|
679
801
|
// 服务器若截断超大单次响应,会被误判为已结束 → 附件被静默截断。
|
|
680
|
-
|
|
802
|
+
// 分块大小默认 1MB,可由连接配置 downloadChunkSize 覆盖(与 Android 对齐)
|
|
803
|
+
const chunkSize = s.config?.downloadChunkSize ?? 1024 * 1024;
|
|
804
|
+
const download = await withTimeout(s.client.download(String(uid), partId, { uid: true, chunkSize }), 20000, `download ${partId}`);
|
|
681
805
|
const chunks = [];
|
|
682
806
|
for await (const chunk of download.content) {
|
|
683
807
|
chunks.push(chunk);
|