From 50ce5d5e6cd0d720ff437d35eace545816848428 Mon Sep 17 00:00:00 2001 From: "test@dangofactory.cos" Date: Thu, 20 Aug 2026 16:57:18 +0000 Subject: Inital --- .../java/com/fsck/k9/mail/MessageBuilderDsl.kt | 65 +++++++++++++++++++ .../src/main/java/com/fsck/k9/mail/StringHelper.kt | 5 ++ .../main/java/com/fsck/k9/mail/SystemOutLogger.kt | 70 ++++++++++++++++++++ .../fsck/k9/mail/TestMessageConstructionUtils.java | 59 +++++++++++++++++ .../fsck/k9/mail/XOAuth2ChallengeParserTest.java | 15 +++++ .../com/fsck/k9/mail/helpers/KeyStoreProvider.java | 61 ++++++++++++++++++ .../java/com/fsck/k9/mail/helpers/TestMessage.java | 75 ++++++++++++++++++++++ .../fsck/k9/mail/helpers/TestMessageBuilder.java | 37 +++++++++++ .../k9/mail/helpers/TestTrustedSocketFactory.java | 46 +++++++++++++ .../k9/mail/helpers/VeryTrustingTrustManager.java | 33 ++++++++++ 10 files changed, 466 insertions(+) create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/MessageBuilderDsl.kt create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/StringHelper.kt create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/TestMessageConstructionUtils.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/XOAuth2ChallengeParserTest.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/helpers/KeyStoreProvider.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessage.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessageBuilder.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestTrustedSocketFactory.java create mode 100644 mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java (limited to 'mail/testing/src/main/java') diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/MessageBuilderDsl.kt b/mail/testing/src/main/java/com/fsck/k9/mail/MessageBuilderDsl.kt new file mode 100644 index 0000000..0e27b69 --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/MessageBuilderDsl.kt @@ -0,0 +1,65 @@ +package com.fsck.k9.mail + +import com.fsck.k9.mail.internet.MimeBodyPart +import com.fsck.k9.mail.internet.MimeMessage +import com.fsck.k9.mail.internet.MimeMessageHelper +import com.fsck.k9.mail.internet.MimeMultipart +import com.fsck.k9.mail.internet.TextBody +import com.fsck.k9.mailstore.BinaryMemoryBody + +fun buildMessage(block: PartBuilder.() -> Unit): Message { + return MimeMessage().also { message -> + PartBuilder(message).block() + } +} + +@DslMarker +annotation class MessageBuilderMarker + +@MessageBuilderMarker +class PartBuilder(private val part: Part) { + private var gotBodyBlock = false + + fun header(name: String, value: String) { + part.addHeader(name, value) + } + + fun textBody(text: String = "Hello World") { + require(!gotBodyBlock) { "Only one body block allowed" } + gotBodyBlock = true + + val body = TextBody(text) + MimeMessageHelper.setBody(part, body) + } + + fun dataBody(size: Int = 20 * 1024, encoding: String = "7bit") { + require(!gotBodyBlock) { "Only one body block allowed" } + gotBodyBlock = true + + val body = BinaryMemoryBody(ByteArray(size) { 'A'.code.toByte() }, encoding) + MimeMessageHelper.setBody(part, body) + } + + fun multipart(subType: String = "mixed", block: MultipartBuilder.() -> Unit) { + require(!gotBodyBlock) { "Only one body block allowed" } + gotBodyBlock = true + + val multipart = MimeMultipart.newInstance() + multipart.setSubType(subType) + MultipartBuilder(multipart).block() + MimeMessageHelper.setBody(part, multipart) + } +} + +@MessageBuilderMarker +class MultipartBuilder(private val multipart: Multipart) { + fun bodyPart(mimeType: String? = null, block: PartBuilder.() -> Unit) { + MimeBodyPart().let { bodyPart -> + if (mimeType != null) { + bodyPart.addHeader("Content-Type", mimeType) + } + multipart.addBodyPart(bodyPart) + PartBuilder(bodyPart).block() + } + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/StringHelper.kt b/mail/testing/src/main/java/com/fsck/k9/mail/StringHelper.kt new file mode 100644 index 0000000..cada3ad --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/StringHelper.kt @@ -0,0 +1,5 @@ +package com.fsck.k9.mail + +fun String.crlf() = replace("\n", "\r\n") + +fun String.removeLineBreaks() = replace(Regex("""\r|\n"""), "") diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt b/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt new file mode 100644 index 0000000..d0e0c15 --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt @@ -0,0 +1,70 @@ +package com.fsck.k9.mail + +import com.fsck.k9.logging.Logger + +class SystemOutLogger : Logger { + override fun v(message: String?, vararg args: Any?) { + System.out.printf("V/ ${message.orEmpty()}\n", *args) + } + + override fun v(t: Throwable?, message: String?, vararg args: Any?) { + t?.printStackTrace(System.out) + v(message, *args) + } + + override fun v(t: Throwable?) { + t?.printStackTrace(System.out) + } + + override fun d(message: String?, vararg args: Any?) { + System.out.printf("D/ ${message.orEmpty()}\n", *args) + } + + override fun d(t: Throwable?, message: String?, vararg args: Any?) { + t?.printStackTrace(System.out) + d(message, *args) + } + + override fun d(t: Throwable?) { + t?.printStackTrace(System.out) + } + + override fun i(message: String?, vararg args: Any?) { + System.out.printf("I/ ${message.orEmpty()}\n", *args) + } + + override fun i(t: Throwable?, message: String?, vararg args: Any?) { + t?.printStackTrace(System.out) + i(message, *args) + } + + override fun i(t: Throwable?) { + t?.printStackTrace(System.out) + } + + override fun w(message: String?, vararg args: Any?) { + System.out.printf("W/ ${message.orEmpty()}\n", *args) + } + + override fun w(t: Throwable?, message: String?, vararg args: Any?) { + t?.printStackTrace(System.out) + w(message, *args) + } + + override fun w(t: Throwable?) { + t?.printStackTrace(System.out) + } + + override fun e(message: String?, vararg args: Any?) { + System.out.printf("E/ ${message.orEmpty()}\n", *args) + } + + override fun e(t: Throwable?, message: String?, vararg args: Any?) { + t?.printStackTrace(System.out) + e(message, *args) + } + + override fun e(t: Throwable?) { + t?.printStackTrace(System.out) + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/TestMessageConstructionUtils.java b/mail/testing/src/main/java/com/fsck/k9/mail/TestMessageConstructionUtils.java new file mode 100644 index 0000000..9ce33aa --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/TestMessageConstructionUtils.java @@ -0,0 +1,59 @@ +package com.fsck.k9.mail; + + +import com.fsck.k9.mail.internet.MimeBodyPart; +import com.fsck.k9.mail.internet.MimeHeader; +import com.fsck.k9.mail.internet.MimeMessage; +import com.fsck.k9.mail.internet.MimeMessageHelper; +import com.fsck.k9.mail.internet.MimeMultipart; +import com.fsck.k9.mail.internet.TextBody; + + +public class TestMessageConstructionUtils { + public static MimeMessage messageFromBody(String subject, BodyPart bodyPart) throws MessagingException { + MimeMessage message = messageFromBody(bodyPart); + message.setSubject(subject); + return message; + } + + public static MimeMessage messageFromBody(BodyPart bodyPart) throws MessagingException { + MimeMessage message = new MimeMessage(); + MimeMessageHelper.setBody(message, bodyPart.getBody()); + if (bodyPart.getContentType() != null) { + message.setHeader("Content-Type", bodyPart.getContentType()); + } + message.setUid("msguid"); + return message; + } + + public static MimeBodyPart multipart(String type, BodyPart... subParts) throws MessagingException { + return multipart(type, null, subParts); + } + + public static MimeBodyPart multipart(String type, String typeParameters, BodyPart... subParts) throws MessagingException { + MimeMultipart multiPart = MimeMultipart.newInstance(); + multiPart.setSubType(type); + for (BodyPart subPart : subParts) { + multiPart.addBodyPart(subPart); + } + MimeBodyPart mimeBodyPart = new MimeBodyPart(multiPart); + if (typeParameters != null) { + mimeBodyPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, + mimeBodyPart.getContentType() + "; " + typeParameters); + } + return mimeBodyPart; + } + + public static BodyPart bodypart(String type) throws MessagingException { + return new MimeBodyPart(null, type); + } + + public static MimeBodyPart bodypart(String type, String text) throws MessagingException { + TextBody textBody = new TextBody(text); + return new MimeBodyPart(textBody, type); + } + + public static BodyPart bodypart(String type, Body body) throws MessagingException { + return new MimeBodyPart(body, type); + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/XOAuth2ChallengeParserTest.java b/mail/testing/src/main/java/com/fsck/k9/mail/XOAuth2ChallengeParserTest.java new file mode 100644 index 0000000..e130894 --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/XOAuth2ChallengeParserTest.java @@ -0,0 +1,15 @@ +package com.fsck.k9.mail; + +import com.fsck.k9.mail.filter.Base64; + + +public class XOAuth2ChallengeParserTest { + public static final String STATUS_400_RESPONSE = Base64.encode( + "{\"status\":\"400\",\"schemes\":\"bearer mac\",\"scope\":\"https://mail.google.com/\"}"); + public static final String STATUS_401_RESPONSE = Base64.encode( + "{\"status\":\"401\",\"schemes\":\"bearer mac\",\"scope\":\"https://mail.google.com/\"}"); + public static final String MISSING_STATUS_RESPONSE = Base64.encode( + "{\"schemes\":\"bearer mac\",\"scope\":\"https://mail.google.com/\"}"); + public static final String INVALID_RESPONSE = Base64.encode( + "{\"status\":\"401\",\"schemes\":\"bearer mac\",\"scope\":\"https://mail.google.com/\""); +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/KeyStoreProvider.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/KeyStoreProvider.java new file mode 100644 index 0000000..493e4af --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/KeyStoreProvider.java @@ -0,0 +1,61 @@ +package com.fsck.k9.mail.helpers; + + +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.X509Certificate; + + +public class KeyStoreProvider { + private static final String KEYSTORE_PASSWORD = "password"; + private static final String KEYSTORE_RESOURCE = "/keystore.jks"; + private static final String SERVER_CERTIFICATE_ALIAS = "mockimapserver"; + + + private final KeyStore keyStore; + + + public static KeyStoreProvider getInstance() { + KeyStore keyStore = loadKeyStore(); + return new KeyStoreProvider(keyStore); + } + + private static KeyStore loadKeyStore() { + try { + KeyStore keyStore = KeyStore.getInstance("JKS"); + + InputStream keyStoreInputStream = KeyStoreProvider.class.getResourceAsStream(KEYSTORE_RESOURCE); + try { + keyStore.load(keyStoreInputStream, KEYSTORE_PASSWORD.toCharArray()); + } finally { + keyStoreInputStream.close(); + } + + return keyStore; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private KeyStoreProvider(KeyStore keyStore) { + this.keyStore = keyStore; + } + + public KeyStore getKeyStore() { + return keyStore; + } + + public char[] getPassword() { + return KEYSTORE_PASSWORD.toCharArray(); + } + + public X509Certificate getServerCertificate() { + try { + KeyStore keyStore = loadKeyStore(); + return (X509Certificate) keyStore.getCertificate(SERVER_CERTIFICATE_ALIAS); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessage.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessage.java new file mode 100644 index 0000000..c867b0f --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessage.java @@ -0,0 +1,75 @@ +package com.fsck.k9.mail.helpers; + + +import java.io.IOException; +import java.io.OutputStream; + +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.internet.MimeMessage; +import okio.BufferedSink; +import okio.Okio; + + +class TestMessage extends MimeMessage { + private final long messageSize; + private final Address[] from; + private final Address[] to; + private final boolean hasAttachments; + + + TestMessage(TestMessageBuilder builder) { + from = toAddressArray(builder.from); + to = toAddressArray(builder.to); + hasAttachments = builder.hasAttachments; + messageSize = builder.messageSize; + } + + @Override + public Address[] getFrom() { + return from; + } + + @Override + public Address[] getRecipients(RecipientType type) { + switch (type) { + case TO: + return to; + case CC: + return new Address[0]; + case BCC: + return new Address[0]; + } + + throw new AssertionError("Missing switch case: " + type); + } + + @Override + public boolean hasAttachments() { + return hasAttachments; + } + + @Override + public long calculateSize() { + return messageSize; + } + + @Override + public void writeTo(OutputStream out) throws IOException, MessagingException { + BufferedSink bufferedSink = Okio.buffer(Okio.sink(out)); + bufferedSink.writeUtf8("[message data]"); + bufferedSink.emit(); + } + + private static Address[] toAddressArray(String[] emails) { + return emails == null ? new Address[0] : stringArrayToAddressArray(emails); + } + + private static Address[] stringArrayToAddressArray(String[] emails) { + Address addresses[] = new Address[emails.length]; + for (int i = 0; i < emails.length; i++) { + addresses[i] = new Address(emails[i]); + } + return addresses; + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessageBuilder.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessageBuilder.java new file mode 100644 index 0000000..f8d545e --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestMessageBuilder.java @@ -0,0 +1,37 @@ +package com.fsck.k9.mail.helpers; + + +import com.fsck.k9.mail.Message; + + +public class TestMessageBuilder { + String[] from; + String[] to; + boolean hasAttachments; + long messageSize; + + + public TestMessageBuilder from(String... email) { + from = email; + return this; + } + + public TestMessageBuilder to(String... email) { + to = email; + return this; + } + + public TestMessageBuilder setHasAttachments(boolean hasAttachments) { + this.hasAttachments = hasAttachments; + return this; + } + + public TestMessageBuilder messageSize(long messageSize) { + this.messageSize = messageSize; + return this; + } + + public Message build() { + return new TestMessage(this); + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestTrustedSocketFactory.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestTrustedSocketFactory.java new file mode 100644 index 0000000..1d32b1a --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/TestTrustedSocketFactory.java @@ -0,0 +1,46 @@ +package com.fsck.k9.mail.helpers; + + +import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; + +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.ssl.TrustedSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + + +public class TestTrustedSocketFactory implements TrustedSocketFactory { + private final X509Certificate serverCertificate; + + + public static TestTrustedSocketFactory newInstance() { + X509Certificate serverCertificate = KeyStoreProvider.getInstance().getServerCertificate(); + return new TestTrustedSocketFactory(serverCertificate); + } + + private TestTrustedSocketFactory(X509Certificate serverCertificate) { + this.serverCertificate = serverCertificate; + } + + @Override + public Socket createSocket(Socket socket, String host, int port, String clientCertificateAlias) + throws NoSuchAlgorithmException, KeyManagementException, MessagingException, IOException { + + TrustManager[] trustManagers = new TrustManager[] { new VeryTrustingTrustManager(serverCertificate) }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagers, null); + + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + return sslSocketFactory.createSocket( + socket, + socket.getInetAddress().getHostAddress(), + socket.getPort(), + true); + } +} diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java new file mode 100644 index 0000000..843358a --- /dev/null +++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java @@ -0,0 +1,33 @@ +package com.fsck.k9.mail.helpers; + + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + + +class VeryTrustingTrustManager implements X509TrustManager { + private final X509Certificate serverCertificate; + + + public VeryTrustingTrustManager(X509Certificate serverCertificate) { + this.serverCertificate = serverCertificate; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // Accept all certificates + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // Accept all certificates + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] { serverCertificate }; + } +} + -- cgit v1.2.1