Remove Jakarta Mail dependency from spring-web
This commit removes the dependency that the spring-web module has on Jakarta Mail. In FormHttpMessageConverter, a dependency on jakarta.mail.internet.MimeUtility was replaced by existing encoding logic in ContentDisposition. In StandardMultipartHttpServletRequest, a dependency on the same MimeUtility was replaced by new quoted-printable decoding logic in ContentDisposition. Closes gh-28392
This commit is contained in:
parent
b4e6014a14
commit
217117ced0
|
|
@ -15,7 +15,6 @@ dependencies {
|
|||
optional("jakarta.el:jakarta.el-api")
|
||||
optional("jakarta.faces:jakarta.faces-api")
|
||||
optional("jakarta.json.bind:jakarta.json.bind-api")
|
||||
optional("jakarta.mail:jakarta.mail-api")
|
||||
optional("jakarta.validation:jakarta.validation-api")
|
||||
optional("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||
optional("io.reactivex.rxjava3:rxjava")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -33,6 +33,7 @@ import org.springframework.util.ObjectUtils;
|
|||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
|
||||
|
||||
|
|
@ -51,6 +52,9 @@ public final class ContentDisposition {
|
|||
private final static Pattern BASE64_ENCODED_PATTERN =
|
||||
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?B\\?([+/0-9a-zA-Z]+=*)\\?=");
|
||||
|
||||
private final static Pattern QUOTED_PRINTABLE_ENCODED_PATTERN =
|
||||
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?Q\\?(\\p{Print}+)\\?=");
|
||||
|
||||
private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
|
||||
"Invalid header field parameter format (as defined in RFC 5987)";
|
||||
|
||||
|
|
@ -371,12 +375,20 @@ public final class ContentDisposition {
|
|||
if (value.startsWith("=?") ) {
|
||||
Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value);
|
||||
if (matcher.find()) {
|
||||
String match1 = matcher.group(1);
|
||||
String match2 = matcher.group(2);
|
||||
filename = new String(Base64.getDecoder().decode(match2), Charset.forName(match1));
|
||||
charset = Charset.forName(matcher.group(1));
|
||||
String encodedValue = matcher.group(2);
|
||||
filename = new String(Base64.getDecoder().decode(encodedValue), charset);
|
||||
}
|
||||
else {
|
||||
filename = value;
|
||||
matcher = QUOTED_PRINTABLE_ENCODED_PATTERN.matcher(value);
|
||||
if (matcher.find()) {
|
||||
charset = Charset.forName(matcher.group(1));
|
||||
String encodedValue = matcher.group(2);
|
||||
filename = decodeQuotedPrintableFilename(encodedValue, charset);
|
||||
}
|
||||
else {
|
||||
filename = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -498,6 +510,43 @@ public final class ContentDisposition {
|
|||
c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given header field param as described in RFC 2047.
|
||||
* @param filename the filename
|
||||
* @param charset the charset for the filename
|
||||
* @return the encoded header field param
|
||||
* @see <a href="https://tools.ietf.org/html/rfc2047">RFC 2047</a>
|
||||
*/
|
||||
private static String decodeQuotedPrintableFilename(String filename, Charset charset) {
|
||||
Assert.notNull(filename, "'input' String` should not be null");
|
||||
Assert.notNull(charset, "'charset' should not be null");
|
||||
|
||||
byte[] value = filename.getBytes(US_ASCII);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int index = 0;
|
||||
while (index < value.length) {
|
||||
byte b = value[index];
|
||||
if (b == '_') {
|
||||
baos.write(' ');
|
||||
index++;
|
||||
}
|
||||
else if (b == '=' && index < value.length - 2) {
|
||||
int i1 = Character.digit((char) value[index + 1], 16);
|
||||
int i2 = Character.digit((char) value[index + 2], 16);
|
||||
if (i1 == -1 || i2 == -1) {
|
||||
throw new IllegalArgumentException("Not a valid hex sequence: " + filename.substring(index));
|
||||
}
|
||||
baos.write((i1 << 4) | i2);
|
||||
index += 3;
|
||||
}
|
||||
else {
|
||||
baos.write(b);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return StreamUtils.copyToString(baos, charset);
|
||||
}
|
||||
|
||||
private static String escapeQuotationsInFilename(String filename) {
|
||||
if (filename.indexOf('"') == -1 && filename.indexOf('\\') == -1) {
|
||||
return filename;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -18,7 +18,6 @@ package org.springframework.http.converter;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
|
|
@ -29,9 +28,8 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.mail.internet.MimeUtility;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
|
@ -526,7 +524,13 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
if (messageConverter.canWrite(partType, partContentType)) {
|
||||
Charset charset = isFilenameCharsetSet() ? StandardCharsets.US_ASCII : this.charset;
|
||||
HttpOutputMessage multipartMessage = new MultipartHttpOutputMessage(os, charset);
|
||||
multipartMessage.getHeaders().setContentDispositionFormData(name, getFilename(partBody));
|
||||
String filename = getFilename(partBody);
|
||||
ContentDisposition.Builder cd = ContentDisposition.formData()
|
||||
.name(name);
|
||||
if (filename != null) {
|
||||
cd.filename(filename, this.multipartCharset);
|
||||
}
|
||||
multipartMessage.getHeaders().setContentDisposition(cd.build());
|
||||
if (!partHeaders.isEmpty()) {
|
||||
multipartMessage.getHeaders().putAll(partHeaders);
|
||||
}
|
||||
|
|
@ -568,11 +572,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
@Nullable
|
||||
protected String getFilename(Object part) {
|
||||
if (part instanceof Resource resource) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename != null && this.multipartCharset != null) {
|
||||
filename = MimeDelegate.encode(filename, this.multipartCharset.name());
|
||||
}
|
||||
return filename;
|
||||
return resource.getFilename();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
|
|
@ -655,20 +655,4 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on the JavaMail API.
|
||||
*/
|
||||
private static class MimeDelegate {
|
||||
|
||||
public static String encode(String value, String charset) {
|
||||
try {
|
||||
return MimeUtility.encodeText(value, charset, null);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -20,7 +20,6 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -32,7 +31,6 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.mail.internet.MimeUtility;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.Part;
|
||||
|
||||
|
|
@ -100,9 +98,6 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
|
|||
ContentDisposition disposition = ContentDisposition.parse(headerValue);
|
||||
String filename = disposition.getFilename();
|
||||
if (filename != null) {
|
||||
if (filename.startsWith("=?") && filename.endsWith("?=")) {
|
||||
filename = MimeDelegate.decode(filename);
|
||||
}
|
||||
files.add(part.getName(), new StandardMultipartFile(part, filename));
|
||||
}
|
||||
else {
|
||||
|
|
@ -271,20 +266,4 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on the JavaMail API.
|
||||
*/
|
||||
private static class MimeDelegate {
|
||||
|
||||
public static String decode(String value) {
|
||||
try {
|
||||
return MimeUtility.decodeText(value);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -92,6 +92,18 @@ class ContentDispositionTests {
|
|||
assertThat(parse(input).getFilename()).isEqualTo("日本語.csv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseQuotedPrintableFilename() {
|
||||
String input = "attachment; filename=\"=?UTF-8?Q?=E6=97=A5=E6=9C=AC=E8=AA=9E.csv?=\"";
|
||||
assertThat(parse(input).getFilename()).isEqualTo("日本語.csv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseQuotedPrintableShiftJISFilename() {
|
||||
String input = "attachment; filename=\"=?SHIFT_JIS?Q?=93=FA=96{=8C=EA.csv?=\"";
|
||||
assertThat(parse(input).getFilename()).isEqualTo("日本語.csv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseEncodedFilenameWithoutCharset() {
|
||||
assertThat(parse("form-data; name=\"name\"; filename*=test.txt"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue