Loading mail/common/src/main/java/com/fsck/k9/mail/internet/MimeExtensions.kt +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ internal const val SEMICOLON = ';' internal const val EQUALS_SIGN = '=' internal const val ASTERISK = '*' internal const val SINGLE_QUOTE = '\'' internal const val BACKSLASH = '\\' internal fun Char.isTSpecial() = this in TSPECIALS Loading mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt +17 −1 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ object MimeParameterEncoder { private fun String.isQuotable() = all { it.isQuotable() } fun String.quoted(): String { private fun String.quoted(): String { // quoted-string = [CFWS] DQUOTE *([FWS] qcontent) [FWS] DQUOTE [CFWS] // qcontent = qtext / quoted-pair // quoted-pair = ("\" (VCHAR / WSP)) Loading @@ -165,6 +165,22 @@ object MimeParameterEncoder { } } // RFC 6532-style header values // Right now we only create such values for internal use (see IMAP BODYSTRUCTURE response parsing code) fun String.quotedUtf8(): String { return buildString(capacity = length + 16) { append(DQUOTE) for (c in this@quotedUtf8) { if (c == DQUOTE || c == BACKSLASH) { append('\\').append(c) } else { append(c) } } append(DQUOTE) } } private fun String.quotedLength(): Int { var length = 2 /* start and end quote */ for (c in this) { Loading mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt +3 −5 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ import com.fsck.k9.mail.internet.MimeHeader import com.fsck.k9.mail.internet.MimeMessageHelper import com.fsck.k9.mail.internet.MimeMultipart import com.fsck.k9.mail.internet.MimeParameterEncoder.isToken import com.fsck.k9.mail.internet.MimeParameterEncoder.quoted import com.fsck.k9.mail.internet.MimeParameterEncoder.quotedUtf8 import com.fsck.k9.mail.internet.MimeUtility import java.io.IOException import java.io.InputStream import java.text.SimpleDateFormat import java.util.Date import java.util.HashMap import java.util.LinkedHashSet import java.util.Locale import kotlin.math.max import kotlin.math.min Loading Loading @@ -891,7 +889,7 @@ internal class RealImapFolder( for (i in bodyParams.indices step 2) { val paramName = bodyParams.getString(i) val paramValue = bodyParams.getString(i + 1) val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quoted() val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quotedUtf8() contentType.append(String.format(";\r\n %s=%s", paramName, encodedValue)) } } Loading @@ -918,7 +916,7 @@ internal class RealImapFolder( for (i in bodyDispositionParams.indices step 2) { val paramName = bodyDispositionParams.getString(i).lowercase() val paramValue = bodyDispositionParams.getString(i + 1) val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quoted() val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quotedUtf8() contentDisposition.append(String.format(";\r\n %s=%s", paramName, encodedValue)) } } Loading mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -770,6 +770,15 @@ class RealImapFolderTest { ) } @Test fun `fetch() with UTF-8 encoded content type parameter`() { testHeaderFromBodyStructure( bodyStructure = """("text" "plain" ("name" "filenäme.ext") NIL NIL "7bit" 42 23)""", headerName = MimeHeader.HEADER_CONTENT_TYPE, expectedHeaderValue = "text/plain;\r\n name=\"filenäme.ext\"" ) } @Test fun `fetch() with simple content disposition parameter`() { testHeaderFromBodyStructure( Loading Loading @@ -810,6 +819,16 @@ class RealImapFolderTest { ) } @Test fun `fetch() with UTF-8 encoded content disposition parameter`() { testHeaderFromBodyStructure( bodyStructure = """("application" "octet-stream" NIL NIL NIL "8bit" 23 NIL """ + """("attachment" ("filename" "filenäme.ext")) NIL NIL)""", headerName = MimeHeader.HEADER_CONTENT_DISPOSITION, expectedHeaderValue = "attachment;\r\n filename=\"filenäme.ext\";\r\n size=23" ) } @Test fun fetch_withBodySaneFetchProfile_shouldIssueRespectiveCommand() { val folder = createFolder("Folder") Loading Loading
mail/common/src/main/java/com/fsck/k9/mail/internet/MimeExtensions.kt +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ internal const val SEMICOLON = ';' internal const val EQUALS_SIGN = '=' internal const val ASTERISK = '*' internal const val SINGLE_QUOTE = '\'' internal const val BACKSLASH = '\\' internal fun Char.isTSpecial() = this in TSPECIALS Loading
mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt +17 −1 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ object MimeParameterEncoder { private fun String.isQuotable() = all { it.isQuotable() } fun String.quoted(): String { private fun String.quoted(): String { // quoted-string = [CFWS] DQUOTE *([FWS] qcontent) [FWS] DQUOTE [CFWS] // qcontent = qtext / quoted-pair // quoted-pair = ("\" (VCHAR / WSP)) Loading @@ -165,6 +165,22 @@ object MimeParameterEncoder { } } // RFC 6532-style header values // Right now we only create such values for internal use (see IMAP BODYSTRUCTURE response parsing code) fun String.quotedUtf8(): String { return buildString(capacity = length + 16) { append(DQUOTE) for (c in this@quotedUtf8) { if (c == DQUOTE || c == BACKSLASH) { append('\\').append(c) } else { append(c) } } append(DQUOTE) } } private fun String.quotedLength(): Int { var length = 2 /* start and end quote */ for (c in this) { Loading
mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt +3 −5 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ import com.fsck.k9.mail.internet.MimeHeader import com.fsck.k9.mail.internet.MimeMessageHelper import com.fsck.k9.mail.internet.MimeMultipart import com.fsck.k9.mail.internet.MimeParameterEncoder.isToken import com.fsck.k9.mail.internet.MimeParameterEncoder.quoted import com.fsck.k9.mail.internet.MimeParameterEncoder.quotedUtf8 import com.fsck.k9.mail.internet.MimeUtility import java.io.IOException import java.io.InputStream import java.text.SimpleDateFormat import java.util.Date import java.util.HashMap import java.util.LinkedHashSet import java.util.Locale import kotlin.math.max import kotlin.math.min Loading Loading @@ -891,7 +889,7 @@ internal class RealImapFolder( for (i in bodyParams.indices step 2) { val paramName = bodyParams.getString(i) val paramValue = bodyParams.getString(i + 1) val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quoted() val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quotedUtf8() contentType.append(String.format(";\r\n %s=%s", paramName, encodedValue)) } } Loading @@ -918,7 +916,7 @@ internal class RealImapFolder( for (i in bodyDispositionParams.indices step 2) { val paramName = bodyDispositionParams.getString(i).lowercase() val paramValue = bodyDispositionParams.getString(i + 1) val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quoted() val encodedValue = if (paramValue.isToken()) paramValue else paramValue.quotedUtf8() contentDisposition.append(String.format(";\r\n %s=%s", paramName, encodedValue)) } } Loading
mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -770,6 +770,15 @@ class RealImapFolderTest { ) } @Test fun `fetch() with UTF-8 encoded content type parameter`() { testHeaderFromBodyStructure( bodyStructure = """("text" "plain" ("name" "filenäme.ext") NIL NIL "7bit" 42 23)""", headerName = MimeHeader.HEADER_CONTENT_TYPE, expectedHeaderValue = "text/plain;\r\n name=\"filenäme.ext\"" ) } @Test fun `fetch() with simple content disposition parameter`() { testHeaderFromBodyStructure( Loading Loading @@ -810,6 +819,16 @@ class RealImapFolderTest { ) } @Test fun `fetch() with UTF-8 encoded content disposition parameter`() { testHeaderFromBodyStructure( bodyStructure = """("application" "octet-stream" NIL NIL NIL "8bit" 23 NIL """ + """("attachment" ("filename" "filenäme.ext")) NIL NIL)""", headerName = MimeHeader.HEADER_CONTENT_DISPOSITION, expectedHeaderValue = "attachment;\r\n filename=\"filenäme.ext\";\r\n size=23" ) } @Test fun fetch_withBodySaneFetchProfile_shouldIssueRespectiveCommand() { val folder = createFolder("Folder") Loading