react-native-email-imap-smtp 0.3.33 → 0.3.35
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.
|
@@ -13,6 +13,8 @@ import jakarta.mail.search.*
|
|
|
13
13
|
import java.io.ByteArrayOutputStream
|
|
14
14
|
import java.io.File
|
|
15
15
|
import java.io.FileOutputStream
|
|
16
|
+
import java.net.Inet4Address
|
|
17
|
+
import java.net.InetAddress
|
|
16
18
|
import java.security.cert.X509Certificate
|
|
17
19
|
import java.time.ZoneId
|
|
18
20
|
import java.time.ZonedDateTime
|
|
@@ -67,9 +69,24 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
67
69
|
ctx.socketFactory
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/** 解析 host 为「优先 IPv4」的地址:
|
|
73
|
+
* - 默认走 IPv4,避免 JavaMail 优先选 IPv6 导致模拟器等无 IPv6 出站路由的环境连接超时;
|
|
74
|
+
* - 若域名无 IPv4(纯 IPv6-only 网络),回退用第一个地址(可能是 IPv6),保证仍可连接。
|
|
75
|
+
* 解析失败则回退原 host。 */
|
|
76
|
+
private fun resolveIpv4(host: String): String {
|
|
77
|
+
return try {
|
|
78
|
+
val addrs = InetAddress.getAllByName(host)
|
|
79
|
+
addrs.firstOrNull { it is Inet4Address }?.hostAddress
|
|
80
|
+
?: addrs.firstOrNull()?.hostAddress
|
|
81
|
+
?: host
|
|
82
|
+
} catch (_: Exception) {
|
|
83
|
+
host
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
70
87
|
private fun imapSession(config: ConnectionConfig): Session {
|
|
71
88
|
val props = Properties().apply {
|
|
72
|
-
put("mail.imap.host", config.host)
|
|
89
|
+
put("mail.imap.host", resolveIpv4(config.host))
|
|
73
90
|
put("mail.imap.port", config.port.toInt().toString())
|
|
74
91
|
put("mail.imap.connectiontimeout", config.timeout.toInt().toString())
|
|
75
92
|
put("mail.imap.timeout", config.timeout.toInt().toString())
|
|
@@ -104,7 +121,7 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
104
121
|
|
|
105
122
|
private fun smtpSession(config: SmtpConnectionConfig): Session {
|
|
106
123
|
val props = Properties().apply {
|
|
107
|
-
put("mail.smtp.host", config.host)
|
|
124
|
+
put("mail.smtp.host", resolveIpv4(config.host))
|
|
108
125
|
put("mail.smtp.port", config.port.toInt().toString())
|
|
109
126
|
put("mail.smtp.auth", "true")
|
|
110
127
|
put("mail.smtp.connectiontimeout", config.timeout.toInt().toString())
|
|
@@ -235,7 +252,7 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
235
252
|
|
|
236
253
|
// ─── 邮件解析 ─────────────────────────────────────────────
|
|
237
254
|
|
|
238
|
-
private fun emailToEmailMessage(msg: Message, folder: IMAPFolder): EmailMessage {
|
|
255
|
+
private fun emailToEmailMessage(msg: Message, folder: IMAPFolder, needBody: Boolean = true): EmailMessage {
|
|
239
256
|
val mime = msg as? MimeMessage ?: return EmailMessage(
|
|
240
257
|
uid = 0.0, sequenceNumber = msg.messageNumber.toDouble(),
|
|
241
258
|
subject = "", from = EmailAddress("", ""), to = emptyArray(),
|
|
@@ -269,9 +286,10 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
269
286
|
onHtml = { bodyHtml = it },
|
|
270
287
|
onEmbedded = { embeddedAttachments.add(it) },
|
|
271
288
|
onAttachment = { attachments.add(it) },
|
|
272
|
-
fullData = false
|
|
289
|
+
fullData = false,
|
|
290
|
+
needBody = needBody)
|
|
273
291
|
} catch (_: Exception) {
|
|
274
|
-
bodyText = mime.content?.toString() ?: ""
|
|
292
|
+
if (needBody) bodyText = mime.content?.toString() ?: ""
|
|
275
293
|
}
|
|
276
294
|
|
|
277
295
|
val headers = mutableMapOf<String, String>()
|
|
@@ -375,19 +393,20 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
375
393
|
onEmbedded: (String) -> Unit,
|
|
376
394
|
onAttachment: (AttachmentInfo) -> Unit,
|
|
377
395
|
fullData: Boolean = true,
|
|
396
|
+
needBody: Boolean = true,
|
|
378
397
|
) {
|
|
379
398
|
if (part.isMimeType("text/plain")) {
|
|
380
399
|
// text/plain 也可能是附件(带 attachment disposition / filename / name):
|
|
381
400
|
// 先判定附件,再当作正文。与 Web 端一致(1.txt/2.txt 等 text/plain 附件)。
|
|
382
401
|
if (isAttachmentLike(part)) {
|
|
383
402
|
handleAttachmentLeaf(part, onEmbedded, onAttachment, fullData)
|
|
384
|
-
} else {
|
|
403
|
+
} else if (needBody) {
|
|
385
404
|
onText(part.content?.toString() ?: "")
|
|
386
405
|
}
|
|
387
406
|
} else if (part.isMimeType("text/html")) {
|
|
388
407
|
if (isAttachmentLike(part)) {
|
|
389
408
|
handleAttachmentLeaf(part, onEmbedded, onAttachment, fullData)
|
|
390
|
-
} else {
|
|
409
|
+
} else if (needBody) {
|
|
391
410
|
onHtml(part.content?.toString() ?: "")
|
|
392
411
|
}
|
|
393
412
|
} else if (part.isMimeType("multipart/alternative")) {
|
|
@@ -397,12 +416,14 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
397
416
|
for (i in 0 until mp.count) {
|
|
398
417
|
val bp = mp.getBodyPart(i)
|
|
399
418
|
if (bp.isMimeType("text/html") && !foundHtml && !isAttachmentLike(bp)) {
|
|
400
|
-
|
|
419
|
+
foundHtml = true
|
|
420
|
+
if (needBody) onHtml(bp.content?.toString() ?: "")
|
|
401
421
|
} else if (bp.isMimeType("text/plain") && !foundText && !isAttachmentLike(bp)) {
|
|
402
|
-
|
|
422
|
+
foundText = true
|
|
423
|
+
if (needBody) onText(bp.content?.toString() ?: "")
|
|
403
424
|
} else if (bp.isMimeType("multipart/*")) {
|
|
404
425
|
// alternative 内嵌套的子 multipart(related/mixed 等)→ 递归,不遗漏附件
|
|
405
|
-
parseBodyParts(bp, onText, onHtml, onEmbedded, onAttachment, fullData)
|
|
426
|
+
parseBodyParts(bp, onText, onHtml, onEmbedded, onAttachment, fullData, needBody)
|
|
406
427
|
} else {
|
|
407
428
|
// alternative 内的非正文叶子(内嵌图/附件)→ 附件判定
|
|
408
429
|
handleAttachmentLeaf(bp, onEmbedded, onAttachment, fullData)
|
|
@@ -411,7 +432,7 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
411
432
|
} else if (part.isMimeType("multipart/*")) {
|
|
412
433
|
val mp = part.content as? Multipart ?: return
|
|
413
434
|
for (i in 0 until mp.count) {
|
|
414
|
-
parseBodyParts(mp.getBodyPart(i), onText, onHtml, onEmbedded, onAttachment, fullData)
|
|
435
|
+
parseBodyParts(mp.getBodyPart(i), onText, onHtml, onEmbedded, onAttachment, fullData, needBody)
|
|
415
436
|
}
|
|
416
437
|
} else {
|
|
417
438
|
handleAttachmentLeaf(part, onEmbedded, onAttachment, fullData)
|
|
@@ -897,7 +918,8 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
897
918
|
}
|
|
898
919
|
|
|
899
920
|
try {
|
|
900
|
-
|
|
921
|
+
val needBody = options.fetchBody != false
|
|
922
|
+
msgs.forEach { result.add(emailToEmailMessage(it, ensureFolderOpen(), needBody)) }
|
|
901
923
|
} catch (e: Exception) {
|
|
902
924
|
Log.e(TAG, "Fetch emails failed", e)
|
|
903
925
|
}
|
|
@@ -958,7 +980,7 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
958
980
|
}
|
|
959
981
|
val limit = minOf(criteria.limit?.toInt() ?: msgs.size, msgs.size, MAX_FETCH_COUNT)
|
|
960
982
|
val sorted = msgs.sortedByDescending { it.receivedDate?.time ?: 0L }.take(limit)
|
|
961
|
-
sorted.forEach { result.add(emailToEmailMessage(it, folder)) }
|
|
983
|
+
sorted.forEach { result.add(emailToEmailMessage(it, folder, criteria.fetchBody != false)) }
|
|
962
984
|
return@async result.toTypedArray()
|
|
963
985
|
}
|
|
964
986
|
|
|
@@ -1022,7 +1044,7 @@ class EmailImapSmtp : HybridEmailImapSmtpSpec() {
|
|
|
1022
1044
|
|
|
1023
1045
|
val limit = minOf(criteria.limit?.toInt() ?: sortedMsgs.size, sortedMsgs.size, MAX_FETCH_COUNT)
|
|
1024
1046
|
for (i in 0 until limit) {
|
|
1025
|
-
result.add(emailToEmailMessage(sortedMsgs[i], folder))
|
|
1047
|
+
result.add(emailToEmailMessage(sortedMsgs[i], folder, criteria.fetchBody != false))
|
|
1026
1048
|
}
|
|
1027
1049
|
}
|
|
1028
1050
|
} catch (e: Exception) {
|
package/package.json
CHANGED