mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-14 13:33:08 +00:00
Merge pull request #11269 from tdonohue/port_11268_to_7x
[Port dspace-7_x] fix(#11191): Align Content-Disposition with RFC 5987/6266
This commit is contained in:
@@ -9,9 +9,11 @@ package org.dspace.app.rest.utils;
|
|||||||
|
|
||||||
import static java.util.Objects.isNull;
|
import static java.util.Objects.isNull;
|
||||||
import static java.util.Objects.nonNull;
|
import static java.util.Objects.nonNull;
|
||||||
import static javax.mail.internet.MimeUtility.encodeText;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.Normalizer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -171,9 +173,16 @@ public class HttpHeadersInitializer {
|
|||||||
|
|
||||||
// distposition may be null here if contentType is null
|
// distposition may be null here if contentType is null
|
||||||
if (!isNullOrEmpty(disposition)) {
|
if (!isNullOrEmpty(disposition)) {
|
||||||
httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(String.format(CONTENT_DISPOSITION_FORMAT,
|
String fallbackAsciiName = createFallbackAsciiName(this.fileName);
|
||||||
|
String encodedUtf8Name = createEncodedUtf8Name(this.fileName);
|
||||||
|
|
||||||
|
String headerValue = String.format(
|
||||||
|
"%s; filename=\"%s\"; filename*=UTF-8''%s",
|
||||||
disposition,
|
disposition,
|
||||||
encodeText(fileName))));
|
fallbackAsciiName,
|
||||||
|
encodedUtf8Name
|
||||||
|
);
|
||||||
|
httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(headerValue));
|
||||||
}
|
}
|
||||||
log.debug("Content-Disposition : {}", disposition);
|
log.debug("Content-Disposition : {}", disposition);
|
||||||
|
|
||||||
@@ -261,4 +270,41 @@ public class HttpHeadersInitializer {
|
|||||||
return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1;
|
return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a safe ASCII-only fallback filename by removing diacritics (accents)
|
||||||
|
* and replacing any remaining non-ASCII characters.
|
||||||
|
* E.g., "ä-ö-é.pdf" becomes "a-o-e.pdf".
|
||||||
|
* @param originalFilename The original filename.
|
||||||
|
* @return A string containing only ASCII characters.
|
||||||
|
*/
|
||||||
|
private String createFallbackAsciiName(String originalFilename) {
|
||||||
|
if (originalFilename == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String normalized = Normalizer.normalize(originalFilename, Normalizer.Form.NFD);
|
||||||
|
String withoutAccents = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
|
||||||
|
return withoutAccents.replaceAll("[^\\x00-\\x7F]", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a percent-encoded UTF-8 filename according to RFC 5987.
|
||||||
|
* This is for the `filename*` parameter.
|
||||||
|
* E.g., "ä ö é.pdf" becomes "%C3%A4%20%C3%B6%20%C3%A9.pdf".
|
||||||
|
* @param originalFilename The original filename.
|
||||||
|
* @return A percent-encoded string.
|
||||||
|
*/
|
||||||
|
private String createEncodedUtf8Name(String originalFilename) {
|
||||||
|
if (originalFilename == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String encoded = URLEncoder.encode(originalFilename, StandardCharsets.UTF_8.toString());
|
||||||
|
return encoded.replace("+", "%20");
|
||||||
|
} catch (java.io.UnsupportedEncodingException e) {
|
||||||
|
// Fallback to a simple ASCII name if encoding fails.
|
||||||
|
log.error("UTF-8 encoding not supported, which should not happen.", e);
|
||||||
|
return createFallbackAsciiName(originalFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
package org.dspace.app.rest;
|
package org.dspace.app.rest;
|
||||||
|
|
||||||
import static java.util.UUID.randomUUID;
|
import static java.util.UUID.randomUUID;
|
||||||
import static javax.mail.internet.MimeUtility.encodeText;
|
|
||||||
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
||||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||||
import static org.apache.commons.io.IOUtils.toInputStream;
|
import static org.apache.commons.io.IOUtils.toInputStream;
|
||||||
@@ -347,7 +346,11 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
//2. A public item with a bitstream
|
//2. A public item with a bitstream
|
||||||
|
|
||||||
String bitstreamContent = "0123456789";
|
String bitstreamContent = "0123456789";
|
||||||
String bitstreamName = "ภาษาไทย";
|
String bitstreamName = "ภาษาไทย-com-acentuação.pdf";
|
||||||
|
String expectedAscii = "-com-acentuacao.pdf";
|
||||||
|
String expectedUtf8Encoded =
|
||||||
|
"%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-"
|
||||||
|
+ "com-acentua%C3%A7%C3%A3o.pdf";
|
||||||
|
|
||||||
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
|
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
|
||||||
|
|
||||||
@@ -371,7 +374,9 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
//We expect the content disposition to have the encoded bitstream name
|
//We expect the content disposition to have the encoded bitstream name
|
||||||
.andExpect(header().string(
|
.andExpect(header().string(
|
||||||
"Content-Disposition",
|
"Content-Disposition",
|
||||||
"attachment;filename=\"" + encodeText(bitstreamName) + "\""
|
String.format("attachment; filename=\"%s\"; filename*=UTF-8''%s",
|
||||||
|
expectedAscii,
|
||||||
|
expectedUtf8Encoded)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user