[MNT-24623] fix for unzipping zip files having accent chars (#3321)

This commit is contained in:
SatyamSah5
2025-04-18 09:45:21 +05:30
committed by GitHub
parent 6e5b64be12
commit b51374532e
3 changed files with 610 additions and 559 deletions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -41,6 +41,11 @@ import java.util.List;
import java.util.Map;
import java.util.zip.ZipException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.utils.InputStreamStatistics;
import org.apache.commons.lang3.StringUtils;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
@@ -62,10 +67,6 @@ import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.utils.InputStreamStatistics;
import org.apache.commons.lang3.StringUtils;
/**
* Importer action executor
@@ -110,7 +111,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Sets the ImporterService to use
*
* @param importerService The ImporterService
* @param importerService
* The ImporterService
*/
public void setImporterService(ImporterService importerService)
{
@@ -120,7 +122,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Sets the NodeService to use
*
* @param nodeService The NodeService
* @param nodeService
* The NodeService
*/
public void setNodeService(NodeService nodeService)
{
@@ -130,7 +133,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Sets the ContentService to use
*
* @param contentService The ContentService
* @param contentService
* The ContentService
*/
public void setContentService(ContentService contentService)
{
@@ -140,7 +144,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Sets the FileFolderService to use
*
* @param fileFolderService The FileFolderService
* @param fileFolderService
* The FileFolderService
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
@@ -156,7 +161,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
}
/**
* @param highByteZip the encoding switch for high-byte ZIP filenames to set
* @param highByteZip
* the encoding switch for high-byte ZIP filenames to set
*/
public void setHighByteZip(boolean highByteZip)
{
@@ -164,7 +170,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
}
/**
* @param ratioThreshold the compression ratio threshold for Zip bomb detection
* @param ratioThreshold
* the compression ratio threshold for Zip bomb detection
*/
public void setRatioThreshold(long ratioThreshold)
{
@@ -172,10 +179,10 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
}
/**
* This method sets a value for the uncompressed bytes limit. If the string does not {@link Long#parseLong(String) parse} to a
* java long.
* This method sets a value for the uncompressed bytes limit. If the string does not {@link Long#parseLong(String) parse} to a java long.
*
* @param limit a String representing a valid Java long.
* @param limit
* a String representing a valid Java long.
*/
public void setUncompressedBytesLimit(String limit)
{
@@ -302,8 +309,10 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Recursively import a directory structure into the specified root node
*
* @param dir The directory of files and folders to import
* @param root The root node to import into
* @param dir
* The directory of files and folders to import
* @param root
* The root node to import into
*/
private void importDirectory(String dir, NodeRef root)
{
@@ -376,8 +385,10 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Extract the file and folder structure of a ZIP file into the specified directory
*
* @param archive The ZIP archive to extract
* @param extractDir The directory to extract into
* @param archive
* The ZIP archive to extract
* @param extractDir
* The directory to extract into
*/
public static void extractFile(ZipFile archive, String extractDir)
{
@@ -387,9 +398,12 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Extract the file and folder structure of a ZIP file into the specified directory using a progress tracker
*
* @param archive The ZIP archive to extract
* @param extractDir The directory to extract into
* @param tracker The extraction progress tracker to check against during the extraction process
* @param archive
* The ZIP archive to extract
* @param extractDir
* The directory to extract into
* @param tracker
* The extraction progress tracker to check against during the extraction process
*/
public static void extractFile(ZipFile archive, String extractDir, ExtractionProgressTracker tracker)
{
@@ -421,7 +435,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
if (parent != null)
{
File parentFile = new File(parent);
if (!parentFile.exists()) parentFile.mkdirs();
if (!parentFile.exists())
parentFile.mkdirs();
}
try (InputStream zis = archive.getInputStream(entry);
@@ -441,7 +456,7 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
}
else
{
File newdir = new File(extractDir + entry.getName());
File newdir = new File(extractDir + StringUtils.stripAccents(entry.getName()).replaceAll("\\?", "_"));
newdir.mkdirs();
}
}
@@ -463,7 +478,8 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
/**
* Recursively delete a dir of files and directories
*
* @param dir directory to delete
* @param dir
* directory to delete
*/
public static void deleteDir(File dir)
{
@@ -478,8 +494,10 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
{
for (File file : files)
{
if (file.isFile()) file.delete();
else deleteDir(file);
if (file.isFile())
file.delete();
else
deleteDir(file);
}
}
@@ -525,8 +543,7 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
{
void reportProgress(long compressedBytesCount, long uncompressedBytesCount);
ExtractionProgressTracker NONE = new ExtractionProgressTracker()
{
ExtractionProgressTracker NONE = new ExtractionProgressTracker() {
@Override
public void reportProgress(long compressedBytesCount, long uncompressedBytesCount)
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -34,6 +34,10 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
@@ -44,7 +48,6 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -53,15 +56,13 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.test.junitrules.ApplicationContextInit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* This class contains tests for {@link ImporterActionExecuter}.
*
* @author abalmus
*/
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
public class ImporterActionExecuterTest
{
// Rule to initialise the default Alfresco spring configuration
@@ -87,8 +88,7 @@ public class ImporterActionExecuterTest
AuthenticationUtil.setRunAsUserSystem();
// we need a store
storeRef = serviceRegistry.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<StoreRef>()
{
storeRef = serviceRegistry.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<StoreRef>() {
public StoreRef execute()
{
StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.nanoTime());
@@ -102,8 +102,7 @@ public class ImporterActionExecuterTest
{
try
{
serviceRegistry.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
{
serviceRegistry.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute()
{
if (storeRef != null)
@@ -125,8 +124,7 @@ public class ImporterActionExecuterTest
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute()
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
@@ -162,8 +160,7 @@ public class ImporterActionExecuterTest
}
/**
* MNT-16292: Unzipped files which have folders do not get the cm:titled
* aspect applied
* MNT-16292: Unzipped files which have folders do not get the cm:titled aspect applied
*
* @throws IOException
*/
@@ -172,8 +169,7 @@ public class ImporterActionExecuterTest
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute()
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
@@ -224,8 +220,7 @@ public class ImporterActionExecuterTest
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute()
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
@@ -270,8 +265,7 @@ public class ImporterActionExecuterTest
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
public Void execute()
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
@@ -306,6 +300,46 @@ public class ImporterActionExecuterTest
});
}
@Test
public void testUnzipZipFileHavingAccentCharInFolderName() throws IOException
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// create test data
NodeRef zipFileNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTENT).getChildRef();
NodeRef targetFolderNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER).getChildRef();
putContent(zipFileNodeRef, "import-archive-test/accentCharTestZip.zip");
Action action = createAction(zipFileNodeRef, "ImporterActionExecuterTestActionDefinition", targetFolderNodeRef);
try
{
importerActionExecuter.setUncompressedBytesLimit("100000");
importerActionExecuter.execute(action, zipFileNodeRef);
NodeRef importedFolder = nodeService.getChildByName(targetFolderNodeRef, ContentModel.ASSOC_CONTAINS, "accentCharTestZip");
assertNotNull("unzip action failed", importedFolder);
assertTrue("multiple folder structure created", nodeService.getChildAssocs(importedFolder).size() == 1);
}
finally
{
// clean test data
nodeService.deleteNode(targetFolderNodeRef);
nodeService.deleteNode(zipFileNodeRef);
}
return null;
}
});
}
private void putContent(NodeRef zipFileNodeRef, String resource)
{
URL url = AbstractContentTransformerTest.class.getClassLoader().getResource(resource);