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
- onHtml(bp.content?.toString() ?: ""); foundHtml = true
419
+ foundHtml = true
420
+ if (needBody) onHtml(bp.content?.toString() ?: "")
401
421
  } else if (bp.isMimeType("text/plain") && !foundText && !isAttachmentLike(bp)) {
402
- onText(bp.content?.toString() ?: ""); foundText = true
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
- msgs.forEach { result.add(emailToEmailMessage(it, ensureFolderOpen())) }
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-email-imap-smtp",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "description": "The best of imap and smtp client of react native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",