Skip to content

Commit

Permalink
Encode a filename parameter if it is not encoded, throw an exception …
Browse files Browse the repository at this point in the history
…if a filename parameter is not valid
  • Loading branch information
aserkes committed Jan 4, 2021
1 parent 109e685 commit 83d048e
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.glassfish.jersey.message.internal.HttpDateFormat;
import org.glassfish.jersey.message.internal.HttpHeaderReader;
import org.glassfish.jersey.uri.UriComponent;

/**
* A content disposition header.
Expand All @@ -41,8 +43,15 @@ public class ContentDisposition {
private Date modificationDate;
private Date readDate;
private long size;

private static final String CHARSET_GROUP_NAME = "charset";
private static final String CHARSET_REGEX = "(?<" + CHARSET_GROUP_NAME + ">UTF-8|ISO-8859-1)";
private static final String LANG_GROUP_NAME = "lang";
private static final String LANG_REGEX = "(?<" + LANG_GROUP_NAME + ">[a-z]{2,8}(-[a-z0-9-]+)?)?";
private static final String FILENAME_GROUP_NAME = "filename";
private static final String FILENAME_REGEX = "(?<" + FILENAME_GROUP_NAME + ">.+)";
private static final Pattern FILENAME_EXT_VALUE_PATTERN =
Pattern.compile("(UTF-8|ISO-8859-1)'([a-z]{2,8}(-[a-z0-9]+)?)?'(%[a-f0-9]{2}|[a-z0-9!#$&+.^_`|~-])+",
Pattern.compile(CHARSET_REGEX + "'" + LANG_REGEX + "'" + FILENAME_REGEX,
Pattern.CASE_INSENSITIVE);

protected ContentDisposition(final String type, final String fileName, final Date creationDate,
Expand Down Expand Up @@ -185,12 +194,7 @@ protected void addLongParameter(final StringBuilder sb, final String name, final
}

private void createParameters() throws ParseException {
fileName = parameters.get("filename");

String fileNameExt = parameters.get("filename*");
if (fileNameExt != null && FILENAME_EXT_VALUE_PATTERN.matcher(fileNameExt).matches()) {
fileName = fileNameExt;
}
fileName = defineFileName();

creationDate = createDate("creation-date");

Expand All @@ -201,6 +205,42 @@ private void createParameters() throws ParseException {
size = createLong("size");
}

private String defineFileName() throws ParseException {
final String fileName = parameters.get("filename");

final String fileNameExt = parameters.get("filename*");
if (fileNameExt == null) {
return fileName;
}

final Matcher matcher = FILENAME_EXT_VALUE_PATTERN.matcher(fileNameExt);
if (matcher.matches()) {
if (isEncodedInUriFormat(fileNameExt)) {
return fileNameExt;
} else {
if (matcher.group(CHARSET_GROUP_NAME).equalsIgnoreCase("UTF-8")) {
return new StringBuilder(matcher.group(CHARSET_GROUP_NAME))
.append("'")
.append(matcher.group(LANG_GROUP_NAME) == null ? "" : matcher.group(LANG_GROUP_NAME))
.append("'")
.append(encodeToUriFormat(matcher.group(FILENAME_GROUP_NAME)))
.toString();
}
throw new ParseException(matcher.group(CHARSET_GROUP_NAME) + " charset is not supported", 0);
}
}

throw new ParseException(fileNameExt + " - unsupported filename parameter", 0);
}

private String encodeToUriFormat(final String parameter) {
return UriComponent.contextualEncode(parameter, UriComponent.Type.UNRESERVED);
}

private boolean isEncodedInUriFormat(final String parameter) {
return UriComponent.valid(parameter, UriComponent.Type.UNRESERVED);
}

private Date createDate(final String name) throws ParseException {
final String value = parameters.get(name);
if (value == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
import org.glassfish.jersey.message.internal.HttpHeaderReader;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;


/**
* @author [email protected]
*/
Expand Down Expand Up @@ -100,81 +102,115 @@ public void testToString() {
@Test
public void testFileNameExt() {
final String fileName = "test.file";
String fileNameExt;
String encodedFilename;
try {
//incorrect fileNameExt - does not contain charset''
String fileNameExt = "testExt.file";
assertFileNameExt(fileName, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'abc%a1abc%a2%b1!#$&+.^_`|~-";
assertFileNameExt(fileNameExt, fileName, fileNameExt);
try {
fileNameExt = "testExt.file";
assertFileNameExt(fileName, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//correct fileNameExt, but unsupported charset (support only UTF-8)
try {
fileNameExt = "ISO-8859-1'language-us'abc%a1abc%a2%b1!#$&+.^_`|~-";
assertFileNameExt(fileNameExt, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//correct fileNameExt with encoding
fileNameExt = "UTF-8'language-us'abc%a1abc%a2%b1!#$&+.^_`|~-";
encodedFilename = "UTF-8'language-us'abc%a1abc%a2%b1%21%23%24%26%2B.%5E_%60%7C~-";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "UTF-8'us'fileName.txt";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//incorrect fileNameExt - too long language tag
fileNameExt = "utf-8'languageTooLong'fileName.txt";
assertFileNameExt(fileName, fileName, fileNameExt);
try {
fileNameExt = "utf-8'languageTooLong'fileName.txt";
assertFileNameExt(fileName, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//correct fileNameExt
fileNameExt = "utf-8''a";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//incorrect fileNameExt - language tag does not match to pattern
fileNameExt = "utf-8'lang-'a";
assertFileNameExt(fileName, fileName, fileNameExt);
try {
fileNameExt = "utf-8'lang-'a";
assertFileNameExt(fileName, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//incorrect fileNameExt - ext-value contains an inappropriate symbol sequence (%z1). Jersey encodes it.
fileNameExt = "utf-8'language-us'a%z1";
encodedFilename = "utf-8'language-us'a%25z1";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'a";
fileNameExt = "UTF-8'language-us'abc%a1abc%a2%b1";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'a%a1";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains an inappropriate symbol sequence (%z1)
fileNameExt = "ISO-8859-1'language-us'a%z1";
assertFileNameExt(fileName, fileName, fileNameExt);
//incorrect fileNameExt - unsupported charset
try {
fileNameExt = "Windows-1251'sr-Latn-RS'a";
assertFileNameExt(fileName, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'abc%a1abc%a2%b1";
fileNameExt = "utf-8'sr-Latn-RS'a";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains % without two HEXDIG
fileNameExt = "ISO-8859-1'language-us'a%";
assertFileNameExt(fileName, fileName, fileNameExt);
//incorrect fileNameExt - ext-value contains % without two HEXDIG. Jersey encodes it.
fileNameExt = "utf-8'language-us'a%";
encodedFilename = "utf-8'language-us'a%25";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'abc%a1abc%a2%b1!#$&+.^_`|~-";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'abc%a1abc%a2%b1!#$&+.^_`|~-";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "iso-8859-1'language-us'abc.TXT";
fileNameExt = "UTF-8'language-us'abc.TXT";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

//incorrect fileNameExt - no ext-value
fileNameExt = "ISO-8859-1'language-us'";
assertFileNameExt(fileName, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains forbidden symbol (\)
fileNameExt = "ISO-8859-1'language-us'c:\\file.txt";
assertFileNameExt(fileName, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains forbidden symbol (/)
fileNameExt = "ISO-8859-1'language-us'home/file.txt";
assertFileNameExt(fileName, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains forbidden symbol (李)
fileNameExt = "ISO-8859-1'language-us'李.txt";
assertFileNameExt(fileName, fileName, fileNameExt);
try {
fileNameExt = "utf-8'language-us'";
assertFileNameExt(fileName, fileName, fileNameExt);
fail("ParseException was expected to be thrown.");
} catch (ParseException e) {
//expected
}

//incorrect fileNameExt - ext-value contains forbidden symbol (\). Jersey encodes it.
fileNameExt = "utf-8'language-us'c:\\\\file.txt";
encodedFilename = "utf-8'language-us'c%3A%5Cfile.txt";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains forbidden symbol (/). Jersey encodes it.
fileNameExt = "utf-8'language-us'home/file.txt";
encodedFilename = "utf-8'language-us'home%2Ffile.txt";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//incorrect fileNameExt - ext-value contains forbidden symbol (李). Jersey encodes it.
fileNameExt = "utf-8'language-us'李.txt";
encodedFilename = "utf-8'language-us'%E6%9D%8E.txt";
assertFileNameExt(encodedFilename, fileName, fileNameExt);

//correct fileNameExt
fileNameExt = "ISO-8859-1'language-us'FILEname.tXt";
fileNameExt = "utf-8'language-us'FILEname.tXt";
assertFileNameExt(fileNameExt, fileName, fileNameExt);

} catch (ParseException ex) {
Expand Down

0 comments on commit 83d048e

Please sign in to comment.