From 5f307ecd2872f3dac176af6c5c611b2f6427cf6f Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Wed, 22 Jan 2014 16:26:34 +0100
Subject: [PATCH] OO-947: use the solution defined by RFC2231/5987 to encode
 the filename in the headers

---
 .../modules/bc/components/CrumbRenderer.java  |  3 +-
 .../core/gui/media/FileMediaResource.java     |  4 +-
 .../gui/media/NamedFileMediaResource.java     |  4 +-
 .../org/olat/core/gui/render/URLBuilder.java  | 42 +++++++++++++------
 .../java/org/olat/core/util/StringHelper.java | 24 +----------
 .../olat/core/util/vfs/VFSMediaResource.java  | 16 +++----
 .../util/vfs/VFSRevisionMediaResource.java    | 17 +++-----
 .../olat/course/archiver/ArchiveResource.java |  4 +-
 .../olat/course/db/CourseDBMediaResource.java |  5 ++-
 .../olat/ims/qti/export/QTIWordExport.java    |  4 +-
 .../olat/ims/qti/qpool/QTIPoolWordExport.java |  4 +-
 .../manager/AbstractExportTestResource.java   |  4 +-
 .../qpool/manager/ExportQItemResource.java    |  5 ++-
 .../manager/ExportQItemsZipResource.java      |  5 ++-
 14 files changed, 65 insertions(+), 76 deletions(-)

diff --git a/src/main/java/org/olat/core/commons/modules/bc/components/CrumbRenderer.java b/src/main/java/org/olat/core/commons/modules/bc/components/CrumbRenderer.java
index f7a4e2bfc32..c9dc843954e 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/components/CrumbRenderer.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/components/CrumbRenderer.java
@@ -31,7 +31,6 @@ import java.util.StringTokenizer;
 import org.olat.core.gui.control.winmgr.AJAXFlags;
 import org.olat.core.gui.render.StringOutput;
 import org.olat.core.gui.render.URLBuilder;
-import org.olat.core.util.StringHelper;
 
 /**
  * Initial Date:  08.07.2003
@@ -72,7 +71,7 @@ public class CrumbRenderer {
 		StringTokenizer st = new StringTokenizer(fc.getCurrentContainerPath(), "/", false);
 		while (st.hasMoreElements()) {
 			String token = st.nextToken();
-			pathLink.append(StringHelper.urlEncodeUTF8(token));
+			pathLink.append(ubu.encodeUrl(token));
 			sb.append("&nbsp;/&nbsp;");
 			if (st.hasMoreElements() && renderLinks) {
 				sb.append("<a href=\"");
diff --git a/src/main/java/org/olat/core/gui/media/FileMediaResource.java b/src/main/java/org/olat/core/gui/media/FileMediaResource.java
index 860d8f93660..4ffb9fc521e 100644
--- a/src/main/java/org/olat/core/gui/media/FileMediaResource.java
+++ b/src/main/java/org/olat/core/gui/media/FileMediaResource.java
@@ -141,8 +141,8 @@ public class FileMediaResource implements MediaResource {
 		if (deliverAsAttachment) {
 			// encode filename in ISO8859-1; does not really help but prevents from filename not being displayed at all
 			// if it contains non-US-ASCII characters which are not allowed in header fields.
-			hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file.getName()) + "\"");			
-			hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(file.getName()));
+			hres.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(file.getName()));			
+			hres.setHeader("Content-Description",StringHelper.urlEncodeUTF8(file.getName()));
 		} else {
 			hres.setHeader("Content-Disposition", "inline");
 		}
diff --git a/src/main/java/org/olat/core/gui/media/NamedFileMediaResource.java b/src/main/java/org/olat/core/gui/media/NamedFileMediaResource.java
index 454752eca4a..1d3f80b9935 100644
--- a/src/main/java/org/olat/core/gui/media/NamedFileMediaResource.java
+++ b/src/main/java/org/olat/core/gui/media/NamedFileMediaResource.java
@@ -84,8 +84,8 @@ public class NamedFileMediaResource extends FileMediaResource {
 	public void prepare(HttpServletResponse hres) {
 		// encode filename in ISO8859-1; does not really help but prevents from filename not being displayed at all
 		// if it contains non-US-ASCII characters which are not allowed in header fields.
-		hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(fileName) + "\"");
-		hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(fileDescription));
+		hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(fileName));
+		hres.setHeader("Content-Description", StringHelper.urlEncodeUTF8(fileDescription));
 	}
 
 
diff --git a/src/main/java/org/olat/core/gui/render/URLBuilder.java b/src/main/java/org/olat/core/gui/render/URLBuilder.java
index eecfa312eb0..38a8abea9d1 100644
--- a/src/main/java/org/olat/core/gui/render/URLBuilder.java
+++ b/src/main/java/org/olat/core/gui/render/URLBuilder.java
@@ -26,18 +26,25 @@
 
 package org.olat.core.gui.render;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.regex.Pattern;
+
 import org.olat.core.gui.GUIInterna;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.winmgr.AJAXFlags;
 import org.olat.core.gui.control.winmgr.WindowBackOfficeImpl;
-import org.olat.core.util.StringHelper;
+import org.olat.core.logging.AssertException;
 
 /**
  * 
  * @author Felix Jost
  */
 public class URLBuilder {
+	
+	private static final Pattern p1 = Pattern.compile("\\+");
+	private static final Pattern p2 = Pattern.compile("%2F");
 
 	private final String uriPrefix;
 
@@ -135,16 +142,6 @@ public class URLBuilder {
 		else buf.append(quote);
 	}
 	
-	
-	/*public boolean isAjaxOn() {
-		return true;
-	}*/
-
-	/*public void buildNonAjaxURI(StringOutput buf, String[] keys, String[] values, String modURI) {
-		buildURI(buf, keys, values, modURI, AJAXFlags.MODE_TOBGIFRAME);
-	}*/
-
-	
 	/**
 	 * builds an uri. neither key nor values may contain the character
 	 * UserRequest.PARAM_DELIM which is a ":" (colon). in case you think you
@@ -184,7 +181,7 @@ public class URLBuilder {
 		result.append('/');
 		if (modURI != null) result.append(modURI);
 		//FIXME:fj:a urlEncodeUTF8 is slow; improve the regexp, also convert only the modURI to utf-8?
-		buf.append(StringHelper.urlEncodeUTF8(result.toString()));
+		buf.append(encodeUrl(result.toString()));
 	}
 	
 	public void buildURI(StringOutput buf, String[] keys, String[] values, String modURI) {
@@ -244,6 +241,27 @@ public class URLBuilder {
 		*/
 		return result;
 	}
