| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); 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.apache.poi.openxml4j.opc; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.TreeMap; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| import java.util.zip.ZipOutputStream; |
| |
| import org.apache.poi.EncryptedDocumentException; |
| import org.apache.poi.POIDataSamples; |
| import org.apache.poi.POITestCase; |
| import org.apache.poi.POIXMLException; |
| import org.apache.poi.UnsupportedFileFormatException; |
| import org.apache.poi.openxml4j.OpenXML4JTestDataSamples; |
| import org.apache.poi.openxml4j.exceptions.InvalidFormatException; |
| import org.apache.poi.openxml4j.exceptions.InvalidOperationException; |
| import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; |
| import org.apache.poi.openxml4j.exceptions.ODFNotOfficeXmlFileException; |
| import org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException; |
| import org.apache.poi.openxml4j.opc.internal.ContentTypeManager; |
| import org.apache.poi.openxml4j.opc.internal.FileHelper; |
| import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart; |
| import org.apache.poi.openxml4j.opc.internal.ZipHelper; |
| import org.apache.poi.openxml4j.util.ZipSecureFile; |
| import org.apache.poi.ss.usermodel.Workbook; |
| import org.apache.poi.ss.usermodel.WorkbookFactory; |
| import org.apache.poi.util.DocumentHelper; |
| import org.apache.poi.util.IOUtils; |
| import org.apache.poi.util.POILogFactory; |
| import org.apache.poi.util.POILogger; |
| import org.apache.poi.util.TempFile; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| public final class TestPackage { |
| private static final POILogger logger = POILogFactory.getLogger(TestPackage.class); |
| |
| /** |
| * Test that just opening and closing the file doesn't alter the document. |
| */ |
| @Test |
| public void openSave() throws IOException, InvalidFormatException { |
| String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx"); |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE); |
| try { |
| p.save(targetFile.getAbsoluteFile()); |
| |
| // Compare the original and newly saved document |
| assertTrue(targetFile.exists()); |
| ZipFileAssert.assertEquals(new File(originalFile), targetFile); |
| assertTrue(targetFile.delete()); |
| } finally { |
| // use revert to not re-write the input file |
| p.revert(); |
| } |
| } |
| |
| /** |
| * Test that when we create a new Package, we give it |
| * the correct default content types |
| * @throws IllegalAccessException |
| * @throws NoSuchFieldException |
| * @throws IllegalArgumentException |
| * @throws SecurityException |
| */ |
| @Test |
| public void createGetsContentTypes() |
| throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException { |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx"); |
| |
| // Zap the target file, in case of an earlier run |
| if(targetFile.exists()) { |
| assertTrue(targetFile.delete()); |
| } |
| |
| @SuppressWarnings("resource") |
| OPCPackage pkg = OPCPackage.create(targetFile); |
| |
| // Check it has content types for rels and xml |
| ContentTypeManager ctm = getContentTypeManager(pkg); |
| assertEquals( |
| "application/xml", |
| ctm.getContentType( |
| PackagingURIHelper.createPartName("/foo.xml") |
| ) |
| ); |
| assertEquals( |
| ContentTypes.RELATIONSHIPS_PART, |
| ctm.getContentType( |
| PackagingURIHelper.createPartName("/foo.rels") |
| ) |
| ); |
| assertNull( |
| ctm.getContentType( |
| PackagingURIHelper.createPartName("/foo.txt") |
| ) |
| ); |
| |
| pkg.revert(); |
| } |
| |
| /** |
| * Test package creation. |
| */ |
| @Test |
| public void createPackageAddPart() throws IOException, InvalidFormatException { |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx"); |
| |
| File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestCreatePackageOUTPUT.docx"); |
| |
| // Zap the target file, in case of an earlier run |
| if(targetFile.exists()) { |
| assertTrue(targetFile.delete()); |
| } |
| |
| // Create a package |
| OPCPackage pkg = OPCPackage.create(targetFile); |
| PackagePartName corePartName = PackagingURIHelper |
| .createPartName("/word/document.xml"); |
| |
| pkg.addRelationship(corePartName, TargetMode.INTERNAL, |
| PackageRelationshipTypes.CORE_DOCUMENT, "rId1"); |
| |
| PackagePart corePart = pkg |
| .createPart( |
| corePartName, |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"); |
| |
| Document doc = DocumentHelper.createDocument(); |
| Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document"); |
| doc.appendChild(elDocument); |
| Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body"); |
| elDocument.appendChild(elBody); |
| Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p"); |
| elBody.appendChild(elParagraph); |
| Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r"); |
| elParagraph.appendChild(elRun); |
| Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t"); |
| elRun.appendChild(elText); |
| elText.setTextContent("Hello Open XML !"); |
| |
| StreamHelper.saveXmlInStream(doc, corePart.getOutputStream()); |
| pkg.close(); |
| |
| ZipFileAssert.assertEquals(expectedFile, targetFile); |
| assertTrue(targetFile.delete()); |
| } |
| |
| /** |
| * Tests that we can create a new package, add a core |
| * document and another part, save and re-load and |
| * have everything setup as expected |
| * @throws SAXException |
| */ |
| @Test |
| public void createPackageWithCoreDocument() throws IOException, InvalidFormatException, URISyntaxException, SAXException { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| OPCPackage pkg = OPCPackage.create(baos); |
| |
| // Add a core document |
| PackagePartName corePartName = PackagingURIHelper.createPartName("/xl/workbook.xml"); |
| // Create main part relationship |
| pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT, "rId1"); |
| // Create main document part |
| PackagePart corePart = pkg.createPart(corePartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"); |
| // Put in some dummy content |
| OutputStream coreOut = corePart.getOutputStream(); |
| coreOut.write("<dummy-xml />".getBytes("UTF-8")); |
| coreOut.close(); |
| |
| // And another bit |
| PackagePartName sheetPartName = PackagingURIHelper.createPartName("/xl/worksheets/sheet1.xml"); |
| PackageRelationship rel = |
| corePart.addRelationship(sheetPartName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "rSheet1"); |
| assertNotNull(rel); |
| |
| PackagePart part = pkg.createPart(sheetPartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); |
| assertNotNull(part); |
| |
| // Dummy content again |
| coreOut = corePart.getOutputStream(); |
| coreOut.write("<dummy-xml2 />".getBytes("UTF-8")); |
| coreOut.close(); |
| |
| //add a relationship with internal target: "#Sheet1!A1" |
| corePart.addRelationship(new URI("#Sheet1!A1"), TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "rId2"); |
| |
| // Check things are as expected |
| PackageRelationshipCollection coreRels = |
| pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); |
| assertEquals(1, coreRels.size()); |
| PackageRelationship coreRel = coreRels.getRelationship(0); |
| assertNotNull(coreRel); |
| assertEquals("/", coreRel.getSourceURI().toString()); |
| assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString()); |
| assertNotNull(pkg.getPart(coreRel)); |
| |
| |
| // Save and re-load |
| pkg.close(); |
| File tmp = TempFile.createTempFile("testCreatePackageWithCoreDocument", ".zip"); |
| OutputStream fout = new FileOutputStream(tmp); |
| try { |
| fout.write(baos.toByteArray()); |
| } finally { |
| fout.close(); |
| } |
| pkg = OPCPackage.open(tmp.getPath()); |
| //tmp.delete(); |
| |
| try { |
| // Check still right |
| coreRels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); |
| assertEquals(1, coreRels.size()); |
| coreRel = coreRels.getRelationship(0); |
| |
| assertNotNull(coreRel); |
| assertEquals("/", coreRel.getSourceURI().toString()); |
| assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString()); |
| corePart = pkg.getPart(coreRel); |
| assertNotNull(corePart); |
| |
| PackageRelationshipCollection rels = corePart.getRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"); |
| assertEquals(1, rels.size()); |
| rel = rels.getRelationship(0); |
| assertNotNull(rel); |
| assertEquals("Sheet1!A1", rel.getTargetURI().getRawFragment()); |
| |
| assertMSCompatibility(pkg); |
| } finally { |
| pkg.close(); |
| } |
| } |
| |
| private void assertMSCompatibility(OPCPackage pkg) throws IOException, InvalidFormatException, SAXException { |
| PackagePartName relName = PackagingURIHelper.createPartName(PackageRelationship.getContainerPartRelationship()); |
| PackagePart relPart = pkg.getPart(relName); |
| |
| Document xmlRelationshipsDoc = DocumentHelper.readDocument(relPart.getInputStream()); |
| |
| Element root = xmlRelationshipsDoc.getDocumentElement(); |
| NodeList nodeList = root.getElementsByTagName(PackageRelationship.RELATIONSHIP_TAG_NAME); |
| int nodeCount = nodeList.getLength(); |
| for (int i = 0; i < nodeCount; i++) { |
| Element element = (Element) nodeList.item(i); |
| String value = element.getAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME); |
| assertTrue("Root target must not start with a leading slash ('/'): " + value, value.charAt(0) != '/'); |
| } |
| |
| } |
| |
| /** |
| * Test package opening. |
| */ |
| @Test |
| public void openPackage() throws IOException, InvalidFormatException { |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestOpenPackageTMP.docx"); |
| |
| File inputFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageINPUT.docx"); |
| |
| File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageOUTPUT.docx"); |
| |
| // Copy the input file in the output directory |
| FileHelper.copyFile(inputFile, targetFile); |
| |
| // Create a package |
| OPCPackage pkg = OPCPackage.open(targetFile.getAbsolutePath()); |
| |
| // Modify core part |
| PackagePartName corePartName = PackagingURIHelper |
| .createPartName("/word/document.xml"); |
| |
| PackagePart corePart = pkg.getPart(corePartName); |
| |
| // Delete some part to have a valid document |
| for (PackageRelationship rel : corePart.getRelationships()) { |
| corePart.removeRelationship(rel.getId()); |
| pkg.removePart(PackagingURIHelper.createPartName(PackagingURIHelper |
| .resolvePartUri(corePart.getPartName().getURI(), rel |
| .getTargetURI()))); |
| } |
| |
| // Create a content |
| Document doc = DocumentHelper.createDocument(); |
| Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document"); |
| doc.appendChild(elDocument); |
| Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body"); |
| elDocument.appendChild(elBody); |
| Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p"); |
| elBody.appendChild(elParagraph); |
| Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r"); |
| elParagraph.appendChild(elRun); |
| Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t"); |
| elRun.appendChild(elText); |
| elText.setTextContent("Hello Open XML !"); |
| |
| StreamHelper.saveXmlInStream(doc, corePart.getOutputStream()); |
| |
| // Save and close |
| try { |
| pkg.close(); |
| } catch (IOException e) { |
| fail(); |
| } |
| |
| ZipFileAssert.assertEquals(expectedFile, targetFile); |
| assertTrue(targetFile.delete()); |
| } |
| |
| /** |
| * Checks that we can write a package to a simple |
| * OutputStream, in addition to the normal writing |
| * to a file |
| */ |
| @Test |
| public void saveToOutputStream() throws IOException, InvalidFormatException { |
| String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx"); |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE); |
| try { |
| FileOutputStream fout = new FileOutputStream(targetFile); |
| try { |
| p.save(fout); |
| } finally { |
| fout.close(); |
| } |
| |
| // Compare the original and newly saved document |
| assertTrue(targetFile.exists()); |
| ZipFileAssert.assertEquals(new File(originalFile), targetFile); |
| assertTrue(targetFile.delete()); |
| } finally { |
| // use revert to not re-write the input file |
| p.revert(); |
| } |
| } |
| |
| /** |
| * Checks that we can open+read a package from a |
| * simple InputStream, in addition to the normal |
| * reading from a file |
| */ |
| @Test |
| public void openFromInputStream() throws IOException, InvalidFormatException { |
| String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx"); |
| |
| FileInputStream finp = new FileInputStream(originalFile); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(finp); |
| |
| assertNotNull(p); |
| assertNotNull(p.getRelationships()); |
| assertEquals(12, p.getParts().size()); |
| |
| // Check it has the usual bits |
| assertTrue(p.hasRelationships()); |
| assertTrue(p.containPart(PackagingURIHelper.createPartName("/_rels/.rels"))); |
| |
| p.revert(); |
| finp.close(); |
| } |
| |
| /** |
| * TODO: fix and enable |
| * @throws URISyntaxException |
| */ |
| @Test |
| @Ignore |
| public void removePartRecursive() throws IOException, InvalidFormatException, URISyntaxException { |
| String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx"); |
| File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveOUTPUT.docx"); |
| File tempFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveTMP.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE); |
| p.removePartRecursive(PackagingURIHelper.createPartName(new URI( |
| "/word/document.xml"))); |
| p.save(tempFile.getAbsoluteFile()); |
| |
| // Compare the original and newly saved document |
| assertTrue(targetFile.exists()); |
| ZipFileAssert.assertEquals(targetFile, tempFile); |
| assertTrue(targetFile.delete()); |
| |
| p.revert(); |
| } |
| |
| @Test |
| public void deletePart() throws InvalidFormatException { |
| TreeMap<PackagePartName, String> expectedValues; |
| TreeMap<PackagePartName, String> values; |
| |
| values = new TreeMap<PackagePartName, String>(); |
| |
| // Expected values |
| expectedValues = new TreeMap<PackagePartName, String>(); |
| expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"), |
| "application/vnd.openxmlformats-package.relationships+xml"); |
| |
| expectedValues |
| .put(PackagingURIHelper.createPartName("/docProps/app.xml"), |
| "application/vnd.openxmlformats-officedocument.extended-properties+xml"); |
| expectedValues.put(PackagingURIHelper |
| .createPartName("/docProps/core.xml"), |
| "application/vnd.openxmlformats-package.core-properties+xml"); |
| expectedValues |
| .put(PackagingURIHelper.createPartName("/word/fontTable.xml"), |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"); |
| expectedValues.put(PackagingURIHelper |
| .createPartName("/word/media/image1.gif"), "image/gif"); |
| expectedValues |
| .put(PackagingURIHelper.createPartName("/word/settings.xml"), |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"); |
| expectedValues |
| .put(PackagingURIHelper.createPartName("/word/styles.xml"), |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"); |
| expectedValues.put(PackagingURIHelper |
| .createPartName("/word/theme/theme1.xml"), |
| "application/vnd.openxmlformats-officedocument.theme+xml"); |
| expectedValues |
| .put( |
| PackagingURIHelper |
| .createPartName("/word/webSettings.xml"), |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"); |
| |
| String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE); |
| // Remove the core part |
| p.deletePart(PackagingURIHelper.createPartName("/word/document.xml")); |
| |
| for (PackagePart part : p.getParts()) { |
| values.put(part.getPartName(), part.getContentType()); |
| logger.log(POILogger.DEBUG, part.getPartName()); |
| } |
| |
| // Compare expected values with values return by the package |
| for (PackagePartName partName : expectedValues.keySet()) { |
| assertNotNull(values.get(partName)); |
| assertEquals(expectedValues.get(partName), values.get(partName)); |
| } |
| // Don't save modifications |
| p.revert(); |
| } |
| |
| @Test |
| public void deletePartRecursive() throws InvalidFormatException { |
| TreeMap<PackagePartName, String> expectedValues; |
| TreeMap<PackagePartName, String> values; |
| |
| values = new TreeMap<PackagePartName, String>(); |
| |
| // Expected values |
| expectedValues = new TreeMap<PackagePartName, String>(); |
| expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"), |
| "application/vnd.openxmlformats-package.relationships+xml"); |
| |
| expectedValues |
| .put(PackagingURIHelper.createPartName("/docProps/app.xml"), |
| "application/vnd.openxmlformats-officedocument.extended-properties+xml"); |
| expectedValues.put(PackagingURIHelper |
| .createPartName("/docProps/core.xml"), |
| "application/vnd.openxmlformats-package.core-properties+xml"); |
| |
| String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE); |
| // Remove the core part |
| p.deletePartRecursive(PackagingURIHelper.createPartName("/word/document.xml")); |
| |
| for (PackagePart part : p.getParts()) { |
| values.put(part.getPartName(), part.getContentType()); |
| logger.log(POILogger.DEBUG, part.getPartName()); |
| } |
| |
| // Compare expected values with values return by the package |
| for (PackagePartName partName : expectedValues.keySet()) { |
| assertNotNull(values.get(partName)); |
| assertEquals(expectedValues.get(partName), values.get(partName)); |
| } |
| // Don't save modifications |
| p.revert(); |
| } |
| |
| /** |
| * Test that we can open a file by path, and then |
| * write changes to it. |
| */ |
| @Test |
| public void openFileThenOverwrite() throws IOException, InvalidFormatException { |
| File tempFile = TempFile.createTempFile("poiTesting","tmp"); |
| File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx"); |
| FileHelper.copyFile(origFile, tempFile); |
| |
| // Open the temp file |
| OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE); |
| // Close it |
| p.close(); |
| // Delete it |
| assertTrue(tempFile.delete()); |
| |
| // Reset |
| FileHelper.copyFile(origFile, tempFile); |
| p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE); |
| |
| // Save it to the same file - not allowed |
| try { |
| p.save(tempFile); |
| fail("You shouldn't be able to call save(File) to overwrite the current file"); |
| } catch(InvalidOperationException e) { |
| // expected here |
| } |
| |
| p.close(); |
| // Delete it |
| assertTrue(tempFile.delete()); |
| |
| |
| // Open it read only, then close and delete - allowed |
| FileHelper.copyFile(origFile, tempFile); |
| p = OPCPackage.open(tempFile.toString(), PackageAccess.READ); |
| p.close(); |
| assertTrue(tempFile.delete()); |
| } |
| /** |
| * Test that we can open a file by path, save it |
| * to another file, then delete both |
| */ |
| @Test |
| public void openFileThenSaveDelete() throws IOException, InvalidFormatException { |
| File tempFile = TempFile.createTempFile("poiTesting","tmp"); |
| File tempFile2 = TempFile.createTempFile("poiTesting","tmp"); |
| File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx"); |
| FileHelper.copyFile(origFile, tempFile); |
| |
| // Open the temp file |
| OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE); |
| |
| // Save it to a different file |
| p.save(tempFile2); |
| p.close(); |
| |
| // Delete both the files |
| assertTrue(tempFile.delete()); |
| assertTrue(tempFile2.delete()); |
| } |
| |
| private static ContentTypeManager getContentTypeManager(OPCPackage pkg) { |
| return POITestCase.getFieldValue(OPCPackage.class, pkg, ContentTypeManager.class, "contentTypeManager"); |
| } |
| |
| @Test |
| public void getPartsByName() throws IOException, InvalidFormatException { |
| String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx"); |
| |
| @SuppressWarnings("resource") |
| OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ_WRITE); |
| try { |
| List<PackagePart> rs = pkg.getPartsByName(Pattern.compile("/word/.*?\\.xml")); |
| HashMap<String, PackagePart> selected = new HashMap<String, PackagePart>(); |
| |
| for(PackagePart p : rs) |
| selected.put(p.getPartName().getName(), p); |
| |
| assertEquals(6, selected.size()); |
| assertTrue(selected.containsKey("/word/document.xml")); |
| assertTrue(selected.containsKey("/word/fontTable.xml")); |
| assertTrue(selected.containsKey("/word/settings.xml")); |
| assertTrue(selected.containsKey("/word/styles.xml")); |
| assertTrue(selected.containsKey("/word/theme/theme1.xml")); |
| assertTrue(selected.containsKey("/word/webSettings.xml")); |
| } finally { |
| // use revert to not re-write the input file |
| pkg.revert(); |
| } |
| } |
| |
| @Test |
| public void getPartSize() throws IOException, InvalidFormatException { |
| String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx"); |
| OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ); |
| try { |
| int checked = 0; |
| for (PackagePart part : pkg.getParts()) { |
| // Can get the size of zip parts |
| if (part.getPartName().getName().equals("/word/document.xml")) { |
| checked++; |
| assertEquals(ZipPackagePart.class, part.getClass()); |
| assertEquals(6031L, part.getSize()); |
| } |
| if (part.getPartName().getName().equals("/word/fontTable.xml")) { |
| checked++; |
| assertEquals(ZipPackagePart.class, part.getClass()); |
| assertEquals(1312L, part.getSize()); |
| } |
| |
| // But not from the others |
| if (part.getPartName().getName().equals("/docProps/core.xml")) { |
| checked++; |
| assertEquals(PackagePropertiesPart.class, part.getClass()); |
| assertEquals(-1, part.getSize()); |
| } |
| } |
| // Ensure we actually found the parts we want to check |
| assertEquals(3, checked); |
| } finally { |
| pkg.close(); |
| } |
| } |
| |
| @Test |
| public void replaceContentType() |
| throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException { |
| InputStream is = OpenXML4JTestDataSamples.openSampleStream("sample.xlsx"); |
| @SuppressWarnings("resource") |
| OPCPackage p = OPCPackage.open(is); |
| |
| ContentTypeManager mgr = getContentTypeManager(p); |
| |
| assertTrue(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")); |
| assertFalse(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml")); |
| |
| assertTrue( |
| p.replaceContentType( |
| "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", |
| "application/vnd.ms-excel.sheet.macroEnabled.main+xml") |
| ); |
| |
| assertFalse(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")); |
| assertTrue(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml")); |
| p.revert(); |
| is.close(); |
| } |
| |
| /** |
| * Verify we give helpful exceptions (or as best we can) when |
| * supplied with non-OOXML file types (eg OLE2, ODF) |
| */ |
| @Test |
| public void NonOOXMLFileTypes() throws Exception { |
| // Spreadsheet has a good mix of alternate file types |
| POIDataSamples files = POIDataSamples.getSpreadSheetInstance(); |
| |
| // OLE2 - Stream |
| try { |
| InputStream stream = files.openResourceAsStream("SampleSS.xls"); |
| try { |
| OPCPackage.open(stream); |
| } finally { |
| stream.close(); |
| } |
| fail("Shouldn't be able to open OLE2"); |
| } catch (OLE2NotOfficeXmlFileException e) { |
| assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format")); |
| assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML")); |
| } |
| // OLE2 - File |
| try { |
| OPCPackage.open(files.getFile("SampleSS.xls")); |
| fail("Shouldn't be able to open OLE2"); |
| } catch (OLE2NotOfficeXmlFileException e) { |
| assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format")); |
| assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML")); |
| } |
| |
| // Raw XML - Stream |
| try { |
| InputStream stream = files.openResourceAsStream("SampleSS.xml"); |
| try { |
| OPCPackage.open(stream); |
| } finally { |
| stream.close(); |
| } |
| fail("Shouldn't be able to open XML"); |
| } catch (NotOfficeXmlFileException e) { |
| assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file")); |
| assertTrue(e.getMessage().contains("Formats such as Office 2003 XML")); |
| } |
| // Raw XML - File |
| try { |
| OPCPackage.open(files.getFile("SampleSS.xml")); |
| fail("Shouldn't be able to open XML"); |
| } catch (NotOfficeXmlFileException e) { |
| assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file")); |
| assertTrue(e.getMessage().contains("Formats such as Office 2003 XML")); |
| } |
| |
| // ODF / ODS - Stream |
| try { |
| InputStream stream = files.openResourceAsStream("SampleSS.ods"); |
| try { |
| OPCPackage.open(stream); |
| } finally { |
| stream.close(); |
| } |
| fail("Shouldn't be able to open ODS"); |
| } catch (ODFNotOfficeXmlFileException e) { |
| assertTrue(e.toString().contains("The supplied data appears to be in ODF")); |
| assertTrue(e.toString().contains("Formats like these (eg ODS")); |
| } |
| // ODF / ODS - File |
| try { |
| OPCPackage.open(files.getFile("SampleSS.ods")); |
| fail("Shouldn't be able to open ODS"); |
| } catch (ODFNotOfficeXmlFileException e) { |
| assertTrue(e.toString().contains("The supplied data appears to be in ODF")); |
| assertTrue(e.toString().contains("Formats like these (eg ODS")); |
| } |
| |
| // Plain Text - Stream |
| try { |
| InputStream stream = files.openResourceAsStream("SampleSS.txt"); |
| try { |
| OPCPackage.open(stream); |
| } finally { |
| stream.close(); |
| } |
| fail("Shouldn't be able to open Plain Text"); |
| } catch (NotOfficeXmlFileException e) { |
| assertTrue(e.getMessage().contains("No valid entries or contents found")); |
| assertTrue(e.getMessage().contains("not a valid OOXML")); |
| } |
| // Plain Text - File |
| try { |
| OPCPackage.open(files.getFile("SampleSS.txt")); |
| fail("Shouldn't be able to open Plain Text"); |
| } catch (UnsupportedFileFormatException e) { |
| // Unhelpful low-level error, sorry |
| } |
| } |
| |
| @Test(expected=IOException.class) |
| public void zipBombCreateAndHandle() |
| throws IOException, EncryptedDocumentException, InvalidFormatException { |
| // #50090 / #56865 |
| ZipFile zipFile = ZipHelper.openZipFile(OpenXML4JTestDataSamples.getSampleFile("sample.xlsx")); |
| assertNotNull(zipFile); |
| |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(2500000); |
| ZipOutputStream append = new ZipOutputStream(bos); |
| // first, copy contents from existing war |
| Enumeration<? extends ZipEntry> entries = zipFile.entries(); |
| while (entries.hasMoreElements()) { |
| ZipEntry e2 = entries.nextElement(); |
| ZipEntry e = new ZipEntry(e2.getName()); |
| e.setTime(e2.getTime()); |
| e.setComment(e2.getComment()); |
| e.setSize(e2.getSize()); |
| |
| append.putNextEntry(e); |
| if (!e.isDirectory()) { |
| InputStream is = zipFile.getInputStream(e); |
| if (e.getName().equals("[Content_Types].xml")) { |
| ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); |
| IOUtils.copy(is, bos2); |
| long size = bos2.size()-"</Types>".length(); |
| append.write(bos2.toByteArray(), 0, (int)size); |
| byte spam[] = new byte[0x7FFF]; |
| for (int i=0; i<spam.length; i++) spam[i] = ' '; |
| // 0x7FFF0000 is the maximum for 32-bit zips, but less still works |
| while (size < 0x7FFF00) { |
| append.write(spam); |
| size += spam.length; |
| } |
| append.write("</Types>".getBytes("UTF-8")); |
| size += 8; |
| e.setSize(size); |
| } else { |
| IOUtils.copy(is, append); |
| } |
| is.close(); |
| } |
| append.closeEntry(); |
| } |
| |
| append.close(); |
| zipFile.close(); |
| |
| byte buf[] = bos.toByteArray(); |
| //noinspection UnusedAssignment |
| bos = null; |
| |
| Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(buf)); |
| wb.getSheetAt(0); |
| wb.close(); |
| zipFile.close(); |
| } |
| |
| @Test |
| public void zipBombCheckSizes() |
| throws IOException, EncryptedDocumentException, InvalidFormatException { |
| File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx"); |
| |
| try { |
| double min_ratio = Double.MAX_VALUE; |
| long max_size = 0; |
| ZipFile zf = ZipHelper.openZipFile(file); |
| assertNotNull(zf); |
| Enumeration<? extends ZipEntry> entries = zf.entries(); |
| while (entries.hasMoreElements()) { |
| ZipEntry ze = entries.nextElement(); |
| double ratio = (double)ze.getCompressedSize() / (double)ze.getSize(); |
| min_ratio = Math.min(min_ratio, ratio); |
| max_size = Math.max(max_size, ze.getSize()); |
| } |
| zf.close(); |
| |
| // use values close to, but within the limits |
| ZipSecureFile.setMinInflateRatio(min_ratio-0.002); |
| assertEquals(min_ratio-0.002, ZipSecureFile.getMinInflateRatio(), 0.00001); |
| ZipSecureFile.setMaxEntrySize(max_size+1); |
| assertEquals(max_size+1, ZipSecureFile.getMaxEntrySize()); |
| |
| WorkbookFactory.create(file, null, true).close(); |
| |
| // check ratio out of bounds |
| ZipSecureFile.setMinInflateRatio(min_ratio+0.002); |
| try { |
| WorkbookFactory.create(file, null, true).close(); |
| // this is a bit strange, as there will be different exceptions thrown |
| // depending if this executed via "ant test" or within eclipse |
| // maybe a difference in JDK ... |
| } catch (InvalidFormatException e) { |
| checkForZipBombException(e); |
| } catch (POIXMLException e) { |
| checkForZipBombException(e); |
| } |
| |
| // check max entry size ouf of bounds |
| ZipSecureFile.setMinInflateRatio(min_ratio-0.002); |
| ZipSecureFile.setMaxEntrySize(max_size-1); |
| try { |
| WorkbookFactory.create(file, null, true).close(); |
| } catch (InvalidFormatException e) { |
| checkForZipBombException(e); |
| } catch (POIXMLException e) { |
| checkForZipBombException(e); |
| } |
| } finally { |
| // reset otherwise a lot of ooxml tests will fail |
| ZipSecureFile.setMinInflateRatio(0.01d); |
| ZipSecureFile.setMaxEntrySize(0xFFFFFFFFL); |
| } |
| } |
| |
| private void checkForZipBombException(Throwable e) { |
| if(e instanceof InvocationTargetException) { |
| InvocationTargetException t = (InvocationTargetException)e; |
| IOException t2 = (IOException)t.getTargetException(); |
| if(t2.getMessage().startsWith("Zip bomb detected!")) { |
| return; |
| } |
| } |
| |
| String msg = e.getMessage(); |
| if(msg != null && msg.startsWith("Zip bomb detected!")) { |
| return; |
| } |
| |
| // recursively check the causes for the message as it can be nested further down in the exception-tree |
| if(e.getCause() != null && e.getCause() != e) { |
| checkForZipBombException(e.getCause()); |
| return; |
| } |
| |
| throw new IllegalStateException("Expected to catch an Exception because of a detected Zip Bomb, but did not find the related error message in the exception", e); |
| } |
| |
| @Test |
| public void testConstructors() throws IOException { |
| // verify the various ways to construct a ZipSecureFile |
| File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx"); |
| ZipSecureFile zipFile = new ZipSecureFile(file); |
| assertNotNull(zipFile.getName()); |
| zipFile.close(); |
| |
| zipFile = new ZipSecureFile(file, ZipFile.OPEN_READ); |
| assertNotNull(zipFile.getName()); |
| zipFile.close(); |
| |
| zipFile = new ZipSecureFile(file.getAbsolutePath()); |
| assertNotNull(zipFile.getName()); |
| zipFile.close(); |
| } |
| |
| @Test |
| public void testMaxTextSize() { |
| long before = ZipSecureFile.getMaxTextSize(); |
| try { |
| ZipSecureFile.setMaxTextSize(12345); |
| assertEquals(12345, ZipSecureFile.getMaxTextSize()); |
| } finally { |
| ZipSecureFile.setMaxTextSize(before); |
| } |
| } |
| |
| // bug 60128 |
| @Test |
| public void testCorruptFile() throws IOException { |
| OPCPackage pkg = null; |
| File file = OpenXML4JTestDataSamples.getSampleFile("invalid.xlsx"); |
| try { |
| pkg = OPCPackage.open(file, PackageAccess.READ); |
| } catch (Exception e) { |
| System.out.println(e.getClass().getName()); |
| System.out.println(e.getMessage()); |
| e.printStackTrace(); |
| } finally { |
| if (pkg != null) { |
| pkg.close(); |
| } |
| } |
| } |
| } |