Skip to content

Commit

Permalink
fix(engine-rest): add support for utf-8 in content disposition header
Browse files Browse the repository at this point in the history
related to CAM-10472, closes camunda#1527
  • Loading branch information
ATropichev authored Oct 8, 2021
1 parent d949492 commit f979ba1
Show file tree
Hide file tree
Showing 20 changed files with 218 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.camunda.bpm.engine.rest.history.HistoricProcessInstanceRestService;
import org.camunda.bpm.engine.rest.sub.history.HistoricProcessInstanceResource;
import org.camunda.bpm.engine.rest.sub.history.impl.HistoricProcessInstanceResourceImpl;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
Expand Down Expand Up @@ -144,7 +145,7 @@ else if (APPLICATION_CSV_TYPE.equals(mediaType) || TEXT_CSV_TYPE.equals(mediaTyp
String csv = getReportResultAsCsv(uriInfo);
return Response
.ok(csv, mediaType)
.header("Content-Disposition", "attachment; filename=\"process-instance-report.csv\"")
.header("Content-Disposition", URLEncodingUtil.buildAttachmentValue("process-instance-report.csv"))
.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.camunda.bpm.engine.rest.sub.impl;

import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.camunda.bpm.engine.variable.type.ValueType;
import org.camunda.bpm.engine.variable.value.BytesValue;
import org.camunda.bpm.engine.variable.value.FileValue;
Expand Down Expand Up @@ -51,7 +52,7 @@ protected Response responseForFileVariable(FileValue fileValue) {
type += "; charset=" + fileValue.getEncoding();
}
Object value = fileValue.getValue() == null ? "" : fileValue.getValue();
return Response.ok(value, type).header("Content-Disposition", "attachment; filename=\"" + fileValue.getFilename() + "\"").build();
return Response.ok(value, type).header("Content-Disposition", URLEncodingUtil.buildAttachmentValue(fileValue.getFilename())).build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.exception.RestException;
import org.camunda.bpm.engine.rest.sub.repository.CaseDefinitionResource;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.camunda.bpm.engine.runtime.CaseInstance;
import org.camunda.bpm.engine.variable.VariableMap;

Expand Down Expand Up @@ -173,7 +174,7 @@ public Response getCaseDefinitionDiagram() {
return Response.noContent().build();
} else {
String fileName = definition.getDiagramResourceName();
return Response.ok(caseDiagram).header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
return Response.ok(caseDiagram).header("Content-Disposition", URLEncodingUtil.buildAttachmentValue(fileName))
.type(ProcessDefinitionResourceImpl.getMediaTypeForFileSuffix(fileName)).build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.exception.RestException;
import org.camunda.bpm.engine.rest.sub.repository.DecisionDefinitionResource;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.camunda.bpm.engine.variable.VariableMap;
import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.engine.variable.value.TypedValue;
Expand Down Expand Up @@ -123,7 +124,7 @@ public Response getDecisionDefinitionDiagram() {
return Response.noContent().build();
} else {
String fileName = definition.getDiagramResourceName();
return Response.ok(decisionDiagram).header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
return Response.ok(decisionDiagram).header("Content-Disposition",URLEncodingUtil.buildAttachmentValue(fileName))
.type(ProcessDefinitionResourceImpl.getMediaTypeForFileSuffix(fileName)).build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.exception.RestException;
import org.camunda.bpm.engine.rest.sub.repository.DecisionRequirementsDefinitionResource;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;

/**
*
Expand Down Expand Up @@ -107,7 +108,7 @@ public Response getDecisionRequirementsDefinitionDiagram() {
return Response.noContent().build();
} else {
String fileName = definition.getDiagramResourceName();
return Response.ok(decisionRequirementsDiagram).header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
return Response.ok(decisionRequirementsDiagram).header("Content-Disposition", URLEncodingUtil.buildAttachmentValue(fileName))
.type(ProcessDefinitionResourceImpl.getMediaTypeForFileSuffix(fileName)).build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.camunda.bpm.engine.rest.dto.repository.DeploymentResourceDto;
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.sub.repository.DeploymentResourcesResource;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;

/**
* @author Sebastian Menski
Expand Down Expand Up @@ -145,7 +146,7 @@ public Response getDeploymentResourceData(String resourceId) {

return Response
.ok(resourceAsStream, mediaType)
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
.header("Content-Disposition", URLEncodingUtil.buildAttachmentValue(filename))
.build();
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.camunda.bpm.engine.rest.util.ApplicationContextPathUtil;
import org.camunda.bpm.engine.rest.util.ContentTypeUtil;
import org.camunda.bpm.engine.rest.util.EncodingUtil;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.runtime.ProcessInstanceWithVariables;
import org.camunda.bpm.engine.runtime.ProcessInstantiationBuilder;
Expand Down Expand Up @@ -280,7 +281,7 @@ public Response getProcessDefinitionDiagram() {
} else {
String fileName = definition.getDiagramResourceName();
return Response.ok(processDiagram)
.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
.header("Content-Disposition", URLEncodingUtil.buildAttachmentValue(fileName))
.type(getMediaTypeForFileSuffix(fileName)).build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.camunda.bpm.engine.rest.dto.task.TaskCountByCandidateGroupResultDto;
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.sub.task.TaskReportResource;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.camunda.bpm.engine.task.TaskCountByCandidateGroupResult;

import javax.ws.rs.core.MediaType;
Expand Down Expand Up @@ -56,7 +57,7 @@ else if (APPLICATION_CSV_TYPE.equals(mediaType) || TEXT_CSV_TYPE.equals(mediaTyp
String csv = getReportResultAsCsv();
return Response
.ok(csv, mediaType)
.header("Content-Disposition", "attachment; filename=\"task-count-by-candidate-group.csv\"")
.header("Content-Disposition", URLEncodingUtil.buildAttachmentValue("task-count-by-candidate-group.csv"))
.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. Camunda licenses this file to you under the Apache License,
* Version 2.0; you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.camunda.bpm.engine.rest.util;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import static java.text.MessageFormat.format;

public class URLEncodingUtil {

/**
* Encode a string value using `UTF-8` encoding scheme
*/
public static String encode(String value) {
if (value != null) {
try {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException ex) {
// should not happen
return value;
}
}

return null;
}

public static String buildAttachmentValue(String attachmentFileName) {
return format("attachment; filename=\"{0}\"; filename*=UTF-8''''{1}", attachmentFileName, encode(attachmentFileName));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,9 @@ public void testCaseDiagramRetrieval() throws FileNotFoundException, URISyntaxEx
.expect()
.statusCode(Status.OK.getStatusCode())
.contentType("image/png")
.header("Content-Disposition", "attachment; filename=\"" +
MockProvider.EXAMPLE_CASE_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"")
.header("Content-Disposition", "attachment; " +
"filename=\"" + MockProvider.EXAMPLE_CASE_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"; " +
"filename*=UTF-8''" + MockProvider.EXAMPLE_CASE_DEFINITION_DIAGRAM_RESOURCE_NAME)
.when().get(DIAGRAM_DEFINITION_URL).getBody().asByteArray();

// verify service interaction
Expand All @@ -591,7 +592,9 @@ public void testCaseDiagramNullFilename() throws FileNotFoundException, URISynta
.expect()
.statusCode(Status.OK.getStatusCode())
.contentType("application/octet-stream")
.header("Content-Disposition", "attachment; filename=\"" + null + "\"")
.header("Content-Disposition", "attachment; " +
"filename=\"" + null + "\"; "+
"filename*=UTF-8''" + null)
.when().get(DIAGRAM_DEFINITION_URL).getBody().asByteArray();

// verify service interaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,9 @@ public void testDecisionDiagramRetrieval() throws FileNotFoundException, URISynt
.expect()
.statusCode(Status.OK.getStatusCode())
.contentType("image/png")
.header("Content-Disposition", "attachment; filename=\"" +
MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"")
.header("Content-Disposition", "attachment; " +
"filename=\"" + MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"; " +
"filename*=UTF-8''" + MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME)
.when().get(DIAGRAM_DEFINITION_URL).getBody().asByteArray();

// verify service interaction
Expand All @@ -355,7 +356,9 @@ public void testDecisionDiagramNullFilename() throws FileNotFoundException, URIS
.expect()
.statusCode(Status.OK.getStatusCode())
.contentType("application/octet-stream")
.header("Content-Disposition", "attachment; filename=\"" + null + "\"")
.header("Content-Disposition", "attachment; " +
"filename=\"" + null + "\"; "+
"filename*=UTF-8''" + null)
.when().get(DIAGRAM_DEFINITION_URL).getBody().asByteArray();

// verify service interaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@ public void decisionRequirementsDiagramRetrieval() throws FileNotFoundException,
.expect()
.statusCode(Status.OK.getStatusCode())
.contentType("image/png")
.header("Content-Disposition", "attachment; filename=\"" +
MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"")
.header("Content-Disposition", "attachment; " +
"filename=\"" + MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME + "\"; " +
"filename*=UTF-8''" + MockProvider.EXAMPLE_DECISION_DEFINITION_DIAGRAM_RESOURCE_NAME
)
.when().get(DIAGRAM_DEFINITION_URL).getBody().asByteArray();

verify(repositoryServiceMock).getDecisionRequirementsDefinition(MockProvider.EXAMPLE_DECISION_REQUIREMENTS_DEFINITION_ID);
Expand Down
Loading

0 comments on commit f979ba1

Please sign in to comment.