diff --git a/README.md b/README.md
index f2e9d63..597cd86 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ repositories {
}
// Append dependency
-implementation("com.icerockdev.service:email-service:0.5.2")
+implementation("com.icerockdev.service:email-service:1.0.0")
````
## Library usage
@@ -37,13 +37,18 @@ implementation("com.icerockdev.service:email-service:0.5.2")
fromName = "From Person"
subject = "TEST EMAIL"
to = mutableMapOf("to@icerockdev.com" to "Test Person")
- html = "
Test test test
"
+ html = "Test test test
"
attachments = listOf(
Mail.Attachment(
file = File("test.pdf"),
name = "test.pdf"
)
)
+ inlineAttachments = listOf(
+ file = File("test.png"),
+ cidName = "testpng",
+ name = "test.png"
+ )
}.sendAsync()
````
diff --git a/email-service/build.gradle.kts b/email-service/build.gradle.kts
index aee9a4e..d3003dd 100644
--- a/email-service/build.gradle.kts
+++ b/email-service/build.gradle.kts
@@ -19,7 +19,7 @@ apply(plugin = "java")
apply(plugin = "kotlin")
group = "com.icerockdev.service"
-version = "0.5.2"
+version = "1.0.0"
val sourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
@@ -33,7 +33,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${properties["coroutines_version"]}")
// https://mvnrepository.com/artifact/org.apache.commons/commons-email
- implementation("org.apache.commons:commons-email:${properties["common_email_version"]}")
+ implementation("org.apache.commons:commons-email2-jakarta:${properties["common_email2_version"]}")
// logging
implementation("ch.qos.logback:logback-classic:${properties["logback_version"]}")
@@ -144,4 +144,3 @@ jreleaser {
}
}
}
-
diff --git a/email-service/src/main/kotlin/com/icerockdev/service/email/Mail.kt b/email-service/src/main/kotlin/com/icerockdev/service/email/Mail.kt
index a5c35c0..abfd06c 100644
--- a/email-service/src/main/kotlin/com/icerockdev/service/email/Mail.kt
+++ b/email-service/src/main/kotlin/com/icerockdev/service/email/Mail.kt
@@ -4,21 +4,21 @@
package com.icerockdev.service.email
+import jakarta.activation.DataSource
+import jakarta.activation.FileDataSource
+import jakarta.activation.URLDataSource
+import jakarta.mail.util.ByteArrayDataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import org.apache.commons.mail.DefaultAuthenticator
-import org.apache.commons.mail.Email
-import org.apache.commons.mail.EmailConstants.UTF_8
-import org.apache.commons.mail.HtmlEmail
+import org.apache.commons.mail2.core.EmailConstants.UTF_8
+import org.apache.commons.mail2.jakarta.DefaultAuthenticator
+import org.apache.commons.mail2.jakarta.Email
+import org.apache.commons.mail2.jakarta.HtmlEmail
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.net.URL
-import javax.activation.DataSource
-import javax.activation.FileDataSource
-import javax.activation.URLDataSource
-import javax.mail.util.ByteArrayDataSource
class Mail(private val coroutineScope: CoroutineScope?, private val config: SMTPConfig) {
@@ -38,6 +38,9 @@ class Mail(private val coroutineScope: CoroutineScope?, private val config: SMTP
var fromEmail: String = ""
var charset = UTF_8
var attachments: List = emptyList()
+ var inlineAttachments: List = emptyList()
+ var replyToEmail: String? = null
+ var replyToName: String? = null
private var isHtml: Boolean = false
private fun prepareEmail(): Email {
@@ -76,6 +79,14 @@ class Mail(private val coroutineScope: CoroutineScope?, private val config: SMTP
email.attach(attachment.dataSource, attachment.name, attachment.description)
}
+ inlineAttachments.forEach { inline ->
+ email.embed(inline.dataSource, inline.name, inline.cidName)
+ }
+
+ if (!replyToEmail.isNullOrBlank()) {
+ email.addReplyTo(replyToEmail, replyToName)
+ }
+
return email
}
@@ -114,6 +125,18 @@ class Mail(private val coroutineScope: CoroutineScope?, private val config: SMTP
)
}
+ class InlineAttachment(
+ val dataSource: DataSource,
+ val cidName: String,
+ val name: String,
+ ) {
+ constructor(file: File, cidName: String, name: String) : this(FileDataSource(file), cidName, name)
+ constructor(url: URL, cidName: String, name: String) : this(URLDataSource(url), cidName, name)
+ constructor(data: ByteArray, cidName: String, name: String, charset: String = UTF_8) : this(
+ ByteArrayDataSource(data, "application/octet-stream;charset=$charset"), cidName, name
+ )
+ }
+
private companion object {
val LOGGER: Logger = LoggerFactory.getLogger(Mail::class.java)
}
diff --git a/email-service/src/test/kotlin/EmailTest.kt b/email-service/src/test/kotlin/EmailTest.kt
index 9924754..75d763f 100644
--- a/email-service/src/test/kotlin/EmailTest.kt
+++ b/email-service/src/test/kotlin/EmailTest.kt
@@ -3,6 +3,7 @@
*/
import com.dumbster.smtp.SimpleSmtpServer
+import com.icerockdev.service.email.Mail
import com.icerockdev.service.email.MailerService
import com.icerockdev.service.email.SMTPConfig
import kotlinx.coroutines.CoroutineScope
@@ -12,8 +13,10 @@ import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
+import java.io.File
import kotlin.test.assertContains
import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -66,4 +69,55 @@ class EmailTest {
assertEquals(expected = 1, email.getHeaderValues("To").size)
assertTrue { email.getHeaderValues("To").contains("Test Person ") }
}
+
+ @Test
+ fun testSendWithInlineAttachments() = runBlocking {
+ val imageFile = File("src/test/resources/kotlin.png")
+ val cidName = "kotlin-name"
+ val name = "kotlinlogo"
+
+ mailerService.compose().apply {
+ fromEmail = "from@icerockdev.com"
+ fromName = "From Person"
+ subject = "TEST EMAIL"
+ to = mutableMapOf("to@icerockdev.com" to "Test Person")
+ html = "Test Inline
"
+
+ inlineAttachments = listOf(
+ Mail.InlineAttachment(imageFile, cidName, name)
+ )
+ }.sendAsync().join()
+
+ val emails = server.receivedEmails
+ val email = emails[0]
+
+ assertContains(email.body, "
")
+ assertContains(email.body, "Content-ID: <$cidName>")
+ assertContains(email.body, "Content-Disposition: inline")
+ assertContains(email.body, "name=$name")
+ }
+
+ @Test
+ fun testSendWithReplyTo() = runBlocking {
+ val replyToEmail = "replyto@icerockdev.com"
+ val replyToName = "Reply Person"
+
+ mailerService.compose().apply {
+ fromEmail = "from@icerockdev.com"
+ fromName = "From Person"
+ subject = "TEST EMAIL"
+ to = mutableMapOf("to@icerockdev.com" to "Test Person")
+ html = "Test test test
"
+ this.replyToEmail = replyToEmail
+ this.replyToName = replyToName
+ }.sendAsync().join()
+
+ val emails = server.receivedEmails
+ assertEquals(1, emails.size)
+ val email = emails[0]
+
+ val replyToHeader = email.getHeaderValue("Reply-To")
+ assertNotNull(replyToHeader)
+ assertEquals("$replyToName <$replyToEmail>", replyToHeader)
+ }
}
diff --git a/email-service/src/test/resources/kotlin.png b/email-service/src/test/resources/kotlin.png
new file mode 100644
index 0000000..ac4a2da
Binary files /dev/null and b/email-service/src/test/resources/kotlin.png differ
diff --git a/gradle.properties b/gradle.properties
index 85efe6e..d51793a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,4 +6,4 @@ logback_version=1.4.5
kotlin.code.style=official
kotlin_version=1.7.21
coroutines_version=1.6.4
-common_email_version=1.5
+common_email2_version=2.0.0-M1