+	
+	/**
+	 * @param url
+	 * @return encoded string
+	 */
+	public String encodeUrl(String url) {
+		String encodedURL;
+		try {
+			encodedURL = URLEncoder.encode(url, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			/*
+			 * from java.nio.Charset Standard charsets Every implementation of the
+			 * Java platform is required to support the following standard charsets...
+			 * ... UTF-8 Eight-bit UCS Transformation Format ...
+			 */
+			throw new AssertException("utf-8 encoding is needed for proper encoding, but not offered on this java platform????");
+		}
+		encodedURL = p1.matcher(encodedURL).replaceAll("%20");
+		encodedURL = p2.matcher(encodedURL).replaceAll("/");
+		return encodedURL;
+	}
 
 	/**
 	 * used for OLAT-1973 only: if in replayable-url-mode for performance testing
diff --git a/src/main/java/org/olat/core/util/StringHelper.java b/src/main/java/org/olat/core/util/StringHelper.java
index 2e65edb4d67..c3bc38653ca 100644
--- a/src/main/java/org/olat/core/util/StringHelper.java
+++ b/src/main/java/org/olat/core/util/StringHelper.java
@@ -68,9 +68,6 @@ public class StringHelper {
 	private static final String WHITESPACE_REGEXP = "^\\s*$";
 	private static final Pattern WHITESPACE_PATTERN = Pattern.compile(WHITESPACE_REGEXP);
 	
-	private static final Pattern p1 = Pattern.compile("\\+");
-	private static final Pattern p2 = Pattern.compile("%2F");
-	
 	/**
 	 * regex for not allowing
 	 * <code>;,:</code> <code>ALL_WITHOUT_COMMA_2POINT_STRPNT</code>
@@ -194,21 +191,7 @@ public class StringHelper {
 		numFormatter.setMaximumFractionDigits(fractionDigits);
 		return numFormatter.format(f);
 	}
-
-	/**
-	 * @param url
-	 * @return encoded string
-	 */
-	public static String urlEncodeISO88591(String url) {
-		String part;
-		try {
-			part = URLEncoder.encode(url, "iso-8859-1");
-		} catch (UnsupportedEncodingException e) {
-			throw new RuntimeException("encoding failed (iso-8859-1) for :" + url);
-		}
-		return part;
-	}
-
+	
 	/**
 	 * @param url
 	 * @return encoded string
@@ -225,13 +208,8 @@ public class StringHelper {
 			 */
 			throw new AssertException("utf-8 encoding is needed for proper encoding, but not offered on this java platform????");
 		}
-		encodedURL = p1.matcher(encodedURL).replaceAll("%20");
-		encodedURL = p2.matcher(encodedURL).replaceAll("/");
 		return encodedURL;
 	}
