Skip to content

Commit 2fa0371

Browse files
committed
Merge pull request sakaiproject#144 from buckett/SAK-12034
SAK-12034 Allow non ISO-8859-1 in download names.
2 parents 429b19a + d000ba8 commit 2fa0371

File tree

3 files changed

+75
-5
lines changed

3 files changed

+75
-5
lines changed

kernel/kernel-impl/src/main/java/org/sakaiproject/content/impl/BaseContentService.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -6917,8 +6917,7 @@ protected void handleAccessResource(HttpServletRequest req, HttpServletResponse
69176917
else
69186918
{
69196919
// use the last part, the file name part of the id, for the download file name
6920-
String fileName = Web.encodeFileName( req, Validator.getFileName(ref.getId()) );
6921-
6920+
String fileName = Validator.getFileName(ref.getId());
69226921
String disposition = null;
69236922

69246923
if (Validator.letBrowserInline(contentType))
@@ -6950,17 +6949,17 @@ protected void handleAccessResource(HttpServletRequest req, HttpServletResponse
69506949
}
69516950

69526951
if (fileInline || folderInline) {
6953-
disposition = "inline; filename=\"" + fileName + "\"";
6952+
disposition = Web.buildContentDisposition(fileName, false);
69546953
}
69556954
} else {
6956-
disposition = "inline; filename=\"" + fileName + "\"";
6955+
disposition = Web.buildContentDisposition(fileName, false);
69576956
}
69586957
}
69596958

69606959
// drop through to attachment
69616960
if (disposition == null)
69626961
{
6963-
disposition = "attachment; filename=\"" + fileName + "\"";
6962+
disposition = Web.buildContentDisposition(fileName, true);
69646963
}
69656964

69666965
// NOTE: Only set the encoding on the content we have to.

kernel/kernel-util/src/main/java/org/sakaiproject/util/Web.java

+33
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import java.io.ByteArrayOutputStream;
2525
import java.io.PrintWriter;
26+
import java.io.UnsupportedEncodingException;
27+
import java.net.URLEncoder;
2628
import java.util.Enumeration;
2729
import java.util.regex.Matcher;
2830
import java.util.regex.Pattern;
@@ -546,6 +548,8 @@ protected static final String toHex(byte b)
546548
** Sadly, Safari has a known bug where doesn't correctly translate encoding for user
547549
**
548550
** This method require inclusion of the javamail mail package.
551+
** @deprecated It is now possible to specify encoded filenames for the browser
552+
** see @link{Web#buildContentDisposition}
549553
**/
550554
public static String encodeFileName(HttpServletRequest req, String fileName )
551555
{
@@ -567,9 +571,38 @@ else if ( agent != null && agent.indexOf("Mozilla")>=0 && agent.indexOf("Safari"
567571
return fileName;
568572
}
569573

574+
/**
575+
* This attempts to build the value of the content disposition header. It provides a ISO-8859-1 representation
576+
* and a full UTF-8 version. This allows browser that understand the full version to use that and
577+
* for mainly IE 8 the old limited one.
578+
* @param filename The filename to encode
579+
* @param isDownload Whether the file is a download, will use "attachment" if true and "inline" if false.
580+
* @return The value of the content disposition header specifying it's inline content.
581+
*/
582+
public static String buildContentDisposition(String filename, boolean isDownload) {
583+
try {
584+
// This will replace all non US-ASCII characters with '?'
585+
// Although this behaviour is unspecified doing it manually is overkill (too much work).
586+
// Make sure we escape double quotes.
587+
String iso8859Filename = new String(filename.getBytes("ISO-8859-1"), "ISO-8859-1")
588+
.replace("\\", "\\\\")
589+
.replace("\"", "\\\"");
590+
String utf8Filename = URLEncoder.encode(filename, "UTF-8").replace("+", "%20");
591+
return new StringBuilder()
592+
.append(isDownload ? "attachment; " : "inline; ")
593+
.append("filename=\"").append(iso8859Filename).append("\"; ")
594+
// For sensible browser give them a full UTF-8 encoded string.
595+
.append("filename*=UTF-8''").append(utf8Filename)
596+
.toString();
597+
} catch (UnsupportedEncodingException shouldNeverHappen) {
598+
throw new RuntimeException(shouldNeverHappen);
599+
}
600+
}
601+
570602
private static String internalEscapeHtml(String value, boolean escapeNewlines) {
571603
// FIXME this method needs to be removed entirely and is only here as a reference of how this used to work
572604

605+
573606
if (value == null) return "";
574607

575608
try {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.sakaiproject.util;
2+
3+
import junit.framework.TestCase;
4+
5+
public class WebTest extends TestCase {
6+
7+
public void testContentDispositionInline() {
8+
assertEquals("inline; filename=\"file.txt\"; filename*=UTF-8''file.txt",
9+
Web.buildContentDisposition("file.txt", false));
10+
}
11+
12+
public void testContentDispositionAttachment() {
13+
assertEquals("attachment; filename=\"file.txt\"; filename*=UTF-8''file.txt",
14+
Web.buildContentDisposition("file.txt", true));
15+
}
16+
17+
public void testContentDispositionSemiColon() {
18+
assertEquals("inline; filename=\"start;stop.txt\"; filename*=UTF-8''start%3Bstop.txt",
19+
Web.buildContentDisposition("start;stop.txt", false));
20+
}
21+
22+
public void testContentDispositionQuotes() {
23+
assertEquals("inline; filename=\"start\\\"stop.txt\"; filename*=UTF-8''start%22stop.txt",
24+
Web.buildContentDisposition("start\"stop.txt", false));
25+
}
26+
27+
public void testContentDispositionUTF8() {
28+
// encoding hello world in greek.
29+
assertEquals("inline; filename=\"???? ??? ?????.txt\"; " +
30+
"filename*=UTF-8''%CE%93%CE%B5%CE%B9%CE%B1%20%CF%83%CE%B1%CF%82%20%CE%BA%CF%8C%CF%83%CE%BC%CE%BF.txt",
31+
Web.buildContentDisposition("\u0393\u03B5\u03B9\u03B1 \u03C3\u03B1\u03C2 \u03BA\u03CC\u03C3\u03BC\u03BF.txt", false));
32+
}
33+
34+
public void testContentDispositionISO8859() {
35+
assertEquals("inline; filename=\"exposé.txt\"; filename*=UTF-8''expos%C3%A9.txt",
36+
Web.buildContentDisposition("exposé.txt", false));
37+
}
38+
}

0 commit comments

Comments
 (0)