JAMES-1814 Encode Content-Disposition header in download servlet when necessary
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/abb21de1 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/abb21de1 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/abb21de1 Branch: refs/heads/master Commit: abb21de13fefe2c99626d2178f99a7002fd2b4f8 Parents: 7443396 Author: Laura Royet <[email protected]> Authored: Wed Sep 21 12:20:48 2016 +0200 Committer: Laura Royet <[email protected]> Committed: Wed Sep 21 12:20:48 2016 +0200 ---------------------------------------------------------------------- .../integration/cucumber/DownloadStepdefs.java | 26 +++++++++++++++++++- .../test/resources/cucumber/DownloadGet.feature | 6 +++++ .../org/apache/james/jmap/DownloadServlet.java | 14 +++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/abb21de1/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java index 6ef8a03..d000bb3 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java @@ -41,7 +41,9 @@ import org.apache.james.jmap.api.access.AccessToken; import org.apache.james.jmap.model.AttachmentAccessToken; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mime4j.codec.DecoderUtil; +import com.google.common.base.CharMatcher; import com.google.common.base.Charsets; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; @@ -63,6 +65,7 @@ public class DownloadStepdefs { private static final String INVALID_ATTACHMENT_TOKEN = "[email protected]_" + "2015-06-29T13:41:22.124Z_" + "DiZa0O14MjLWrAA8P6MG35Gt5CBp7mt5U1EH/M++rIoZK7nlGJ4dPW0dvZD7h4m3o5b/Yd8DXU5x2x4+s0HOOKzD7X0RMlsU7JHJMNLvTvRGWF/C+MUyC8Zce7DtnRVPEQX2uAZhL2PBABV07Vpa8kH+NxoS9CL955Bc1Obr4G+KN2JorADlocFQA6ElXryF5YS/HPZSvq1MTC6aJIP0ku8WRpRnbwgwJnn26YpcHXcJjbkCBtd9/BhlMV6xNd2hTBkfZmYdoNo+UKBaXWzLxAlbLuxjpxwvDNJfOEyWFPgHDoRvzP+G7KzhVWjanHAHrhF0GilEa/MKpOI1qHBSwA=="; + private static final String UTF8_CONTENT_DIPOSITION_START = "Content-Disposition: attachment; filename*=\""; private final UserStepdefs userStepdefs; private final MainStepdefs mainStepdefs; @@ -330,6 +333,27 @@ public class DownloadStepdefs { @Then("^the attachment is named \"([^\"]*)\"$") public void assertContentDisposition(String name) throws IOException { - assertThat(response.getHeaders("Content-Disposition")).extracting(Header::toString).containsExactly("Content-Disposition: attachment; filename=\"" + name + "\""); + if (!CharMatcher.ASCII.matchesAllOf(name)) { + assertEncodedFilenameMatches(name); + } else { + assertThat(response.getFirstHeader("Content-Disposition").getValue()).isEqualTo("attachment; filename=\"" + name + "\""); + } + } + + private void assertEncodedFilenameMatches(String name) { + String contentDispositionHeader = response.getHeaders("Content-Disposition")[0].toString(); + assertThat(contentDispositionHeader).startsWith(UTF8_CONTENT_DIPOSITION_START); + + String expectedFilename = decode(extractFilename(contentDispositionHeader)); + assertThat(name).isEqualTo(expectedFilename); + } + + private String extractFilename(String contentDispositionHeader) { + return contentDispositionHeader.substring(UTF8_CONTENT_DIPOSITION_START.length(), + contentDispositionHeader.length() - 1); + } + + private String decode(String name) { + return DecoderUtil.decodeEncodedWords(name, Charsets.UTF_8); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/abb21de1/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature index 6772e4f..82828d1 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature @@ -22,3 +22,9 @@ Feature: Download GET When "[email protected]" downloads "2" with "myFileName.txt" name Then the user should receive that attachment And the attachment is named "myFileName.txt" + + Scenario: Getting an attachment previously stored with a non ASCII name + Given "[email protected]" mailbox "inbox" contains a message "1" with an attachment "2" + When "[email protected]" downloads "2" with "دÙÙØ§ØµÙر.odt" name + Then the user should receive that attachment + And the attachment is named "دÙÙØ§ØµÙر.odt" \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/abb21de1/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java index 0d140e6..45f5dcc 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java @@ -41,10 +41,13 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.model.Attachment; import org.apache.james.mailbox.model.AttachmentId; +import org.apache.james.mime4j.codec.EncoderUtil; +import org.apache.james.mime4j.codec.EncoderUtil.Usage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.CharMatcher; public class DownloadServlet extends HttpServlet { @@ -126,10 +129,17 @@ public class DownloadServlet extends HttpServlet { } private void addContentDispositionHeader(Optional<String> optionalName, HttpServletResponse resp) { - optionalName.ifPresent(name -> resp.addHeader("Content-Disposition", "attachment; filename=\"" + name + "\"")); + optionalName.ifPresent(name -> addContentDispositionHeaderRegardingEncoding(name, resp)); + } + + private void addContentDispositionHeaderRegardingEncoding(String name, HttpServletResponse resp) { + if (CharMatcher.ASCII.matchesAllOf(name)) { + resp.addHeader("Content-Disposition", "attachment; filename=\"" + name + "\""); + } else { + resp.addHeader("Content-Disposition", "attachment; filename*=\"" + EncoderUtil.encodeEncodedWord(name, Usage.TEXT_TOKEN) + "\""); + } } - private MailboxSession getMailboxSession(HttpServletRequest req) { return (MailboxSession) req.getAttribute(AuthenticationFilter.MAILBOX_SESSION); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