-	
-
-	
 
 	/**
 	 * Converts all keys of a hash map to a string array.
diff --git a/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java b/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
index 1bd5932b0ee..9a1ebec95bb 100644
--- a/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
+++ b/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
@@ -27,14 +27,13 @@
 package org.olat.core.util.vfs;
 
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 
 import javax.servlet.http.HttpServletResponse;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.modules.bc.FilesInfoMBean;
 import org.olat.core.gui.media.MediaResource;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.WebappHelper;
 
 public class VFSMediaResource implements MediaResource {
@@ -93,15 +92,12 @@ public class VFSMediaResource implements MediaResource {
 	 * @see org.olat.core.gui.media.MediaResource#prepare(javax.servlet.http.HttpServletResponse)
 	 */
 	public void prepare(HttpServletResponse hres) {
-		//http headers are ASCII only therefore we encode filenames
-		String filename = "";
-		try {
-			filename = URLEncoder.encode(vfsLeaf.getName(), "utf-8");
-		} catch (UnsupportedEncodingException wontHappen) {
-			//nothing};
+		String filename = StringHelper.urlEncodeUTF8(vfsLeaf.getName());
+		if (unknownMimeType) {
+			hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + filename);
+		} else {
+			hres.setHeader("Content-Disposition", "filename*=UTF-8''" + filename);
 		}
-		if (unknownMimeType) hres.setHeader("Content-Disposition", "attachment; filename=" + filename);
-		else hres.setHeader("Content-Disposition", "filename=" + filename);
 	}
 
 	public void release() {
diff --git a/src/main/java/org/olat/core/util/vfs/VFSRevisionMediaResource.java b/src/main/java/org/olat/core/util/vfs/VFSRevisionMediaResource.java
index f172b7056d7..b34b382ecab 100644
--- a/src/main/java/org/olat/core/util/vfs/VFSRevisionMediaResource.java
+++ b/src/main/java/org/olat/core/util/vfs/VFSRevisionMediaResource.java
@@ -20,12 +20,11 @@
 package org.olat.core.util.vfs;
 
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 
 import javax.servlet.http.HttpServletResponse;
 
 import org.olat.core.gui.media.MediaResource;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.WebappHelper;
 import org.olat.core.util.vfs.version.VFSRevision;
 
@@ -77,16 +76,12 @@ public class VFSRevisionMediaResource implements MediaResource {
 	}
 
 	public void prepare(HttpServletResponse hres) {
-		String filename = "";
-		try {
-			filename = URLEncoder.encode(revision.getName(), "utf-8");
-		} catch (UnsupportedEncodingException wontHappen) {
-			//nothing
+		String filename = StringHelper.urlEncodeUTF8(revision.getName());
+		if (forceDownload || unknownMimeType) {
+			hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + filename);
+		} else {
+			hres.setHeader("Content-Disposition", "filename*=UTF-8''" + filename);
 		}
-		if (forceDownload || unknownMimeType)
-			hres.setHeader("Content-Disposition", "attachment; filename=" + filename);
-		else
-			hres.setHeader("Content-Disposition", "filename=" + filename);
 	}
 
 	public void release() {
diff --git a/src/main/java/org/olat/course/archiver/ArchiveResource.java b/src/main/java/org/olat/course/archiver/ArchiveResource.java
index afd4f5998fc..5f024910033 100644
--- a/src/main/java/org/olat/course/archiver/ArchiveResource.java
+++ b/src/main/java/org/olat/course/archiver/ArchiveResource.java
@@ -93,8 +93,8 @@ public class ArchiveResource implements MediaResource {
 				+ StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName())
 				+ "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()))
 				+ ".zip";
-		String urlEncodedLabel = StringHelper.urlEncodeISO88591(label);
-		hres.setHeader("Content-Disposition","attachment; filename=\"" + urlEncodedLabel + "\"");			
+		String urlEncodedLabel = StringHelper.urlEncodeUTF8(label);
+		hres.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + urlEncodedLabel);			
 		hres.setHeader("Content-Description", urlEncodedLabel);
 		
 		ZipOutputStream zout = null;
diff --git a/src/main/java/org/olat/course/db/CourseDBMediaResource.java b/src/main/java/org/olat/course/db/CourseDBMediaResource.java
index 4ad40aefc5d..45e0c3fbda5 100644
--- a/src/main/java/org/olat/course/db/CourseDBMediaResource.java
+++ b/src/main/java/org/olat/course/db/CourseDBMediaResource.java
@@ -70,8 +70,9 @@ public class CourseDBMediaResource implements MediaResource {
 
 	@Override
 	public void prepare(HttpServletResponse hres) {
-		hres.setHeader("Content-Disposition","filename=\"" + StringHelper.urlEncodeISO88591(fileName) + "\"");
-		hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(fileName));
+		String encodedFileName = StringHelper.urlEncodeUTF8(fileName);
+		hres.setHeader("Content-Disposition","filename*=UTF-8''" + encodedFileName);
+		hres.setHeader("Content-Description",encodedFileName);
 
 	}
 
diff --git a/src/main/java/org/olat/ims/qti/export/QTIWordExport.java b/src/main/java/org/olat/ims/qti/export/QTIWordExport.java
index d60379bce2f..9e2eddccaa3 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIWordExport.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIWordExport.java
@@ -122,8 +122,8 @@ public class QTIWordExport implements MediaResource {
 			String secureLabel = StringHelper.transformDisplayNameToFileSystemName(label);
 
 			String file = secureLabel + ".zip";
-			hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\"");			
-			hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label));
+			hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(file));			
+			hres.setHeader("Content-Description", StringHelper.urlEncodeUTF8(label));
 			
 			zout = new ZipOutputStream(hres.getOutputStream());
 			zout.setLevel(9);
