diff --git a/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java b/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java index f500bf0cd2..00c646c99b 100644 --- a/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java +++ b/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java @@ -40,6 +40,7 @@ import org.json.JSONObject; import org.junit.Assert; import org.springframework.extensions.webscripts.TestWebScriptServer; import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; import java.io.Serializable; import java.util.ArrayList; @@ -194,13 +195,13 @@ public class SlingshotContentGetTest extends BaseWebScriptTest NodeRef rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER); - NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content"); + NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN); NodeRef folderX = createNode(rootFolder, "X", ContentModel.TYPE_FOLDER); NodeRef folderY = createNode(folderX, "Y", ContentModel.TYPE_FOLDER); NodeRef folderZ = createNode(folderY, "Z", ContentModel.TYPE_FOLDER); - NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content"); + NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN); // uri with relative path at the end String uri = URL_CONTENT_DOWNLOAD + doc1.getId() + "/X/Y/Z/doc2"; @@ -212,7 +213,50 @@ public class SlingshotContentGetTest extends BaseWebScriptTest nodeService.deleteNode(rootFolder); } - public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content) + public void testForcedAttachment() throws Exception + { + Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + NodeRef rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER); + NodeRef testhtml = createNodeWithTextContent(rootFolder, "testhtml", ContentModel.TYPE_CONTENT, "testhtml content", MimetypeMap.MIMETYPE_HTML); + NodeRef testpdf = createNodeWithTextContent(rootFolder, "testpdf", ContentModel.TYPE_CONTENT, "testpdf content", MimetypeMap.MIMETYPE_PDF); + + String uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=false"; + GetRequest req = new GetRequest(uri); + Response res = sendRequest(req, 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testhtml.getId(); + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=true"; + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=false"; + res = sendRequest(new GetRequest(uri), 200); + assertNull(res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId(); + res = sendRequest(new GetRequest(uri), 200); + assertNull(res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=true"; + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + + nodeService.deleteNode(rootFolder); + } + + public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content, String mimetype) { NodeRef nodeRef = createNode(parentNode, nodeCmName, nodeType); @@ -220,7 +264,7 @@ public class SlingshotContentGetTest extends BaseWebScriptTest if (content != null) { ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setMimetype(mimetype); writer.setEncoding("UTF-8"); writer.putContent(content); } diff --git a/remote-api/src/main/java/org/alfresco/repo/web/scripts/MimeTypeUtil.java b/remote-api/src/main/java/org/alfresco/repo/web/scripts/MimeTypeUtil.java new file mode 100644 index 0000000000..9d245ad4ea --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/repo/web/scripts/MimeTypeUtil.java @@ -0,0 +1,64 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.web.scripts; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.springframework.extensions.webscripts.WebScriptRequest; + +public class MimeTypeUtil +{ + + /** + * Get the file mimetype from the file ContentReader, and if its null then set the mimetype to binary by default + * and try to get the correct one from file extension + * + * + * @param reader reader of the file in the request + * @param req request relating to the file + * @param mimetypeService MimetypeService + * + * @return mimetype of the file as a string + */ + public static String determineMimetype(ContentReader reader, WebScriptRequest req, MimetypeService mimetypeService) + { + String mimetype = reader.getMimetype(); + if (mimetype == null || mimetype.length() == 0) + { + String extensionPath = req.getExtensionPath(); + mimetype = MimetypeMap.MIMETYPE_BINARY; + int extIndex = extensionPath.lastIndexOf('.'); + if (extIndex != -1) + { + String ext = extensionPath.substring(extIndex + 1); + mimetype = mimetypeService.getMimetype(ext); + } + } + return mimetype; + } + +} diff --git a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentGet.java b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentGet.java index 83e32679d7..1efa6b42ee 100644 --- a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentGet.java +++ b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentGet.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -26,14 +26,18 @@ package org.alfresco.repo.web.scripts.content; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; import org.alfresco.model.ContentModel; +import org.alfresco.repo.web.scripts.MimeTypeUtil; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; @@ -65,6 +69,19 @@ public class ContentGet extends StreamContent implements ServletContextAware private NamespaceService namespaceService; private ContentService contentService; + private List nonAttachContentTypes = Collections.emptyList(); + + /** + * @param nonAttachContentTypes List + */ + public void setNonAttachContentTypes(List nonAttachContentTypes) + { + if (nonAttachContentTypes != null && !nonAttachContentTypes.isEmpty()) + { + this.nonAttachContentTypes = nonAttachContentTypes; + } + } + /** * @param servletContext ServletContext */ @@ -121,9 +138,7 @@ public class ContentGet extends StreamContent implements ServletContextAware { throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find " + reference.toString()); } - - // determine attachment - boolean attach = Boolean.valueOf(req.getParameter("a")); + // render content QName propertyQName = ContentModel.PROP_CONTENT; @@ -140,6 +155,19 @@ public class ContentGet extends StreamContent implements ServletContextAware propertyQName = QName.createQName(propertyName, namespaceService); } } + // determine attachment and force download for specific mimetypes - see PRODSEC-5862 + boolean attach = Boolean.valueOf(req.getParameter("a")); + ContentReader reader = contentService.getReader(nodeRef, propertyQName); + String mimetype = MimeTypeUtil.determineMimetype(reader, req, mimetypeService); + + if (!attach) + { + if (nonAttachContentTypes == null || !nonAttachContentTypes.contains(mimetype)) + { + attach = true; + logger.warn("Ignored a=false for " + nodeRef.getId() + " since " + mimetype + " is not in the whitelist for non-attach content types"); + } + } // Stream the content streamContentLocal(req, res, nodeRef, attach, propertyQName, null); diff --git a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentInfo.java b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentInfo.java index 2e34694c24..9e8b8c2572 100644 --- a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentInfo.java +++ b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentInfo.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.web.scripts.content; import java.io.IOException; @@ -33,7 +33,7 @@ import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.alfresco.model.ContentModel; -import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.web.scripts.MimeTypeUtil; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -79,18 +79,7 @@ public class ContentInfo extends StreamContent delegate.setAttachment(req, res, attach, attachFileName); // establish mimetype - String mimetype = reader.getMimetype(); - String extensionPath = req.getExtensionPath(); - if (mimetype == null || mimetype.length() == 0) - { - mimetype = MimetypeMap.MIMETYPE_BINARY; - int extIndex = extensionPath.lastIndexOf('.'); - if (extIndex != -1) - { - String ext = extensionPath.substring(extIndex + 1); - mimetype = mimetypeService.getMimetype(ext); - } - } + String mimetype = MimeTypeUtil.determineMimetype(reader, req, mimetypeService); // set mimetype for the content and the character encoding + length for the stream res.setContentType(mimetype); diff --git a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentStreamer.java b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentStreamer.java index 75a4948cd5..8c418ce92d 100644 --- a/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentStreamer.java +++ b/remote-api/src/main/java/org/alfresco/repo/web/scripts/content/ContentStreamer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -43,6 +43,7 @@ import javax.servlet.http.HttpServletResponse; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; +import org.alfresco.repo.web.scripts.MimeTypeUtil; import org.alfresco.sync.repo.events.EventPublisher; import org.alfresco.repo.web.util.HttpRangeProcessor; import org.alfresco.rest.framework.resource.content.CacheDirective; @@ -361,18 +362,7 @@ public class ContentStreamer implements ResourceLoaderAware setAttachment(req, res, attach, attachFileName); // establish mimetype - String mimetype = reader.getMimetype(); - String extensionPath = req.getExtensionPath(); - if (mimetype == null || mimetype.length() == 0) - { - mimetype = MimetypeMap.MIMETYPE_BINARY; - int extIndex = extensionPath.lastIndexOf('.'); - if (extIndex != -1) - { - String ext = extensionPath.substring(extIndex + 1); - mimetype = mimetypeService.getMimetype(ext); - } - } + String mimetype = MimeTypeUtil.determineMimetype(reader, req, mimetypeService); res.setHeader(HEADER_ACCEPT_RANGES, "bytes"); try diff --git a/remote-api/src/main/resources/alfresco/web-scripts-application-context.xml b/remote-api/src/main/resources/alfresco/web-scripts-application-context.xml index 47d5358de3..4da5da51b0 100644 --- a/remote-api/src/main/resources/alfresco/web-scripts-application-context.xml +++ b/remote-api/src/main/resources/alfresco/web-scripts-application-context.xml @@ -249,6 +249,7 @@ + diff --git a/remote-api/src/test/java/org/alfresco/repo/web/scripts/content/ContentGetTest.java b/remote-api/src/test/java/org/alfresco/repo/web/scripts/content/ContentGetTest.java index 3e0e2f0f0d..8a93587f58 100644 --- a/remote-api/src/test/java/org/alfresco/repo/web/scripts/content/ContentGetTest.java +++ b/remote-api/src/test/java/org/alfresco/repo/web/scripts/content/ContentGetTest.java @@ -66,13 +66,17 @@ public class ContentGetTest extends BaseWebScriptTest { super.setUp(); - this.authenticationService = (MutableAuthenticationService) getServer().getApplicationContext() - .getBean("AuthenticationService"); + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); this.personService = (PersonService) getServer().getApplicationContext().getBean("PersonService"); this.nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); this.contentService = (ContentService) getServer().getApplicationContext().getBean("ContentService"); AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); createUser(USER_ONE); + + Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER); } private void createUser(String userName) @@ -117,18 +121,13 @@ public class ContentGetTest extends BaseWebScriptTest */ public void testRelativePath() throws Exception { - Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); - NodeRef companyHome = repositoryHelper.getCompanyHome(); - - rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER); - - NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content"); + NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN); NodeRef folderX = createNode(rootFolder, "X", ContentModel.TYPE_FOLDER); NodeRef folderY = createNode(folderX, "Y", ContentModel.TYPE_FOLDER); NodeRef folderZ = createNode(folderY, "Z", ContentModel.TYPE_FOLDER); - NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content"); + NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN); // uri with relative path at the end String uri = URL_CONTENT_DOWNLOAD + doc1.getId() + "/X/Y/Z/doc2"; @@ -138,7 +137,46 @@ public class ContentGetTest extends BaseWebScriptTest Assert.assertEquals("doc2 file content", resp.getContentAsString()); } - public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content) + + + public void testForcedAttachment() throws Exception + { + NodeRef testhtml = createNodeWithTextContent(rootFolder, "testhtml", ContentModel.TYPE_CONTENT, "testhtml content", MimetypeMap.MIMETYPE_HTML); + NodeRef testpdf = createNodeWithTextContent(rootFolder, "testpdf", ContentModel.TYPE_CONTENT, "testpdf content", MimetypeMap.MIMETYPE_PDF); + + String uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=false"; + GetRequest req = new GetRequest(uri); + Response res = sendRequest(req, 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testhtml.getId(); + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=true"; + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=false"; + res = sendRequest(new GetRequest(uri), 200); + assertNull(res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId(); + res = sendRequest(new GetRequest(uri), 200); + assertNull(res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + + uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=true"; + res = sendRequest(new GetRequest(uri), 200); + assertEquals("attachment", res.getHeader("Content-Disposition")); + assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType()); + } + + public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content, String mimetype) { NodeRef nodeRef = createNode(parentNode, nodeCmName, nodeType); @@ -146,7 +184,7 @@ public class ContentGetTest extends BaseWebScriptTest if (content != null) { ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setMimetype(mimetype); writer.setEncoding("UTF-8"); writer.putContent(content); }