You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestPackage.java 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.openxml4j.opc;
  16. import com.google.common.hash.Hashing;
  17. import com.google.common.io.Files;
  18. import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
  19. import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
  20. import org.apache.commons.compress.archivers.zip.ZipFile;
  21. import org.apache.poi.EncryptedDocumentException;
  22. import org.apache.poi.POIDataSamples;
  23. import org.apache.poi.POITestCase;
  24. import org.apache.poi.UnsupportedFileFormatException;
  25. import org.apache.poi.extractor.POITextExtractor;
  26. import org.apache.poi.ooxml.POIXMLException;
  27. import org.apache.poi.ooxml.extractor.ExtractorFactory;
  28. import org.apache.poi.ooxml.util.DocumentHelper;
  29. import org.apache.poi.openxml4j.OpenXML4JTestDataSamples;
  30. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  31. import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
  32. import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
  33. import org.apache.poi.openxml4j.exceptions.ODFNotOfficeXmlFileException;
  34. import org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException;
  35. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  36. import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
  37. import org.apache.poi.openxml4j.opc.internal.FileHelper;
  38. import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
  39. import org.apache.poi.openxml4j.opc.internal.ZipHelper;
  40. import org.apache.poi.openxml4j.util.ZipSecureFile;
  41. import org.apache.poi.sl.usermodel.SlideShow;
  42. import org.apache.poi.sl.usermodel.SlideShowFactory;
  43. import org.apache.poi.ss.usermodel.Workbook;
  44. import org.apache.poi.ss.usermodel.WorkbookFactory;
  45. import org.apache.poi.util.IOUtils;
  46. import org.apache.poi.util.POILogFactory;
  47. import org.apache.poi.util.POILogger;
  48. import org.apache.poi.util.TempFile;
  49. import org.apache.poi.xssf.XSSFTestDataSamples;
  50. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  51. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  52. import org.apache.poi.xwpf.usermodel.XWPFRelation;
  53. import org.apache.xmlbeans.XmlException;
  54. import org.hamcrest.Description;
  55. import org.hamcrest.TypeSafeMatcher;
  56. import org.junit.Ignore;
  57. import org.junit.Rule;
  58. import org.junit.Test;
  59. import org.junit.rules.ExpectedException;
  60. import org.w3c.dom.Document;
  61. import org.w3c.dom.Element;
  62. import org.w3c.dom.NodeList;
  63. import org.xml.sax.SAXException;
  64. import org.xml.sax.SAXParseException;
  65. import java.io.BufferedInputStream;
  66. import java.io.ByteArrayInputStream;
  67. import java.io.ByteArrayOutputStream;
  68. import java.io.File;
  69. import java.io.FileInputStream;
  70. import java.io.FileOutputStream;
  71. import java.io.IOException;
  72. import java.io.InputStream;
  73. import java.io.OutputStream;
  74. import java.io.PushbackInputStream;
  75. import java.net.URI;
  76. import java.net.URISyntaxException;
  77. import java.util.Arrays;
  78. import java.util.Enumeration;
  79. import java.util.HashMap;
  80. import java.util.List;
  81. import java.util.TreeMap;
  82. import java.util.function.BiConsumer;
  83. import java.util.regex.Pattern;
  84. import java.util.zip.ZipException;
  85. import static org.junit.Assert.assertEquals;
  86. import static org.junit.Assert.assertFalse;
  87. import static org.junit.Assert.assertNotNull;
  88. import static org.junit.Assert.assertNull;
  89. import static org.junit.Assert.assertTrue;
  90. import static org.junit.Assert.fail;
  91. public final class TestPackage {
  92. private static final POILogger logger = POILogFactory.getLogger(TestPackage.class);
  93. @Rule
  94. public ExpectedException expectedEx = ExpectedException.none();
  95. /**
  96. * Test that just opening and closing the file doesn't alter the document.
  97. */
  98. @Test
  99. public void openSave() throws IOException, InvalidFormatException {
  100. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  101. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
  102. @SuppressWarnings("resource")
  103. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  104. try {
  105. p.save(targetFile.getAbsoluteFile());
  106. // Compare the original and newly saved document
  107. assertTrue(targetFile.exists());
  108. ZipFileAssert.assertEquals(new File(originalFile), targetFile);
  109. assertTrue(targetFile.delete());
  110. } finally {
  111. // use revert to not re-write the input file
  112. p.revert();
  113. }
  114. }
  115. /**
  116. * Test that when we create a new Package, we give it
  117. * the correct default content types
  118. */
  119. @Test
  120. public void createGetsContentTypes()
  121. throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException {
  122. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
  123. // Zap the target file, in case of an earlier run
  124. if(targetFile.exists()) {
  125. assertTrue(targetFile.delete());
  126. }
  127. @SuppressWarnings("resource")
  128. OPCPackage pkg = OPCPackage.create(targetFile);
  129. // Check it has content types for rels and xml
  130. ContentTypeManager ctm = getContentTypeManager(pkg);
  131. assertEquals(
  132. "application/xml",
  133. ctm.getContentType(
  134. PackagingURIHelper.createPartName("/foo.xml")
  135. )
  136. );
  137. assertEquals(
  138. ContentTypes.RELATIONSHIPS_PART,
  139. ctm.getContentType(
  140. PackagingURIHelper.createPartName("/foo.rels")
  141. )
  142. );
  143. assertNull(
  144. ctm.getContentType(
  145. PackagingURIHelper.createPartName("/foo.txt")
  146. )
  147. );
  148. pkg.revert();
  149. }
  150. /**
  151. * Test package creation.
  152. */
  153. @Test
  154. public void createPackageAddPart() throws IOException, InvalidFormatException {
  155. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
  156. File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestCreatePackageOUTPUT.docx");
  157. // Zap the target file, in case of an earlier run
  158. if(targetFile.exists()) {
  159. assertTrue(targetFile.delete());
  160. }
  161. // Create a package
  162. OPCPackage pkg = OPCPackage.create(targetFile);
  163. PackagePartName corePartName = PackagingURIHelper
  164. .createPartName("/word/document.xml");
  165. pkg.addRelationship(corePartName, TargetMode.INTERNAL,
  166. PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
  167. PackagePart corePart = pkg
  168. .createPart(
  169. corePartName,
  170. "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
  171. Document doc = DocumentHelper.createDocument();
  172. Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document");
  173. doc.appendChild(elDocument);
  174. Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body");
  175. elDocument.appendChild(elBody);
  176. Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p");
  177. elBody.appendChild(elParagraph);
  178. Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r");
  179. elParagraph.appendChild(elRun);
  180. Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t");
  181. elRun.appendChild(elText);
  182. elText.setTextContent("Hello Open XML !");
  183. StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
  184. pkg.close();
  185. ZipFileAssert.assertEquals(expectedFile, targetFile);
  186. assertTrue(targetFile.delete());
  187. }
  188. /**
  189. * Tests that we can create a new package, add a core
  190. * document and another part, save and re-load and
  191. * have everything setup as expected
  192. */
  193. @Test
  194. public void createPackageWithCoreDocument() throws IOException, InvalidFormatException, URISyntaxException, SAXException {
  195. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  196. OPCPackage pkg = OPCPackage.create(baos);
  197. // Add a core document
  198. PackagePartName corePartName = PackagingURIHelper.createPartName("/xl/workbook.xml");
  199. // Create main part relationship
  200. pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
  201. // Create main document part
  202. PackagePart corePart = pkg.createPart(corePartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
  203. // Put in some dummy content
  204. OutputStream coreOut = corePart.getOutputStream();
  205. coreOut.write("<dummy-xml />".getBytes("UTF-8"));
  206. coreOut.close();
  207. // And another bit
  208. PackagePartName sheetPartName = PackagingURIHelper.createPartName("/xl/worksheets/sheet1.xml");
  209. PackageRelationship rel =
  210. corePart.addRelationship(sheetPartName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "rSheet1");
  211. assertNotNull(rel);
  212. PackagePart part = pkg.createPart(sheetPartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
  213. assertNotNull(part);
  214. // Dummy content again
  215. coreOut = corePart.getOutputStream();
  216. coreOut.write("<dummy-xml2 />".getBytes("UTF-8"));
  217. coreOut.close();
  218. //add a relationship with internal target: "#Sheet1!A1"
  219. corePart.addRelationship(new URI("#Sheet1!A1"), TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "rId2");
  220. // Check things are as expected
  221. PackageRelationshipCollection coreRels =
  222. pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
  223. assertEquals(1, coreRels.size());
  224. PackageRelationship coreRel = coreRels.getRelationship(0);
  225. assertNotNull(coreRel);
  226. assertEquals("/", coreRel.getSourceURI().toString());
  227. assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
  228. assertNotNull(pkg.getPart(coreRel));
  229. // Save and re-load
  230. pkg.close();
  231. File tmp = TempFile.createTempFile("testCreatePackageWithCoreDocument", ".zip");
  232. try (OutputStream fout = new FileOutputStream(tmp)) {
  233. fout.write(baos.toByteArray());
  234. }
  235. pkg = OPCPackage.open(tmp.getPath());
  236. //tmp.delete();
  237. try {
  238. // Check still right
  239. coreRels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
  240. assertEquals(1, coreRels.size());
  241. coreRel = coreRels.getRelationship(0);
  242. assertNotNull(coreRel);
  243. assertEquals("/", coreRel.getSourceURI().toString());
  244. assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
  245. corePart = pkg.getPart(coreRel);
  246. assertNotNull(corePart);
  247. PackageRelationshipCollection rels = corePart.getRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
  248. assertEquals(1, rels.size());
  249. rel = rels.getRelationship(0);
  250. assertNotNull(rel);
  251. assertEquals("Sheet1!A1", rel.getTargetURI().getRawFragment());
  252. assertMSCompatibility(pkg);
  253. } finally {
  254. pkg.close();
  255. }
  256. }
  257. private void assertMSCompatibility(OPCPackage pkg) throws IOException, InvalidFormatException, SAXException {
  258. PackagePartName relName = PackagingURIHelper.createPartName(PackageRelationship.getContainerPartRelationship());
  259. PackagePart relPart = pkg.getPart(relName);
  260. Document xmlRelationshipsDoc = DocumentHelper.readDocument(relPart.getInputStream());
  261. Element root = xmlRelationshipsDoc.getDocumentElement();
  262. NodeList nodeList = root.getElementsByTagName(PackageRelationship.RELATIONSHIP_TAG_NAME);
  263. int nodeCount = nodeList.getLength();
  264. for (int i = 0; i < nodeCount; i++) {
  265. Element element = (Element) nodeList.item(i);
  266. String value = element.getAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME);
  267. assertTrue("Root target must not start with a leading slash ('/'): " + value, value.charAt(0) != '/');
  268. }
  269. }
  270. /**
  271. * Test package opening.
  272. */
  273. @Test
  274. public void openPackage() throws IOException, InvalidFormatException {
  275. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestOpenPackageTMP.docx");
  276. File inputFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageINPUT.docx");
  277. File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageOUTPUT.docx");
  278. // Copy the input file in the output directory
  279. FileHelper.copyFile(inputFile, targetFile);
  280. // Create a package
  281. OPCPackage pkg = OPCPackage.open(targetFile.getAbsolutePath());
  282. // Modify core part
  283. PackagePartName corePartName = PackagingURIHelper
  284. .createPartName("/word/document.xml");
  285. PackagePart corePart = pkg.getPart(corePartName);
  286. // Delete some part to have a valid document
  287. for (PackageRelationship rel : corePart.getRelationships()) {
  288. corePart.removeRelationship(rel.getId());
  289. pkg.removePart(PackagingURIHelper.createPartName(PackagingURIHelper
  290. .resolvePartUri(corePart.getPartName().getURI(), rel
  291. .getTargetURI())));
  292. }
  293. // Create a content
  294. Document doc = DocumentHelper.createDocument();
  295. Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document");
  296. doc.appendChild(elDocument);
  297. Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body");
  298. elDocument.appendChild(elBody);
  299. Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p");
  300. elBody.appendChild(elParagraph);
  301. Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r");
  302. elParagraph.appendChild(elRun);
  303. Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t");
  304. elRun.appendChild(elText);
  305. elText.setTextContent("Hello Open XML !");
  306. StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
  307. // Save and close
  308. try {
  309. pkg.close();
  310. } catch (IOException e) {
  311. fail();
  312. }
  313. ZipFileAssert.assertEquals(expectedFile, targetFile);
  314. assertTrue(targetFile.delete());
  315. }
  316. /**
  317. * Checks that we can write a package to a simple
  318. * OutputStream, in addition to the normal writing
  319. * to a file
  320. */
  321. @Test
  322. public void saveToOutputStream() throws IOException, InvalidFormatException {
  323. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  324. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
  325. @SuppressWarnings("resource")
  326. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  327. try {
  328. try (FileOutputStream fout = new FileOutputStream(targetFile)) {
  329. p.save(fout);
  330. }
  331. // Compare the original and newly saved document
  332. assertTrue(targetFile.exists());
  333. ZipFileAssert.assertEquals(new File(originalFile), targetFile);
  334. assertTrue(targetFile.delete());
  335. } finally {
  336. // use revert to not re-write the input file
  337. p.revert();
  338. }
  339. }
  340. /**
  341. * Checks that we can open+read a package from a
  342. * simple InputStream, in addition to the normal
  343. * reading from a file
  344. */
  345. @Test
  346. public void openFromInputStream() throws IOException, InvalidFormatException {
  347. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  348. FileInputStream finp = new FileInputStream(originalFile);
  349. @SuppressWarnings("resource")
  350. OPCPackage p = OPCPackage.open(finp);
  351. assertNotNull(p);
  352. assertNotNull(p.getRelationships());
  353. assertEquals(12, p.getParts().size());
  354. // Check it has the usual bits
  355. assertTrue(p.hasRelationships());
  356. assertTrue(p.containPart(PackagingURIHelper.createPartName("/_rels/.rels")));
  357. p.revert();
  358. finp.close();
  359. }
  360. /**
  361. * TODO: fix and enable
  362. */
  363. @Test
  364. @Ignore
  365. public void removePartRecursive() throws IOException, InvalidFormatException, URISyntaxException {
  366. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  367. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveOUTPUT.docx");
  368. File tempFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveTMP.docx");
  369. @SuppressWarnings("resource")
  370. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  371. p.removePartRecursive(PackagingURIHelper.createPartName(new URI(
  372. "/word/document.xml")));
  373. p.save(tempFile.getAbsoluteFile());
  374. // Compare the original and newly saved document
  375. assertTrue(targetFile.exists());
  376. ZipFileAssert.assertEquals(targetFile, tempFile);
  377. assertTrue(targetFile.delete());
  378. p.revert();
  379. }
  380. @Test
  381. public void deletePart() throws InvalidFormatException {
  382. TreeMap<PackagePartName, String> expectedValues;
  383. TreeMap<PackagePartName, String> values;
  384. values = new TreeMap<>();
  385. // Expected values
  386. expectedValues = new TreeMap<>();
  387. expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
  388. "application/vnd.openxmlformats-package.relationships+xml");
  389. expectedValues
  390. .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
  391. "application/vnd.openxmlformats-officedocument.extended-properties+xml");
  392. expectedValues.put(PackagingURIHelper
  393. .createPartName("/docProps/core.xml"),
  394. "application/vnd.openxmlformats-package.core-properties+xml");
  395. expectedValues
  396. .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),
  397. "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");
  398. expectedValues.put(PackagingURIHelper
  399. .createPartName("/word/media/image1.gif"), "image/gif");
  400. expectedValues
  401. .put(PackagingURIHelper.createPartName("/word/settings.xml"),
  402. "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");
  403. expectedValues
  404. .put(PackagingURIHelper.createPartName("/word/styles.xml"),
  405. "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");
  406. expectedValues.put(PackagingURIHelper
  407. .createPartName("/word/theme/theme1.xml"),
  408. "application/vnd.openxmlformats-officedocument.theme+xml");
  409. expectedValues
  410. .put(
  411. PackagingURIHelper
  412. .createPartName("/word/webSettings.xml"),
  413. "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");
  414. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  415. @SuppressWarnings("resource")
  416. OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  417. // Remove the core part
  418. p.deletePart(PackagingURIHelper.createPartName("/word/document.xml"));
  419. for (PackagePart part : p.getParts()) {
  420. values.put(part.getPartName(), part.getContentType());
  421. logger.log(POILogger.DEBUG, part.getPartName());
  422. }
  423. // Compare expected values with values return by the package
  424. for (PackagePartName partName : expectedValues.keySet()) {
  425. assertNotNull(values.get(partName));
  426. assertEquals(expectedValues.get(partName), values.get(partName));
  427. }
  428. // Don't save modifications
  429. p.revert();
  430. }
  431. @Test
  432. public void deletePartRecursive() throws InvalidFormatException {
  433. TreeMap<PackagePartName, String> expectedValues;
  434. TreeMap<PackagePartName, String> values;
  435. values = new TreeMap<>();
  436. // Expected values
  437. expectedValues = new TreeMap<>();
  438. expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
  439. "application/vnd.openxmlformats-package.relationships+xml");
  440. expectedValues
  441. .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
  442. "application/vnd.openxmlformats-officedocument.extended-properties+xml");
  443. expectedValues.put(PackagingURIHelper
  444. .createPartName("/docProps/core.xml"),
  445. "application/vnd.openxmlformats-package.core-properties+xml");
  446. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  447. @SuppressWarnings("resource")
  448. OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  449. // Remove the core part
  450. p.deletePartRecursive(PackagingURIHelper.createPartName("/word/document.xml"));
  451. for (PackagePart part : p.getParts()) {
  452. values.put(part.getPartName(), part.getContentType());
  453. logger.log(POILogger.DEBUG, part.getPartName());
  454. }
  455. // Compare expected values with values return by the package
  456. for (PackagePartName partName : expectedValues.keySet()) {
  457. assertNotNull(values.get(partName));
  458. assertEquals(expectedValues.get(partName), values.get(partName));
  459. }
  460. // Don't save modifications
  461. p.revert();
  462. }
  463. /**
  464. * Test that we can open a file by path, and then
  465. * write changes to it.
  466. */
  467. @Test
  468. public void openFileThenOverwrite() throws IOException, InvalidFormatException {
  469. File tempFile = TempFile.createTempFile("poiTesting","tmp");
  470. File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
  471. FileHelper.copyFile(origFile, tempFile);
  472. // Open the temp file
  473. OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  474. // Close it
  475. p.close();
  476. // Delete it
  477. assertTrue(tempFile.delete());
  478. // Reset
  479. FileHelper.copyFile(origFile, tempFile);
  480. p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  481. // Save it to the same file - not allowed
  482. try {
  483. p.save(tempFile);
  484. fail("You shouldn't be able to call save(File) to overwrite the current file");
  485. } catch(InvalidOperationException e) {
  486. // expected here
  487. }
  488. p.close();
  489. // Delete it
  490. assertTrue(tempFile.delete());
  491. // Open it read only, then close and delete - allowed
  492. FileHelper.copyFile(origFile, tempFile);
  493. p = OPCPackage.open(tempFile.toString(), PackageAccess.READ);
  494. p.close();
  495. assertTrue(tempFile.delete());
  496. }
  497. /**
  498. * Test that we can open a file by path, save it
  499. * to another file, then delete both
  500. */
  501. @Test
  502. public void openFileThenSaveDelete() throws IOException, InvalidFormatException {
  503. File tempFile = TempFile.createTempFile("poiTesting","tmp");
  504. File tempFile2 = TempFile.createTempFile("poiTesting","tmp");
  505. File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
  506. FileHelper.copyFile(origFile, tempFile);
  507. // Open the temp file
  508. OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  509. // Save it to a different file
  510. p.save(tempFile2);
  511. p.close();
  512. // Delete both the files
  513. assertTrue(tempFile.delete());
  514. assertTrue(tempFile2.delete());
  515. }
  516. private static ContentTypeManager getContentTypeManager(OPCPackage pkg) {
  517. return POITestCase.getFieldValue(OPCPackage.class, pkg, ContentTypeManager.class, "contentTypeManager");
  518. }
  519. @Test
  520. public void getPartsByName() throws InvalidFormatException {
  521. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  522. @SuppressWarnings("resource")
  523. OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  524. try {
  525. List<PackagePart> rs = pkg.getPartsByName(Pattern.compile("/word/.*?\\.xml"));
  526. HashMap<String, PackagePart> selected = new HashMap<>();
  527. for(PackagePart p : rs)
  528. selected.put(p.getPartName().getName(), p);
  529. assertEquals(6, selected.size());
  530. assertTrue(selected.containsKey("/word/document.xml"));
  531. assertTrue(selected.containsKey("/word/fontTable.xml"));
  532. assertTrue(selected.containsKey("/word/settings.xml"));
  533. assertTrue(selected.containsKey("/word/styles.xml"));
  534. assertTrue(selected.containsKey("/word/theme/theme1.xml"));
  535. assertTrue(selected.containsKey("/word/webSettings.xml"));
  536. } finally {
  537. // use revert to not re-write the input file
  538. pkg.revert();
  539. }
  540. }
  541. @Test
  542. public void getPartSize() throws IOException, InvalidFormatException {
  543. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  544. try (OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ)) {
  545. int checked = 0;
  546. for (PackagePart part : pkg.getParts()) {
  547. // Can get the size of zip parts
  548. if (part.getPartName().getName().equals("/word/document.xml")) {
  549. checked++;
  550. assertEquals(ZipPackagePart.class, part.getClass());
  551. assertEquals(6031L, part.getSize());
  552. }
  553. if (part.getPartName().getName().equals("/word/fontTable.xml")) {
  554. checked++;
  555. assertEquals(ZipPackagePart.class, part.getClass());
  556. assertEquals(1312L, part.getSize());
  557. }
  558. // But not from the others
  559. if (part.getPartName().getName().equals("/docProps/core.xml")) {
  560. checked++;
  561. assertEquals(PackagePropertiesPart.class, part.getClass());
  562. assertEquals(-1, part.getSize());
  563. }
  564. }
  565. // Ensure we actually found the parts we want to check
  566. assertEquals(3, checked);
  567. }
  568. }
  569. @Test
  570. public void replaceContentType()
  571. throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException {
  572. InputStream is = OpenXML4JTestDataSamples.openSampleStream("sample.xlsx");
  573. @SuppressWarnings("resource")
  574. OPCPackage p = OPCPackage.open(is);
  575. ContentTypeManager mgr = getContentTypeManager(p);
  576. assertTrue(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"));
  577. assertFalse(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml"));
  578. assertTrue(
  579. p.replaceContentType(
  580. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
  581. "application/vnd.ms-excel.sheet.macroEnabled.main+xml")
  582. );
  583. assertFalse(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"));
  584. assertTrue(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml"));
  585. p.revert();
  586. is.close();
  587. }
  588. /**
  589. * Verify we give helpful exceptions (or as best we can) when
  590. * supplied with non-OOXML file types (eg OLE2, ODF)
  591. */
  592. @Test
  593. public void NonOOXMLFileTypes() throws Exception {
  594. // Spreadsheet has a good mix of alternate file types
  595. POIDataSamples files = POIDataSamples.getSpreadSheetInstance();
  596. // OLE2 - Stream
  597. try {
  598. try (InputStream stream = files.openResourceAsStream("SampleSS.xls")) {
  599. OPCPackage.open(stream);
  600. }
  601. fail("Shouldn't be able to open OLE2");
  602. } catch (OLE2NotOfficeXmlFileException e) {
  603. assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format"));
  604. assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML"));
  605. }
  606. // OLE2 - File
  607. try {
  608. OPCPackage.open(files.getFile("SampleSS.xls"));
  609. fail("Shouldn't be able to open OLE2");
  610. } catch (OLE2NotOfficeXmlFileException e) {
  611. assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format"));
  612. assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML"));
  613. }
  614. // Raw XML - Stream
  615. try {
  616. try (InputStream stream = files.openResourceAsStream("SampleSS.xml")) {
  617. OPCPackage.open(stream);
  618. }
  619. fail("Shouldn't be able to open XML");
  620. } catch (NotOfficeXmlFileException e) {
  621. assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file"));
  622. assertTrue(e.getMessage().contains("Formats such as Office 2003 XML"));
  623. }
  624. // Raw XML - File
  625. try {
  626. OPCPackage.open(files.getFile("SampleSS.xml"));
  627. fail("Shouldn't be able to open XML");
  628. } catch (NotOfficeXmlFileException e) {
  629. assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file"));
  630. assertTrue(e.getMessage().contains("Formats such as Office 2003 XML"));
  631. }
  632. // ODF / ODS - Stream
  633. try {
  634. try (InputStream stream = files.openResourceAsStream("SampleSS.ods")) {
  635. OPCPackage.open(stream);
  636. }
  637. fail("Shouldn't be able to open ODS");
  638. } catch (ODFNotOfficeXmlFileException e) {
  639. assertTrue(e.toString().contains("The supplied data appears to be in ODF"));
  640. assertTrue(e.toString().contains("Formats like these (eg ODS"));
  641. }
  642. // ODF / ODS - File
  643. try {
  644. OPCPackage.open(files.getFile("SampleSS.ods"));
  645. fail("Shouldn't be able to open ODS");
  646. } catch (ODFNotOfficeXmlFileException e) {
  647. assertTrue(e.toString().contains("The supplied data appears to be in ODF"));
  648. assertTrue(e.toString().contains("Formats like these (eg ODS"));
  649. }
  650. // Plain Text - Stream
  651. try {
  652. try (InputStream stream = files.openResourceAsStream("SampleSS.txt")) {
  653. OPCPackage.open(stream);
  654. }
  655. fail("Shouldn't be able to open Plain Text");
  656. } catch (NotOfficeXmlFileException e) {
  657. assertTrue(e.getMessage().contains("No valid entries or contents found"));
  658. assertTrue(e.getMessage().contains("not a valid OOXML"));
  659. }
  660. // Plain Text - File
  661. try {
  662. OPCPackage.open(files.getFile("SampleSS.txt"));
  663. fail("Shouldn't be able to open Plain Text");
  664. } catch (UnsupportedFileFormatException e) {
  665. // Unhelpful low-level error, sorry
  666. }
  667. }
  668. /**
  669. * Zip bomb handling test
  670. *
  671. * see bug #50090 / #56865
  672. */
  673. @Test
  674. public void zipBombCreateAndHandle()
  675. throws IOException, EncryptedDocumentException {
  676. ByteArrayOutputStream bos = new ByteArrayOutputStream(2500000);
  677. try (ZipFile zipFile = ZipHelper.openZipFile(OpenXML4JTestDataSamples.getSampleFile("sample.xlsx"));
  678. ZipArchiveOutputStream append = new ZipArchiveOutputStream(bos)) {
  679. assertNotNull(zipFile);
  680. // first, copy contents from existing war
  681. Enumeration<? extends ZipArchiveEntry> entries = zipFile.getEntries();
  682. while (entries.hasMoreElements()) {
  683. final ZipArchiveEntry eIn = entries.nextElement();
  684. final ZipArchiveEntry eOut = new ZipArchiveEntry(eIn.getName());
  685. eOut.setTime(eIn.getTime());
  686. eOut.setComment(eIn.getComment());
  687. eOut.setSize(eIn.getSize());
  688. append.putArchiveEntry(eOut);
  689. if (!eOut.isDirectory()) {
  690. try (InputStream is = zipFile.getInputStream(eIn)) {
  691. if (eOut.getName().equals("[Content_Types].xml")) {
  692. ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
  693. IOUtils.copy(is, bos2);
  694. long size = bos2.size() - "</Types>".length();
  695. append.write(bos2.toByteArray(), 0, (int) size);
  696. byte[] spam = new byte[0x7FFF];
  697. Arrays.fill(spam, (byte) ' ');
  698. // 0x7FFF0000 is the maximum for 32-bit zips, but less still works
  699. while (size < 0x7FFF00) {
  700. append.write(spam);
  701. size += spam.length;
  702. }
  703. append.write("</Types>".getBytes("UTF-8"));
  704. size += 8;
  705. eOut.setSize(size);
  706. } else {
  707. IOUtils.copy(is, append);
  708. }
  709. }
  710. }
  711. append.closeArchiveEntry();
  712. }
  713. }
  714. expectedEx.expect(IOException.class);
  715. expectedEx.expectMessage("Zip bomb detected!");
  716. try (Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(bos.toByteArray()))) {
  717. wb.getSheetAt(0);
  718. }
  719. }
  720. @Test
  721. public void testZipEntityExpansionTerminates() throws IOException, OpenXML4JException, XmlException {
  722. expectedEx.expect(IllegalStateException.class);
  723. expectedEx.expectMessage("The text would exceed the max allowed overall size of extracted text.");
  724. openXmlBombFile("poc-shared-strings.xlsx");
  725. }
  726. @Test
  727. public void testZipEntityExpansionSharedStringTableEvents() throws IOException, OpenXML4JException, XmlException {
  728. boolean before = ExtractorFactory.getThreadPrefersEventExtractors();
  729. ExtractorFactory.setThreadPrefersEventExtractors(true);
  730. try {
  731. expectedEx.expect(IllegalStateException.class);
  732. expectedEx.expectMessage("The text would exceed the max allowed overall size of extracted text.");
  733. openXmlBombFile("poc-shared-strings.xlsx");
  734. } finally {
  735. ExtractorFactory.setThreadPrefersEventExtractors(before);
  736. }
  737. }
  738. @Test
  739. public void testZipEntityExpansionExceedsMemory() throws IOException, OpenXML4JException, XmlException {
  740. expectedEx.expect(POIXMLException.class);
  741. expectedEx.expectMessage("unable to parse shared strings table");
  742. expectedEx.expectCause(getCauseMatcher(SAXParseException.class, "The parser has encountered more than"));
  743. openXmlBombFile("poc-xmlbomb.xlsx");
  744. }
  745. @Test
  746. public void testZipEntityExpansionExceedsMemory2() throws IOException, OpenXML4JException, XmlException {
  747. expectedEx.expect(POIXMLException.class);
  748. expectedEx.expectMessage("unable to parse shared strings table");
  749. expectedEx.expectCause(getCauseMatcher(SAXParseException.class, "The parser has encountered more than"));
  750. openXmlBombFile("poc-xmlbomb-empty.xlsx");
  751. }
  752. private void openXmlBombFile(String file) throws IOException, OpenXML4JException, XmlException {
  753. final double minInf = ZipSecureFile.getMinInflateRatio();
  754. ZipSecureFile.setMinInflateRatio(0.002);
  755. try (POITextExtractor extractor = ExtractorFactory.createExtractor(XSSFTestDataSamples.getSampleFile(file))) {
  756. assertNotNull(extractor);
  757. extractor.getText();
  758. } finally {
  759. ZipSecureFile.setMinInflateRatio(minInf);
  760. }
  761. }
  762. @Test
  763. public void zipBombCheckSizesWithinLimits() throws IOException, EncryptedDocumentException {
  764. getZipStatsAndConsume((max_size, min_ratio) -> {
  765. // use values close to, but within the limits
  766. ZipSecureFile.setMinInflateRatio(min_ratio - 0.002);
  767. assertEquals(min_ratio - 0.002, ZipSecureFile.getMinInflateRatio(), 0.00001);
  768. ZipSecureFile.setMaxEntrySize(max_size + 1);
  769. assertEquals(max_size + 1, ZipSecureFile.getMaxEntrySize());
  770. });
  771. }
  772. @Test
  773. public void zipBombCheckSizesRatioTooSmall() throws IOException, EncryptedDocumentException {
  774. expectedEx.expect(POIXMLException.class);
  775. expectedEx.expectMessage("You can adjust this limit via ZipSecureFile.setMinInflateRatio()");
  776. getZipStatsAndConsume((max_size, min_ratio) -> {
  777. // check ratio out of bounds
  778. ZipSecureFile.setMinInflateRatio(min_ratio+0.002);
  779. });
  780. }
  781. @Test
  782. public void zipBombCheckSizesSizeTooBig() throws IOException, EncryptedDocumentException {
  783. expectedEx.expect(POIXMLException.class);
  784. expectedEx.expectMessage("You can adjust this limit via ZipSecureFile.setMaxEntrySize()");
  785. getZipStatsAndConsume((max_size, min_ratio) -> {
  786. // check max entry size ouf of bounds
  787. ZipSecureFile.setMinInflateRatio(min_ratio-0.002);
  788. ZipSecureFile.setMaxEntrySize(max_size-200);
  789. });
  790. }
  791. private void getZipStatsAndConsume(BiConsumer<Long,Double> ratioCon) throws IOException {
  792. // use a test file with a xml file bigger than 100k (ZipArchiveThresholdInputStream.GRACE_ENTRY_SIZE)
  793. final File file = XSSFTestDataSamples.getSampleFile("poc-shared-strings.xlsx");
  794. double min_ratio = Double.MAX_VALUE;
  795. long max_size = 0;
  796. try (ZipFile zf = ZipHelper.openZipFile(file)) {
  797. assertNotNull(zf);
  798. Enumeration<? extends ZipArchiveEntry> entries = zf.getEntries();
  799. while (entries.hasMoreElements()) {
  800. ZipArchiveEntry ze = entries.nextElement();
  801. if (ze.getSize() == 0) {
  802. continue;
  803. }
  804. // add zip entry header ~ 128 bytes
  805. long size = ze.getSize()+128;
  806. double ratio = ze.getCompressedSize() / (double)size;
  807. min_ratio = Math.min(min_ratio, ratio);
  808. max_size = Math.max(max_size, size);
  809. }
  810. }
  811. ratioCon.accept(max_size, min_ratio);
  812. //noinspection EmptyTryBlock,unused
  813. try (Workbook wb = WorkbookFactory.create(file, null, true)) {
  814. } finally {
  815. // reset otherwise a lot of ooxml tests will fail
  816. ZipSecureFile.setMinInflateRatio(0.01d);
  817. ZipSecureFile.setMaxEntrySize(0xFFFFFFFFL);
  818. }
  819. }
  820. @Test
  821. public void testConstructors() throws IOException {
  822. // verify the various ways to construct a ZipSecureFile
  823. File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx");
  824. ZipSecureFile zipFile = new ZipSecureFile(file);
  825. assertNotNull(zipFile.getName());
  826. zipFile.close();
  827. zipFile = new ZipSecureFile(file.getAbsolutePath());
  828. assertNotNull(zipFile.getName());
  829. zipFile.close();
  830. }
  831. @Test
  832. public void testMaxTextSize() {
  833. long before = ZipSecureFile.getMaxTextSize();
  834. try {
  835. ZipSecureFile.setMaxTextSize(12345);
  836. assertEquals(12345, ZipSecureFile.getMaxTextSize());
  837. } finally {
  838. ZipSecureFile.setMaxTextSize(before);
  839. }
  840. }
  841. // bug 60128
  842. @Test(expected=NotOfficeXmlFileException.class)
  843. public void testCorruptFile() throws InvalidFormatException {
  844. File file = OpenXML4JTestDataSamples.getSampleFile("invalid.xlsx");
  845. OPCPackage.open(file, PackageAccess.READ);
  846. }
  847. // bug 61381
  848. @Test
  849. public void testTooShortFilterStreams() throws IOException {
  850. File xssf = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx");
  851. File hssf = POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xls");
  852. InputStream[] isList = {
  853. new PushbackInputStream(new FileInputStream(xssf), 2),
  854. new BufferedInputStream(new FileInputStream(xssf), 2),
  855. new PushbackInputStream(new FileInputStream(hssf), 2),
  856. new BufferedInputStream(new FileInputStream(hssf), 2),
  857. };
  858. try {
  859. for (InputStream is : isList) {
  860. WorkbookFactory.create(is).close();
  861. }
  862. } finally {
  863. for (InputStream is : isList) {
  864. IOUtils.closeQuietly(is);
  865. }
  866. }
  867. }
  868. @Test
  869. public void testBug56479() throws Exception {
  870. InputStream is = OpenXML4JTestDataSamples.openSampleStream("dcterms_bug_56479.zip");
  871. OPCPackage p = OPCPackage.open(is);
  872. // Check we found the contents of it
  873. boolean foundCoreProps = false, foundDocument = false, foundTheme1 = false;
  874. for (final PackagePart part : p.getParts()) {
  875. final String partName = part.getPartName().toString();
  876. final String contentType = part.getContentType();
  877. if ("/docProps/core.xml".equals(partName)) {
  878. assertEquals(ContentTypes.CORE_PROPERTIES_PART, contentType);
  879. foundCoreProps = true;
  880. }
  881. if ("/word/document.xml".equals(partName)) {
  882. assertEquals(XWPFRelation.DOCUMENT.getContentType(), contentType);
  883. foundDocument = true;
  884. }
  885. if ("/word/theme/theme1.xml".equals(partName)) {
  886. assertEquals(XWPFRelation.THEME.getContentType(), contentType);
  887. foundTheme1 = true;
  888. }
  889. }
  890. assertTrue("Core not found in " + p.getParts(), foundCoreProps);
  891. assertFalse("Document should not be found in " + p.getParts(), foundDocument);
  892. assertFalse("Theme1 should not found in " + p.getParts(), foundTheme1);
  893. p.close();
  894. is.close();
  895. }
  896. @Test
  897. public void unparseableCentralDirectory() throws IOException {
  898. File f = OpenXML4JTestDataSamples.getSampleFile("at.pzp.www_uploads_media_PP_Scheinecker-jdk6error.pptx");
  899. SlideShow<?,?> ppt = SlideShowFactory.create(f, null, true);
  900. ppt.close();
  901. }
  902. @Test
  903. public void testClosingStreamOnException() throws IOException {
  904. InputStream is = OpenXML4JTestDataSamples.openSampleStream("dcterms_bug_56479.zip");
  905. File tmp = File.createTempFile("poi-test-truncated-zip", "");
  906. // create a corrupted zip file by truncating a valid zip file to the first 100 bytes
  907. OutputStream os = new FileOutputStream(tmp);
  908. for (int i = 0; i < 100; i++) {
  909. os.write(is.read());
  910. }
  911. os.flush();
  912. os.close();
  913. is.close();
  914. // feed the corrupted zip file to OPCPackage
  915. try {
  916. OPCPackage.open(tmp, PackageAccess.READ);
  917. } catch (Exception e) {
  918. // expected: the zip file is invalid
  919. // this test does not care if open() throws an exception or not.
  920. }
  921. // If the stream is not closed on exception, it will keep a file descriptor to tmp,
  922. // and requests to the OS to delete the file will fail.
  923. assertTrue("Can't delete tmp file", tmp.delete());
  924. }
  925. /**
  926. * If ZipPackage is passed an invalid file, a call to close
  927. * (eg from the OPCPackage open method) should tidy up the
  928. * stream / file the broken file is being read from.
  929. * See bug #60128 for more
  930. */
  931. @Test(expected = NotOfficeXmlFileException.class)
  932. public void testTidyStreamOnInvalidFile1() throws Exception {
  933. openInvalidFile("SampleSS.ods", false);
  934. }
  935. @Test(expected = NotOfficeXmlFileException.class)
  936. public void testTidyStreamOnInvalidFile2() throws Exception {
  937. openInvalidFile("SampleSS.ods", true);
  938. }
  939. @Test(expected = NotOfficeXmlFileException.class)
  940. public void testTidyStreamOnInvalidFile3() throws Exception {
  941. openInvalidFile("SampleSS.txt", false);
  942. }
  943. @Test(expected = NotOfficeXmlFileException.class)
  944. public void testTidyStreamOnInvalidFile4() throws Exception {
  945. openInvalidFile("SampleSS.txt", true);
  946. }
  947. @Test(expected = InvalidFormatException.class)
  948. public void testBug62592() throws Exception {
  949. InputStream is = OpenXML4JTestDataSamples.openSampleStream("62592.thmx");
  950. /*OPCPackage p =*/ OPCPackage.open(is);
  951. }
  952. @Test
  953. public void testBug62592SequentialCallsToGetParts() throws Exception {
  954. //make absolutely certain that sequential calls don't throw InvalidFormatExceptions
  955. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  956. OPCPackage p2 = OPCPackage.open(originalFile, PackageAccess.READ);
  957. p2.getParts();
  958. p2.getParts();
  959. }
  960. @Test
  961. public void testDoNotCloseStream() throws IOException {
  962. // up to JDK 10 we did use Mockito here, but OutputStream is
  963. // an abstract class and fails mocking with some changes in JDK 11
  964. // so we use a simple empty output stream implementation instead
  965. OutputStream os = new OutputStream() {
  966. @Override
  967. public void write(int b) {
  968. }
  969. @Override
  970. public void close() {
  971. throw new IllegalStateException("close should not be called here");
  972. }
  973. };
  974. try (XSSFWorkbook wb = new XSSFWorkbook()) {
  975. wb.createSheet();
  976. wb.write(os);
  977. }
  978. try (SXSSFWorkbook wb = new SXSSFWorkbook()) {
  979. wb.createSheet();
  980. wb.write(os);
  981. }
  982. }
  983. private static void openInvalidFile(final String name, final boolean useStream) throws IOException, InvalidFormatException {
  984. // Spreadsheet has a good mix of alternate file types
  985. final POIDataSamples files = POIDataSamples.getSpreadSheetInstance();
  986. ZipPackage pkgTest = null;
  987. try (final InputStream is = (useStream) ? files.openResourceAsStream(name) : null) {
  988. try (final ZipPackage pkg = (useStream) ? new ZipPackage(is, PackageAccess.READ) : new ZipPackage(files.getFile(name), PackageAccess.READ)) {
  989. pkgTest = pkg;
  990. assertNotNull(pkg.getZipArchive());
  991. assertFalse(pkg.getZipArchive().isClosed());
  992. pkg.getParts();
  993. fail("Shouldn't work");
  994. }
  995. } finally {
  996. if (pkgTest != null) {
  997. assertNotNull(pkgTest.getZipArchive());
  998. assertTrue(pkgTest.getZipArchive().isClosed());
  999. }
  1000. }
  1001. }
  1002. @SuppressWarnings("SameParameterValue")
  1003. private static <T extends Throwable> AnyCauseMatcher<T> getCauseMatcher(Class<T> cause, String message) {
  1004. // junit is only using hamcrest-core, so instead of adding hamcrest-beans, we provide the throwable
  1005. // search with the basics...
  1006. // see https://stackoverflow.com/a/47703937/2066598
  1007. return new AnyCauseMatcher<>(cause, message);
  1008. }
  1009. private static class AnyCauseMatcher<T extends Throwable> extends TypeSafeMatcher<T> {
  1010. private final Class<T> expectedType;
  1011. private final String expectedMessage;
  1012. AnyCauseMatcher(Class<T> expectedType, String expectedMessage) {
  1013. this.expectedType = expectedType;
  1014. this.expectedMessage = expectedMessage;
  1015. }
  1016. @Override
  1017. protected boolean matchesSafely(final Throwable root) {
  1018. for (Throwable t = root; t != null; t = t.getCause()) {
  1019. if (t.getClass().isAssignableFrom(expectedType) && t.getMessage().contains(expectedMessage)) {
  1020. return true;
  1021. }
  1022. }
  1023. return false;
  1024. }
  1025. @Override
  1026. public void describeTo(Description description) {
  1027. description.appendText("expects type ")
  1028. .appendValue(expectedType)
  1029. .appendText(" and a message ")
  1030. .appendValue(expectedMessage);
  1031. }
  1032. }
  1033. @Test
  1034. public void testBug63029() throws Exception {
  1035. File testFile = OpenXML4JTestDataSamples.getSampleFile("sample.docx");
  1036. File tmpFile = OpenXML4JTestDataSamples.getOutputFile("Bug63029.docx");
  1037. Files.copy(testFile, tmpFile);
  1038. int numPartsBefore = 0;
  1039. String md5Before = Files.hash(tmpFile, Hashing.md5()).toString();
  1040. RuntimeException ex = null;
  1041. try(OPCPackage pkg = OPCPackage.open(tmpFile, PackageAccess.READ_WRITE))
  1042. {
  1043. numPartsBefore = pkg.getParts().size();
  1044. // add a marshaller that will throw an exception on save
  1045. pkg.addMarshaller("poi/junit", (part, out) -> {
  1046. throw new RuntimeException("Bugzilla 63029");
  1047. });
  1048. pkg.createPart(PackagingURIHelper.createPartName("/poi/test.xml"), "poi/junit");
  1049. } catch (RuntimeException e){
  1050. ex = e;
  1051. }
  1052. // verify there was an exception while closing the file
  1053. assertEquals("Fail to save: an error occurs while saving the package : Bugzilla 63029", ex.getMessage());
  1054. // assert that md5 after closing is the same, i.e. the source is left intact
  1055. String md5After = Files.hash(tmpFile, Hashing.md5()).toString();
  1056. assertEquals(md5Before, md5After);
  1057. // try to read the source file once again
  1058. try ( OPCPackage pkg = OPCPackage.open(tmpFile, PackageAccess.READ_WRITE)){
  1059. // the source is still a valid zip archive.
  1060. // prior to the fix this used to throw NotOfficeXmlFileException("archive is not a ZIP archive")
  1061. // assert that the number of parts remained the same
  1062. assertEquals(pkg.getParts().size(), numPartsBefore);
  1063. }
  1064. }
  1065. }