diff --git a/src/main/java/org/olat/ims/qti/qpool/QTIPoolWordExport.java b/src/main/java/org/olat/ims/qti/qpool/QTIPoolWordExport.java
index c47cbb6a0fa..fdc91bb8ee2 100644
--- a/src/main/java/org/olat/ims/qti/qpool/QTIPoolWordExport.java
+++ b/src/main/java/org/olat/ims/qti/qpool/QTIPoolWordExport.java
@@ -122,8 +122,8 @@ class QTIPoolWordExport implements MediaResource {
 			List<QuestionItemFull> fullItems = questionItemDao.loadByIds(itemKeys);
 
 			String file = secureLabel + ".zip";
-			hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\"");			
-			hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label));
+			hres.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(file));			
+			hres.setHeader("Content-Description", StringHelper.urlEncodeUTF8(label));
 			
 			zout = new ZipOutputStream(hres.getOutputStream());
 			zout.setLevel(9);
diff --git a/src/main/java/org/olat/modules/qpool/manager/AbstractExportTestResource.java b/src/main/java/org/olat/modules/qpool/manager/AbstractExportTestResource.java
index dd433c7bf67..195ead4bc34 100644
--- a/src/main/java/org/olat/modules/qpool/manager/AbstractExportTestResource.java
+++ b/src/main/java/org/olat/modules/qpool/manager/AbstractExportTestResource.java
@@ -85,8 +85,8 @@ public abstract class AbstractExportTestResource implements MediaResource {
 		
 		String label = "Test";
 		String file = StringHelper.transformDisplayNameToFileSystemName(label) + ".zip";
-		hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\"");			
-		hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label));
+		hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(file));			
+		hres.setHeader("Content-Description", StringHelper.urlEncodeUTF8(label));
 		
 		ZipOutputStream zout = null;
 		try {
diff --git a/src/main/java/org/olat/modules/qpool/manager/ExportQItemResource.java b/src/main/java/org/olat/modules/qpool/manager/ExportQItemResource.java
index 540d0b32e98..654e814424f 100644
--- a/src/main/java/org/olat/modules/qpool/manager/ExportQItemResource.java
+++ b/src/main/java/org/olat/modules/qpool/manager/ExportQItemResource.java
@@ -84,8 +84,9 @@ public class ExportQItemResource implements MediaResource {
 		
 		String label = item.getTitle();
 		String file = StringHelper.transformDisplayNameToFileSystemName(label) + ".zip";
-		hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\"");			
-		hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label));
+		String encodedFileName = StringHelper.urlEncodeUTF8(file);
+		hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);			
+		hres.setHeader("Content-Description", encodedFileName);
 		
 		ZipOutputStream zout = null;
 		try {
diff --git a/src/main/java/org/olat/modules/qpool/manager/ExportQItemsZipResource.java b/src/main/java/org/olat/modules/qpool/manager/ExportQItemsZipResource.java
index 51f3fd1cf6a..df8c1cbc668 100644
--- a/src/main/java/org/olat/modules/qpool/manager/ExportQItemsZipResource.java
+++ b/src/main/java/org/olat/modules/qpool/manager/ExportQItemsZipResource.java
@@ -85,8 +85,9 @@ public class ExportQItemsZipResource implements MediaResource {
 		
 		String label = "ExportItems";
 		String file = StringHelper.transformDisplayNameToFileSystemName(label) + ".zip";
-		hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\"");			
-		hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label));
+		String encodedFileName = StringHelper.urlEncodeUTF8(file);
+		hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);			
+		hres.setHeader("Content-Description", encodedFileName);
 		
 		ZipOutputStream zout = null;
 		try {
-- 
GitLab