Skip to content

Commit

Permalink
Add AddressHeaderBuilder to correctly fold recipient headers
Browse files Browse the repository at this point in the history
  • Loading branch information
cketti committed Feb 14, 2019
1 parent 272a03e commit 58d72e0
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.fsck.k9.mail.internet

import com.fsck.k9.mail.Address

/**
* Encode and fold email addresses for use in header values.
*/
object AddressHeaderBuilder {
@JvmStatic
fun createHeaderValue(addresses: Array<Address>): String {
require(addresses.isNotEmpty()) { "addresses must not be empty" }

return buildString {
var lineLength = 0
for ((index, address) in addresses.withIndex()) {
val encodedAddress = address.toEncodedString()
val encodedAddressLength = encodedAddress.length

if (index > 0 && lineLength + 2 + encodedAddressLength + 1 > RECOMMENDED_MAX_LINE_LENGTH) {
append(",$CRLF ")
append(encodedAddress)
lineLength = encodedAddressLength + 1
} else {
if (index > 0) {
append(", ")
lineLength += 2
}

append(encodedAddress)
lineLength += encodedAddressLength
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.fsck.k9.mail.internet


// RFC 5322, section 2.1.1
internal const val RECOMMENDED_MAX_LINE_LENGTH = 78

// RFC 2045: tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "="
private val TSPECIALS = charArrayOf('(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.fsck.k9.mail.internet

import com.fsck.k9.mail.Address
import com.fsck.k9.mail.K9LibRobolectricTestRunner
import com.fsck.k9.mail.crlf
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith


@RunWith(K9LibRobolectricTestRunner::class)
class AddressHeaderBuilderTest {

@Test
fun createHeaderValue_withSingleAddress() {
val addresses = arrayOf(Address("test@domain.example"))

val headerValue = AddressHeaderBuilder.createHeaderValue(addresses)

assertEquals("test@domain.example", headerValue)
}

@Test
fun createHeaderValue_withTwoAddressesThatFitOnSingleLine() {
val addresses = arrayOf(
Address("one@domain.example"),
Address("two@domain.example")
)

val headerValue = AddressHeaderBuilder.createHeaderValue(addresses)

assertEquals("one@domain.example, two@domain.example", headerValue)
}

@Test
fun createHeaderValue_withMultipleAddressesThatNeedWrapping() {
val addresses = arrayOf(
Address("one@domain.example", "Person One"),
Address("two+because.i.can@this.is.quite.some.domain.example", "Person \"Long Email Address\" Two"),
Address("three@domain.example", "Person Three"),
Address("four@domain.example", "Person Four"),
Address("five@domain.example", "Person Five")
)

val headerValue = AddressHeaderBuilder.createHeaderValue(addresses)

assertEquals("""
|Person One <one@domain.example>,
| "Person \"Long Email Address\" Two" <two+because.i.can@this.is.quite.some.domain.example>,
| Person Three <three@domain.example>, Person Four <four@domain.example>,
| Person Five <five@domain.example>
""".trimMargin().crlf(), headerValue)
}

@Test(expected = IllegalArgumentException::class)
fun createHeaderValue_withoutAddresses_shouldThrow() {
AddressHeaderBuilder.createHeaderValue(emptyArray())
}
}

0 comments on commit 58d72e0

Please sign in to comment.