diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index a5c7ecb6fec..6620515123e 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -7407,3 +7407,120 @@ u25YFH0NKiziF0Y2r0pd4NQhoAirGdamVPfWKzvHhZ8XWhklr1Ve+3X9NqZIOcXQ x1rrD3BL6LwVJtHcfQxuT3IRDnjnYXZAUOVAlQ== =NFKb -----END PGP PUBLIC KEY BLOCK----- + +pub 7E0DC505BB5D48AF +uid Eclipse Project for JavaMail +sub 0C27E8FAC93B3B19 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +on9Ew/TkicDfefP6oCRexBeTNEYK3+8= +=tOsa +-----END PGP PUBLIC KEY BLOCK----- diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index eedbce6676b..708209da6a3 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -90,6 +90,7 @@ + diff --git a/renovate.json b/renovate.json index 3ddbebc5a05..acf9a418e7f 100644 --- a/renovate.json +++ b/renovate.json @@ -55,6 +55,14 @@ "com.helger{/,}**" ] }, + { + "allowedVersions": "< 2.0.0", + "description": "jakarta.mail 2.x renames the javax.mail packages to jakarta.mail, so JMeter stays on 1.6.x", + "groupName": "com.sun.mail", + "matchPackageNames": [ + "com.sun.mail:jakarta.mail" + ] + }, { "groupName": "github official actions", "description": "actions/* and github/* are maintained by GitHub/Microsoft and can be updated together", diff --git a/src/bom-thirdparty/build.gradle.kts b/src/bom-thirdparty/build.gradle.kts index 7a7307e9f21..c239f8543d5 100644 --- a/src/bom-thirdparty/build.gradle.kts +++ b/src/bom-thirdparty/build.gradle.kts @@ -75,7 +75,7 @@ dependencies { api("io.burt:jmespath-jackson:0.6.0") api("jakarta.jms:jakarta.jms-api:3.1.0") api("javax.activation:javax.activation-api:1.2.0") - api("javax.mail:mail:1.5.0-b01") + api("com.sun.mail:jakarta.mail:1.6.8") api("jcharts:jcharts:0.7.5") api("junit:junit:4.13.2") { because("ApacheJMeter_junit depends on junit4") diff --git a/src/components/build.gradle.kts b/src/components/build.gradle.kts index 5653af3c135..3ed2fc3fbd8 100644 --- a/src/components/build.gradle.kts +++ b/src/components/build.gradle.kts @@ -35,8 +35,11 @@ dependencies { ) } - api("javax.mail:mail") { - exclude("javax.activation", "activation") + api("com.sun.mail:jakarta.mail") { + // jakarta.mail 1.6.x keeps the javax.mail namespace, however, it + // transitively pulls jakarta.activation that duplicates the + // javax.activation classes we already get from com.sun.activation:javax.activation + exclude("com.sun.activation", "jakarta.activation") } // There's no javax.activation:activation:1.2.0, so we use com.sun... runtimeOnly("com.sun.activation:javax.activation") diff --git a/src/dist/src/dist/expected_release_jars.csv b/src/dist/src/dist/expected_release_jars.csv index f81caaea10f..3743c7f3686 100644 --- a/src/dist/src/dist/expected_release_jars.csv +++ b/src/dist/src/dist/expected_release_jars.csv @@ -76,6 +76,7 @@ 595531,jackson-core-2.21.2.jar 1701926,jackson-databind-2.21.2.jar 58964,jakarta.jms-api-3.1.0.jar +677319,jakarta.mail-1.6.8.jar 78030,javax.activation-1.2.0.jar 142391,jcharts-0.7.5.jar 16555,jcl-over-slf4j-1.7.36.jar @@ -106,7 +107,6 @@ 351126,log4j-api-2.26.0.jar 2015101,log4j-core-2.26.0.jar 30211,log4j-slf4j2-impl-2.26.0.jar -519087,mail-1.5.0-b01.jar 120556,miglayout-core-11.4.3.jar 23361,miglayout-swing-11.4.3.jar 99796,neo4j-bolt-connection-10.1.0.jar diff --git a/src/licenses/build.gradle.kts b/src/licenses/build.gradle.kts index 557d6779860..3fbc8001195 100644 --- a/src/licenses/build.gradle.kts +++ b/src/licenses/build.gradle.kts @@ -137,8 +137,10 @@ val gatherBinaryLicenses by tasks.registering(GatherLicenseTask::class) { overrideLicense("com.sun.mail:all") { // Multiple licenses, specify explicitly - expectedLicense = SimpleLicense("CDDL", uri("http://www.sun.com/cddl")) and SimpleLicense("GPLv2+CE", uri("https://glassfish.java.net/public/CDDL+GPL_1_1.html")) - effectiveLicense = SpdxLicense.CDDL_1_0 and (SpdxLicense.GPL_2_0_or_later with SpdxLicenseException.Classpath_exception_2_0) + expectedLicense = SpdxLicense.EPL_2_0 and + SimpleLicense("GPL2 w/ CPE", uri("https://www.gnu.org/software/classpath/license.html")) and + SimpleLicense("EDL 1.0", uri("http://www.eclipse.org/org/documents/edl-v10.php")) + effectiveLicense = SpdxLicense.EPL_2_0 } overrideLicense("com.sun.activation:javax.activation:1.2.0") { expectedLicense = SimpleLicense("CDDL/GPLv2+CE", uri("https://github.com/javaee/activation/blob/master/LICENSE.txt")) diff --git a/src/protocol/mail/build.gradle.kts b/src/protocol/mail/build.gradle.kts index 204f331e2d0..a2820eda595 100644 --- a/src/protocol/mail/build.gradle.kts +++ b/src/protocol/mail/build.gradle.kts @@ -22,8 +22,11 @@ plugins { dependencies { api(projects.src.core) - api("javax.mail:mail") { - exclude("javax.activation", "activation") + api("com.sun.mail:jakarta.mail") { + // jakarta.mail 1.6.x keeps the javax.mail namespace, however, it + // transitively pulls jakarta.activation that duplicates the + // javax.activation classes we already get from com.sun.activation:javax.activation + exclude("com.sun.activation", "jakarta.activation") } // There's no javax.activation:activation:1.2.0, so we use com.sun... runtimeOnly("com.sun.activation:javax.activation") diff --git a/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommand.java b/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommand.java index 36331e713c4..cd32e902deb 100644 --- a/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommand.java +++ b/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommand.java @@ -23,6 +23,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -34,13 +35,17 @@ import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; +import javax.mail.Part; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; +import javax.mail.internet.ContentDisposition; +import javax.mail.internet.ContentType; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; +import javax.mail.internet.ParameterList; import javax.net.ssl.SSLContext; import org.apache.jmeter.config.Argument; @@ -193,9 +198,9 @@ private Message buildMessage() throws MessagingException, IOException { body.setText(mailBody); multipart.addBodyPart(body); for (File f : attachments) { - BodyPart attach = new MimeBodyPart(); - attach.setFileName(f.getName()); + MimeBodyPart attach = new MimeBodyPart(); attach.setDataHandler(new DataHandler(new FileDataSource(f.getAbsolutePath()))); + setAttachmentFileName(attach, f.getName()); multipart.addBodyPart(attach); } message.setContent(multipart); @@ -241,6 +246,55 @@ private Message buildMessage() throws MessagingException, IOException { return message; } + /** + * Sets the attachment file name in the filename parameter of the + * {@code Content-Disposition} header and in the name parameter of the + * {@code Content-Type} header, encoded as UTF-8 according to RFC 2231. + *

+ * {@link MimeBodyPart#setFileName(String)} encodes the parameters with + * {@code MimeUtility.getDefaultMIMECharset()}, which falls back to the default + * charset of the JVM (for example {@code windows-1252} on Windows). Characters + * that are not representable in that charset are replaced with {@code ?}, so + * recipients see a mangled file name. Encoding the parameters with UTF-8 + * explicitly keeps the file name intact regardless of the platform charset. + *

+ * + * @param attachment + * body part carrying the attachment + * @param fileName + * file name of the attachment + * @throws MessagingException + * when the headers can not be updated + */ + private static void setAttachmentFileName(MimeBodyPart attachment, String fileName) throws MessagingException { + String disposition = attachment.getHeader("Content-Disposition", null); + ContentDisposition contentDisposition = new ContentDisposition(disposition == null ? Part.ATTACHMENT : disposition); + ParameterList parameters = contentDisposition.getParameterList(); + if (parameters == null) { + parameters = new ParameterList(); + contentDisposition.setParameterList(parameters); + } + parameters.set("filename", fileName, StandardCharsets.UTF_8.name()); + attachment.setHeader("Content-Disposition", contentDisposition.toString()); + + // The name parameter of Content-Type repeats the file name for legacy + // mail clients, so it must be encoded as well + String contentType = attachment.getHeader("Content-Type", null); + if (contentType == null && attachment.getDataHandler() != null) { + contentType = attachment.getDataHandler().getContentType(); + } + if (contentType != null) { + ContentType type = new ContentType(contentType); + parameters = type.getParameterList(); + if (parameters == null) { + parameters = new ParameterList(); + type.setParameterList(parameters); + } + parameters.set("name", fileName, StandardCharsets.UTF_8.name()); + attachment.setHeader("Content-Type", type.toString()); + } + } + private void configureCertificateTrust(Properties props) throws IOException { if (trustAllCerts) { if (useSSL) { diff --git a/src/protocol/mail/src/test/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommandTest.java b/src/protocol/mail/src/test/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommandTest.java new file mode 100644 index 00000000000..c18e53007be --- /dev/null +++ b/src/protocol/mail/src/test/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SendMailCommandTest.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jmeter.protocol.smtp.sampler.protocol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Collections; + +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.Multipart; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class SendMailCommandTest { + + // Russian for "text" + private static final String NON_ASCII_WORD = "\u0442\u0435\u043a\u0441\u0442"; + private static final String NON_ASCII_FILE_NAME = NON_ASCII_WORD + ".txt"; + private static final String ENCODED_NON_ASCII_WORD = "%D1%82%D0%B5%D0%BA%D1%81%D1%82"; + + @TempDir + File tempDir; + + @Test + void nonAsciiAttachmentFileNameIsEncodedAsUtf8() throws Exception { + byte[] rawMessage = createMessageWithAttachment(NON_ASCII_FILE_NAME); + String message = new String(rawMessage, StandardCharsets.UTF_8); + // The file name is carried by both the filename parameter of Content-Disposition + // and the name parameter of Content-Type, and both must be encoded as UTF-8 + // according to RFC 2231, so recipients see the original name on every platform + assertTrue( + message.contains("filename*=UTF-8''" + ENCODED_NON_ASCII_WORD + ".txt"), + "RFC 2231 encoded filename parameter expected in:\n" + message); + assertTrue( + message.contains("name*=UTF-8''" + ENCODED_NON_ASCII_WORD + ".txt"), + "RFC 2231 encoded name parameter expected in:\n" + message); + // Parsing the message back must return the original name, no matter how + // the name would have been mangled + assertEquals(NON_ASCII_FILE_NAME, parseAttachmentFileName(rawMessage), "decoded attachment file name"); + } + + @Test + void asciiAttachmentFileNameIsNotEncoded() throws Exception { + byte[] rawMessage = createMessageWithAttachment("attachment.txt"); + String message = new String(rawMessage, StandardCharsets.UTF_8); + assertTrue( + message.contains("filename=attachment.txt"), + "plain filename parameter expected in:\n" + message); + assertTrue( + message.contains("name=attachment.txt"), + "plain name parameter expected in:\n" + message); + assertFalse(message.contains("filename*="), "unexpected RFC 2231 encoding in:\n" + message); + assertFalse(message.contains("name*="), "unexpected RFC 2231 encoding in:\n" + message); + assertEquals("attachment.txt", parseAttachmentFileName(rawMessage), "decoded attachment file name"); + } + + @Test + void longAsciiAttachmentFileNameIsSplitIntoContinuations() throws Exception { + String fileName = "a".repeat(80) + ".txt"; + byte[] rawMessage = createMessageWithAttachment(fileName); + String message = new String(rawMessage, StandardCharsets.UTF_8); + // javax.mail splits parameters longer than 60 characters into + // RFC 2231 continuations, and the parts must reassemble into the name + assertTrue(message.contains("filename*0="), "continuation filename*0 expected in:\n" + message); + assertTrue(message.contains("filename*1="), "continuation filename*1 expected in:\n" + message); + assertTrue(message.contains("name*0="), "continuation name*0 expected in:\n" + message); + assertEquals(fileName, parseAttachmentFileName(rawMessage), "decoded attachment file name"); + } + + @Test + void longNonAsciiAttachmentFileNameIsEncodedAsSingleParameter() throws Exception { + // Long in characters, yet within the 255 byte file name limit of common file systems + String fileName = NON_ASCII_WORD.repeat(20) + ".txt"; + byte[] rawMessage = createMessageWithAttachment(fileName); + String message = new String(rawMessage, StandardCharsets.UTF_8); + // Unlike plain values, encoded values are never split, they stay a + // single filename* parameter regardless of their length + assertTrue(message.contains("filename*=UTF-8''"), "single encoded filename parameter expected in:\n" + message); + assertFalse(message.contains("filename*0"), "unexpected continuation in:\n" + message); + assertEquals(fileName, parseAttachmentFileName(rawMessage), "decoded attachment file name"); + } + + private byte[] createMessageWithAttachment(String attachmentName) throws Exception { + File attachment = new File(tempDir, attachmentName); + Files.writeString(attachment.toPath(), "attachment content", StandardCharsets.UTF_8); + + SendMailCommand sendMailCommand = new SendMailCommand(); + sendMailCommand.setSmtpServer("localhost"); + sendMailCommand.setSmtpPort("25"); + sendMailCommand.setConnectionTimeOut("1000"); + sendMailCommand.setTimeOut("1000"); + sendMailCommand.setSender("from@example.com"); + sendMailCommand.setReceiverTo(Collections.singletonList(new InternetAddress("to@example.com"))); + sendMailCommand.setSubject("attachment file name test"); + sendMailCommand.setMailBody("body"); + sendMailCommand.addAttachment(attachment); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + sendMailCommand.prepareMessage().writeTo(outputStream); + return outputStream.toByteArray(); + } + + private static String parseAttachmentFileName(byte[] rawMessage) throws Exception { + MimeMessage parsed = new MimeMessage(null, new ByteArrayInputStream(rawMessage)); + Multipart multipart = (Multipart) parsed.getContent(); + BodyPart attachment = multipart.getBodyPart(1); + return attachment.getFileName(); + } +} diff --git a/xdocs/changes.xml b/xdocs/changes.xml index 13e0d097e6d..c31c50dd408 100644 --- a/xdocs/changes.xml +++ b/xdocs/changes.xml @@ -98,6 +98,11 @@ Summary
  • Update json-path to 2.10.0 for JSON query expressions.
  • Update Neo4j Java driver to 6.x for Bolt-based database tests.
  • Update Rhino JavaScript engine to 1.8.0 for JSR-223 JavaScript execution.
  • +
  • Update javax.mail to 1.6.8 from 1.5.0-b01, now under the coordinates + com.sun.mail:jakarta.mail: the jar in lib/ becomes + jakarta.mail-1.6.8.jar, the published POMs of ApacheJMeter_mail + and ApacheJMeter_components depend on com.sun.mail:jakarta.mail, and the + domain in the generated Message-ID becomes the canonical host name of the machine.
  • UI

    @@ -116,6 +121,10 @@ Summary
  • 6620Fix report generation paths so dashboard output files are created in the correct location after internal refactoring.
  • 6456Handle malformed percent-encoded URLs gracefully when recording HTTP traffic, logging a warning instead of failing the recording.
  • +

    Other samplers

    +
      +
    • 66526757Encode non-ASCII attachment file names in SMTP Sampler according to RFC 2231, so recipients see the original name. Contributed by Ashraf Ali (github.com/ashrafiucse)
    • +
    Thanks