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 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  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 static org.junit.Assert.assertEquals;
  17. import static org.junit.Assert.assertFalse;
  18. import static org.junit.Assert.assertNotNull;
  19. import static org.junit.Assert.assertNull;
  20. import static org.junit.Assert.assertTrue;
  21. import static org.junit.Assert.fail;
  22. import java.io.ByteArrayInputStream;
  23. import java.io.ByteArrayOutputStream;
  24. import java.io.File;
  25. import java.io.FileInputStream;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.lang.reflect.InvocationTargetException;
  31. import java.net.URI;
  32. import java.net.URISyntaxException;
  33. import java.util.Enumeration;
  34. import java.util.HashMap;
  35. import java.util.List;
  36. import java.util.TreeMap;
  37. import java.util.regex.Pattern;
  38. import java.util.zip.ZipEntry;
  39. import java.util.zip.ZipFile;
  40. import java.util.zip.ZipOutputStream;
  41. import org.apache.poi.EncryptedDocumentException;
  42. import org.apache.poi.POIDataSamples;
  43. import org.apache.poi.POITestCase;
  44. import org.apache.poi.POIXMLException;
  45. import org.apache.poi.UnsupportedFileFormatException;
  46. import org.apache.poi.openxml4j.OpenXML4JTestDataSamples;
  47. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  48. import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
  49. import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
  50. import org.apache.poi.openxml4j.exceptions.ODFNotOfficeXmlFileException;
  51. import org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException;
  52. import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
  53. import org.apache.poi.openxml4j.opc.internal.FileHelper;
  54. import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
  55. import org.apache.poi.openxml4j.opc.internal.ZipHelper;
  56. import org.apache.poi.openxml4j.util.ZipSecureFile;
  57. import org.apache.poi.ss.usermodel.Workbook;
  58. import org.apache.poi.ss.usermodel.WorkbookFactory;
  59. import org.apache.poi.util.DocumentHelper;
  60. import org.apache.poi.util.IOUtils;
  61. import org.apache.poi.util.POILogFactory;
  62. import org.apache.poi.util.POILogger;
  63. import org.apache.poi.util.TempFile;
  64. import org.junit.Ignore;
  65. import org.junit.Test;
  66. import org.w3c.dom.Document;
  67. import org.w3c.dom.Element;
  68. import org.w3c.dom.NodeList;
  69. import org.xml.sax.SAXException;
  70. public final class TestPackage {
  71. private static final POILogger logger = POILogFactory.getLogger(TestPackage.class);
  72. /**
  73. * Test that just opening and closing the file doesn't alter the document.
  74. */
  75. @Test
  76. public void openSave() throws IOException, InvalidFormatException {
  77. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  78. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
  79. @SuppressWarnings("resource")
  80. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  81. try {
  82. p.save(targetFile.getAbsoluteFile());
  83. // Compare the original and newly saved document
  84. assertTrue(targetFile.exists());
  85. ZipFileAssert.assertEquals(new File(originalFile), targetFile);
  86. assertTrue(targetFile.delete());
  87. } finally {
  88. // use revert to not re-write the input file
  89. p.revert();
  90. }
  91. }
  92. /**
  93. * Test that when we create a new Package, we give it
  94. * the correct default content types
  95. * @throws IllegalAccessException
  96. * @throws NoSuchFieldException
  97. * @throws IllegalArgumentException
  98. * @throws SecurityException
  99. */
  100. @Test
  101. public void createGetsContentTypes()
  102. throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
  103. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
  104. // Zap the target file, in case of an earlier run
  105. if(targetFile.exists()) {
  106. assertTrue(targetFile.delete());
  107. }
  108. @SuppressWarnings("resource")
  109. OPCPackage pkg = OPCPackage.create(targetFile);
  110. // Check it has content types for rels and xml
  111. ContentTypeManager ctm = getContentTypeManager(pkg);
  112. assertEquals(
  113. "application/xml",
  114. ctm.getContentType(
  115. PackagingURIHelper.createPartName("/foo.xml")
  116. )
  117. );
  118. assertEquals(
  119. ContentTypes.RELATIONSHIPS_PART,
  120. ctm.getContentType(
  121. PackagingURIHelper.createPartName("/foo.rels")
  122. )
  123. );
  124. assertNull(
  125. ctm.getContentType(
  126. PackagingURIHelper.createPartName("/foo.txt")
  127. )
  128. );
  129. pkg.revert();
  130. }
  131. /**
  132. * Test package creation.
  133. */
  134. @Test
  135. public void createPackageAddPart() throws IOException, InvalidFormatException {
  136. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
  137. File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestCreatePackageOUTPUT.docx");
  138. // Zap the target file, in case of an earlier run
  139. if(targetFile.exists()) {
  140. assertTrue(targetFile.delete());
  141. }
  142. // Create a package
  143. OPCPackage pkg = OPCPackage.create(targetFile);
  144. PackagePartName corePartName = PackagingURIHelper
  145. .createPartName("/word/document.xml");
  146. pkg.addRelationship(corePartName, TargetMode.INTERNAL,
  147. PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
  148. PackagePart corePart = pkg
  149. .createPart(
  150. corePartName,
  151. "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
  152. Document doc = DocumentHelper.createDocument();
  153. Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document");
  154. doc.appendChild(elDocument);
  155. Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body");
  156. elDocument.appendChild(elBody);
  157. Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p");
  158. elBody.appendChild(elParagraph);
  159. Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r");
  160. elParagraph.appendChild(elRun);
  161. Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t");
  162. elRun.appendChild(elText);
  163. elText.setTextContent("Hello Open XML !");
  164. StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
  165. pkg.close();
  166. ZipFileAssert.assertEquals(expectedFile, targetFile);
  167. assertTrue(targetFile.delete());
  168. }
  169. /**
  170. * Tests that we can create a new package, add a core
  171. * document and another part, save and re-load and
  172. * have everything setup as expected
  173. * @throws SAXException
  174. */
  175. @Test
  176. public void createPackageWithCoreDocument() throws IOException, InvalidFormatException, URISyntaxException, SAXException {
  177. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  178. OPCPackage pkg = OPCPackage.create(baos);
  179. // Add a core document
  180. PackagePartName corePartName = PackagingURIHelper.createPartName("/xl/workbook.xml");
  181. // Create main part relationship
  182. pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
  183. // Create main document part
  184. PackagePart corePart = pkg.createPart(corePartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
  185. // Put in some dummy content
  186. OutputStream coreOut = corePart.getOutputStream();
  187. coreOut.write("<dummy-xml />".getBytes("UTF-8"));
  188. coreOut.close();
  189. // And another bit
  190. PackagePartName sheetPartName = PackagingURIHelper.createPartName("/xl/worksheets/sheet1.xml");
  191. PackageRelationship rel =
  192. corePart.addRelationship(sheetPartName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "rSheet1");
  193. assertNotNull(rel);
  194. PackagePart part = pkg.createPart(sheetPartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
  195. assertNotNull(part);
  196. // Dummy content again
  197. coreOut = corePart.getOutputStream();
  198. coreOut.write("<dummy-xml2 />".getBytes("UTF-8"));
  199. coreOut.close();
  200. //add a relationship with internal target: "#Sheet1!A1"
  201. corePart.addRelationship(new URI("#Sheet1!A1"), TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "rId2");
  202. // Check things are as expected
  203. PackageRelationshipCollection coreRels =
  204. pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
  205. assertEquals(1, coreRels.size());
  206. PackageRelationship coreRel = coreRels.getRelationship(0);
  207. assertNotNull(coreRel);
  208. assertEquals("/", coreRel.getSourceURI().toString());
  209. assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
  210. assertNotNull(pkg.getPart(coreRel));
  211. // Save and re-load
  212. pkg.close();
  213. File tmp = TempFile.createTempFile("testCreatePackageWithCoreDocument", ".zip");
  214. OutputStream fout = new FileOutputStream(tmp);
  215. try {
  216. fout.write(baos.toByteArray());
  217. } finally {
  218. fout.close();
  219. }
  220. pkg = OPCPackage.open(tmp.getPath());
  221. //tmp.delete();
  222. try {
  223. // Check still right
  224. coreRels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
  225. assertEquals(1, coreRels.size());
  226. coreRel = coreRels.getRelationship(0);
  227. assertNotNull(coreRel);
  228. assertEquals("/", coreRel.getSourceURI().toString());
  229. assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
  230. corePart = pkg.getPart(coreRel);
  231. assertNotNull(corePart);
  232. PackageRelationshipCollection rels = corePart.getRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
  233. assertEquals(1, rels.size());
  234. rel = rels.getRelationship(0);
  235. assertNotNull(rel);
  236. assertEquals("Sheet1!A1", rel.getTargetURI().getRawFragment());
  237. assertMSCompatibility(pkg);
  238. } finally {
  239. pkg.close();
  240. }
  241. }
  242. private void assertMSCompatibility(OPCPackage pkg) throws IOException, InvalidFormatException, SAXException {
  243. PackagePartName relName = PackagingURIHelper.createPartName(PackageRelationship.getContainerPartRelationship());
  244. PackagePart relPart = pkg.getPart(relName);
  245. Document xmlRelationshipsDoc = DocumentHelper.readDocument(relPart.getInputStream());
  246. Element root = xmlRelationshipsDoc.getDocumentElement();
  247. NodeList nodeList = root.getElementsByTagName(PackageRelationship.RELATIONSHIP_TAG_NAME);
  248. int nodeCount = nodeList.getLength();
  249. for (int i = 0; i < nodeCount; i++) {
  250. Element element = (Element) nodeList.item(i);
  251. String value = element.getAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME);
  252. assertTrue("Root target must not start with a leading slash ('/'): " + value, value.charAt(0) != '/');
  253. }
  254. }
  255. /**
  256. * Test package opening.
  257. */
  258. @Test
  259. public void openPackage() throws IOException, InvalidFormatException {
  260. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestOpenPackageTMP.docx");
  261. File inputFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageINPUT.docx");
  262. File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageOUTPUT.docx");
  263. // Copy the input file in the output directory
  264. FileHelper.copyFile(inputFile, targetFile);
  265. // Create a package
  266. OPCPackage pkg = OPCPackage.open(targetFile.getAbsolutePath());
  267. // Modify core part
  268. PackagePartName corePartName = PackagingURIHelper
  269. .createPartName("/word/document.xml");
  270. PackagePart corePart = pkg.getPart(corePartName);
  271. // Delete some part to have a valid document
  272. for (PackageRelationship rel : corePart.getRelationships()) {
  273. corePart.removeRelationship(rel.getId());
  274. pkg.removePart(PackagingURIHelper.createPartName(PackagingURIHelper
  275. .resolvePartUri(corePart.getPartName().getURI(), rel
  276. .getTargetURI())));
  277. }
  278. // Create a content
  279. Document doc = DocumentHelper.createDocument();
  280. Element elDocument = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:document");
  281. doc.appendChild(elDocument);
  282. Element elBody = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:body");
  283. elDocument.appendChild(elBody);
  284. Element elParagraph = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:p");
  285. elBody.appendChild(elParagraph);
  286. Element elRun = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r");
  287. elParagraph.appendChild(elRun);
  288. Element elText = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t");
  289. elRun.appendChild(elText);
  290. elText.setTextContent("Hello Open XML !");
  291. StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
  292. // Save and close
  293. try {
  294. pkg.close();
  295. } catch (IOException e) {
  296. fail();
  297. }
  298. ZipFileAssert.assertEquals(expectedFile, targetFile);
  299. assertTrue(targetFile.delete());
  300. }
  301. /**
  302. * Checks that we can write a package to a simple
  303. * OutputStream, in addition to the normal writing
  304. * to a file
  305. */
  306. @Test
  307. public void saveToOutputStream() throws IOException, InvalidFormatException {
  308. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  309. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
  310. @SuppressWarnings("resource")
  311. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  312. try {
  313. FileOutputStream fout = new FileOutputStream(targetFile);
  314. try {
  315. p.save(fout);
  316. } finally {
  317. fout.close();
  318. }
  319. // Compare the original and newly saved document
  320. assertTrue(targetFile.exists());
  321. ZipFileAssert.assertEquals(new File(originalFile), targetFile);
  322. assertTrue(targetFile.delete());
  323. } finally {
  324. // use revert to not re-write the input file
  325. p.revert();
  326. }
  327. }
  328. /**
  329. * Checks that we can open+read a package from a
  330. * simple InputStream, in addition to the normal
  331. * reading from a file
  332. */
  333. @Test
  334. public void openFromInputStream() throws IOException, InvalidFormatException {
  335. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  336. FileInputStream finp = new FileInputStream(originalFile);
  337. @SuppressWarnings("resource")
  338. OPCPackage p = OPCPackage.open(finp);
  339. assertNotNull(p);
  340. assertNotNull(p.getRelationships());
  341. assertEquals(12, p.getParts().size());
  342. // Check it has the usual bits
  343. assertTrue(p.hasRelationships());
  344. assertTrue(p.containPart(PackagingURIHelper.createPartName("/_rels/.rels")));
  345. p.revert();
  346. finp.close();
  347. }
  348. /**
  349. * TODO: fix and enable
  350. * @throws URISyntaxException
  351. */
  352. @Test
  353. @Ignore
  354. public void removePartRecursive() throws IOException, InvalidFormatException, URISyntaxException {
  355. String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
  356. File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveOUTPUT.docx");
  357. File tempFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveTMP.docx");
  358. @SuppressWarnings("resource")
  359. OPCPackage p = OPCPackage.open(originalFile, PackageAccess.READ_WRITE);
  360. p.removePartRecursive(PackagingURIHelper.createPartName(new URI(
  361. "/word/document.xml")));
  362. p.save(tempFile.getAbsoluteFile());
  363. // Compare the original and newly saved document
  364. assertTrue(targetFile.exists());
  365. ZipFileAssert.assertEquals(targetFile, tempFile);
  366. assertTrue(targetFile.delete());
  367. p.revert();
  368. }
  369. @Test
  370. public void deletePart() throws InvalidFormatException {
  371. TreeMap<PackagePartName, String> expectedValues;
  372. TreeMap<PackagePartName, String> values;
  373. values = new TreeMap<PackagePartName, String>();
  374. // Expected values
  375. expectedValues = new TreeMap<PackagePartName, String>();
  376. expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
  377. "application/vnd.openxmlformats-package.relationships+xml");
  378. expectedValues
  379. .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
  380. "application/vnd.openxmlformats-officedocument.extended-properties+xml");
  381. expectedValues.put(PackagingURIHelper
  382. .createPartName("/docProps/core.xml"),
  383. "application/vnd.openxmlformats-package.core-properties+xml");
  384. expectedValues
  385. .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),
  386. "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");
  387. expectedValues.put(PackagingURIHelper
  388. .createPartName("/word/media/image1.gif"), "image/gif");
  389. expectedValues
  390. .put(PackagingURIHelper.createPartName("/word/settings.xml"),
  391. "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");
  392. expectedValues
  393. .put(PackagingURIHelper.createPartName("/word/styles.xml"),
  394. "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");
  395. expectedValues.put(PackagingURIHelper
  396. .createPartName("/word/theme/theme1.xml"),
  397. "application/vnd.openxmlformats-officedocument.theme+xml");
  398. expectedValues
  399. .put(
  400. PackagingURIHelper
  401. .createPartName("/word/webSettings.xml"),
  402. "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");
  403. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  404. @SuppressWarnings("resource")
  405. OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  406. // Remove the core part
  407. p.deletePart(PackagingURIHelper.createPartName("/word/document.xml"));
  408. for (PackagePart part : p.getParts()) {
  409. values.put(part.getPartName(), part.getContentType());
  410. logger.log(POILogger.DEBUG, part.getPartName());
  411. }
  412. // Compare expected values with values return by the package
  413. for (PackagePartName partName : expectedValues.keySet()) {
  414. assertNotNull(values.get(partName));
  415. assertEquals(expectedValues.get(partName), values.get(partName));
  416. }
  417. // Don't save modifications
  418. p.revert();
  419. }
  420. @Test
  421. public void deletePartRecursive() throws InvalidFormatException {
  422. TreeMap<PackagePartName, String> expectedValues;
  423. TreeMap<PackagePartName, String> values;
  424. values = new TreeMap<PackagePartName, String>();
  425. // Expected values
  426. expectedValues = new TreeMap<PackagePartName, String>();
  427. expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
  428. "application/vnd.openxmlformats-package.relationships+xml");
  429. expectedValues
  430. .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
  431. "application/vnd.openxmlformats-officedocument.extended-properties+xml");
  432. expectedValues.put(PackagingURIHelper
  433. .createPartName("/docProps/core.xml"),
  434. "application/vnd.openxmlformats-package.core-properties+xml");
  435. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  436. @SuppressWarnings("resource")
  437. OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  438. // Remove the core part
  439. p.deletePartRecursive(PackagingURIHelper.createPartName("/word/document.xml"));
  440. for (PackagePart part : p.getParts()) {
  441. values.put(part.getPartName(), part.getContentType());
  442. logger.log(POILogger.DEBUG, part.getPartName());
  443. }
  444. // Compare expected values with values return by the package
  445. for (PackagePartName partName : expectedValues.keySet()) {
  446. assertNotNull(values.get(partName));
  447. assertEquals(expectedValues.get(partName), values.get(partName));
  448. }
  449. // Don't save modifications
  450. p.revert();
  451. }
  452. /**
  453. * Test that we can open a file by path, and then
  454. * write changes to it.
  455. */
  456. @Test
  457. public void openFileThenOverwrite() throws IOException, InvalidFormatException {
  458. File tempFile = TempFile.createTempFile("poiTesting","tmp");
  459. File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
  460. FileHelper.copyFile(origFile, tempFile);
  461. // Open the temp file
  462. OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  463. // Close it
  464. p.close();
  465. // Delete it
  466. assertTrue(tempFile.delete());
  467. // Reset
  468. FileHelper.copyFile(origFile, tempFile);
  469. p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  470. // Save it to the same file - not allowed
  471. try {
  472. p.save(tempFile);
  473. fail("You shouldn't be able to call save(File) to overwrite the current file");
  474. } catch(InvalidOperationException e) {
  475. // expected here
  476. }
  477. p.close();
  478. // Delete it
  479. assertTrue(tempFile.delete());
  480. // Open it read only, then close and delete - allowed
  481. FileHelper.copyFile(origFile, tempFile);
  482. p = OPCPackage.open(tempFile.toString(), PackageAccess.READ);
  483. p.close();
  484. assertTrue(tempFile.delete());
  485. }
  486. /**
  487. * Test that we can open a file by path, save it
  488. * to another file, then delete both
  489. */
  490. @Test
  491. public void openFileThenSaveDelete() throws IOException, InvalidFormatException {
  492. File tempFile = TempFile.createTempFile("poiTesting","tmp");
  493. File tempFile2 = TempFile.createTempFile("poiTesting","tmp");
  494. File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
  495. FileHelper.copyFile(origFile, tempFile);
  496. // Open the temp file
  497. OPCPackage p = OPCPackage.open(tempFile.toString(), PackageAccess.READ_WRITE);
  498. // Save it to a different file
  499. p.save(tempFile2);
  500. p.close();
  501. // Delete both the files
  502. assertTrue(tempFile.delete());
  503. assertTrue(tempFile2.delete());
  504. }
  505. private static ContentTypeManager getContentTypeManager(OPCPackage pkg) {
  506. return POITestCase.getFieldValue(OPCPackage.class, pkg, ContentTypeManager.class, "contentTypeManager");
  507. }
  508. @Test
  509. public void getPartsByName() throws IOException, InvalidFormatException {
  510. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  511. @SuppressWarnings("resource")
  512. OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
  513. try {
  514. List<PackagePart> rs = pkg.getPartsByName(Pattern.compile("/word/.*?\\.xml"));
  515. HashMap<String, PackagePart> selected = new HashMap<String, PackagePart>();
  516. for(PackagePart p : rs)
  517. selected.put(p.getPartName().getName(), p);
  518. assertEquals(6, selected.size());
  519. assertTrue(selected.containsKey("/word/document.xml"));
  520. assertTrue(selected.containsKey("/word/fontTable.xml"));
  521. assertTrue(selected.containsKey("/word/settings.xml"));
  522. assertTrue(selected.containsKey("/word/styles.xml"));
  523. assertTrue(selected.containsKey("/word/theme/theme1.xml"));
  524. assertTrue(selected.containsKey("/word/webSettings.xml"));
  525. } finally {
  526. // use revert to not re-write the input file
  527. pkg.revert();
  528. }
  529. }
  530. @Test
  531. public void getPartSize() throws IOException, InvalidFormatException {
  532. String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
  533. OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ);
  534. try {
  535. int checked = 0;
  536. for (PackagePart part : pkg.getParts()) {
  537. // Can get the size of zip parts
  538. if (part.getPartName().getName().equals("/word/document.xml")) {
  539. checked++;
  540. assertEquals(ZipPackagePart.class, part.getClass());
  541. assertEquals(6031L, part.getSize());
  542. }
  543. if (part.getPartName().getName().equals("/word/fontTable.xml")) {
  544. checked++;
  545. assertEquals(ZipPackagePart.class, part.getClass());
  546. assertEquals(1312L, part.getSize());
  547. }
  548. // But not from the others
  549. if (part.getPartName().getName().equals("/docProps/core.xml")) {
  550. checked++;
  551. assertEquals(PackagePropertiesPart.class, part.getClass());
  552. assertEquals(-1, part.getSize());
  553. }
  554. }
  555. // Ensure we actually found the parts we want to check
  556. assertEquals(3, checked);
  557. } finally {
  558. pkg.close();
  559. }
  560. }
  561. @Test
  562. public void replaceContentType()
  563. throws IOException, InvalidFormatException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
  564. InputStream is = OpenXML4JTestDataSamples.openSampleStream("sample.xlsx");
  565. @SuppressWarnings("resource")
  566. OPCPackage p = OPCPackage.open(is);
  567. ContentTypeManager mgr = getContentTypeManager(p);
  568. assertTrue(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"));
  569. assertFalse(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml"));
  570. assertTrue(
  571. p.replaceContentType(
  572. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
  573. "application/vnd.ms-excel.sheet.macroEnabled.main+xml")
  574. );
  575. assertFalse(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"));
  576. assertTrue(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml"));
  577. p.revert();
  578. is.close();
  579. }
  580. /**
  581. * Verify we give helpful exceptions (or as best we can) when
  582. * supplied with non-OOXML file types (eg OLE2, ODF)
  583. */
  584. @Test
  585. public void NonOOXMLFileTypes() throws Exception {
  586. // Spreadsheet has a good mix of alternate file types
  587. POIDataSamples files = POIDataSamples.getSpreadSheetInstance();
  588. // OLE2 - Stream
  589. try {
  590. InputStream stream = files.openResourceAsStream("SampleSS.xls");
  591. try {
  592. OPCPackage.open(stream);
  593. } finally {
  594. stream.close();
  595. }
  596. fail("Shouldn't be able to open OLE2");
  597. } catch (OLE2NotOfficeXmlFileException e) {
  598. assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format"));
  599. assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML"));
  600. }
  601. // OLE2 - File
  602. try {
  603. OPCPackage.open(files.getFile("SampleSS.xls"));
  604. fail("Shouldn't be able to open OLE2");
  605. } catch (OLE2NotOfficeXmlFileException e) {
  606. assertTrue(e.getMessage().contains("The supplied data appears to be in the OLE2 Format"));
  607. assertTrue(e.getMessage().contains("You are calling the part of POI that deals with OOXML"));
  608. }
  609. // Raw XML - Stream
  610. try {
  611. InputStream stream = files.openResourceAsStream("SampleSS.xml");
  612. try {
  613. OPCPackage.open(stream);
  614. } finally {
  615. stream.close();
  616. }
  617. fail("Shouldn't be able to open XML");
  618. } catch (NotOfficeXmlFileException e) {
  619. assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file"));
  620. assertTrue(e.getMessage().contains("Formats such as Office 2003 XML"));
  621. }
  622. // Raw XML - File
  623. try {
  624. OPCPackage.open(files.getFile("SampleSS.xml"));
  625. fail("Shouldn't be able to open XML");
  626. } catch (NotOfficeXmlFileException e) {
  627. assertTrue(e.getMessage().contains("The supplied data appears to be a raw XML file"));
  628. assertTrue(e.getMessage().contains("Formats such as Office 2003 XML"));
  629. }
  630. // ODF / ODS - Stream
  631. try {
  632. InputStream stream = files.openResourceAsStream("SampleSS.ods");
  633. try {
  634. OPCPackage.open(stream);
  635. } finally {
  636. stream.close();
  637. }
  638. fail("Shouldn't be able to open ODS");
  639. } catch (ODFNotOfficeXmlFileException e) {
  640. assertTrue(e.toString().contains("The supplied data appears to be in ODF"));
  641. assertTrue(e.toString().contains("Formats like these (eg ODS"));
  642. }
  643. // ODF / ODS - File
  644. try {
  645. OPCPackage.open(files.getFile("SampleSS.ods"));
  646. fail("Shouldn't be able to open ODS");
  647. } catch (ODFNotOfficeXmlFileException e) {
  648. assertTrue(e.toString().contains("The supplied data appears to be in ODF"));
  649. assertTrue(e.toString().contains("Formats like these (eg ODS"));
  650. }
  651. // Plain Text - Stream
  652. try {
  653. InputStream stream = files.openResourceAsStream("SampleSS.txt");
  654. try {
  655. OPCPackage.open(stream);
  656. } finally {
  657. stream.close();
  658. }
  659. fail("Shouldn't be able to open Plain Text");
  660. } catch (NotOfficeXmlFileException e) {
  661. assertTrue(e.getMessage().contains("No valid entries or contents found"));
  662. assertTrue(e.getMessage().contains("not a valid OOXML"));
  663. }
  664. // Plain Text - File
  665. try {
  666. OPCPackage.open(files.getFile("SampleSS.txt"));
  667. fail("Shouldn't be able to open Plain Text");
  668. } catch (UnsupportedFileFormatException e) {
  669. // Unhelpful low-level error, sorry
  670. }
  671. }
  672. @Test(expected=IOException.class)
  673. public void zipBombCreateAndHandle()
  674. throws IOException, EncryptedDocumentException, InvalidFormatException {
  675. // #50090 / #56865
  676. ZipFile zipFile = ZipHelper.openZipFile(OpenXML4JTestDataSamples.getSampleFile("sample.xlsx"));
  677. assertNotNull(zipFile);
  678. ByteArrayOutputStream bos = new ByteArrayOutputStream(2500000);
  679. ZipOutputStream append = new ZipOutputStream(bos);
  680. // first, copy contents from existing war
  681. Enumeration<? extends ZipEntry> entries = zipFile.entries();
  682. while (entries.hasMoreElements()) {
  683. ZipEntry e2 = entries.nextElement();
  684. ZipEntry e = new ZipEntry(e2.getName());
  685. e.setTime(e2.getTime());
  686. e.setComment(e2.getComment());
  687. e.setSize(e2.getSize());
  688. append.putNextEntry(e);
  689. if (!e.isDirectory()) {
  690. InputStream is = zipFile.getInputStream(e);
  691. if (e.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. for (int i=0; i<spam.length; i++) spam[i] = ' ';
  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. e.setSize(size);
  706. } else {
  707. IOUtils.copy(is, append);
  708. }
  709. is.close();
  710. }
  711. append.closeEntry();
  712. }
  713. append.close();
  714. zipFile.close();
  715. byte buf[] = bos.toByteArray();
  716. //noinspection UnusedAssignment
  717. bos = null;
  718. Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(buf));
  719. wb.getSheetAt(0);
  720. wb.close();
  721. zipFile.close();
  722. }
  723. @Test
  724. public void zipBombCheckSizes()
  725. throws IOException, EncryptedDocumentException, InvalidFormatException {
  726. File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx");
  727. try {
  728. double min_ratio = Double.MAX_VALUE;
  729. long max_size = 0;
  730. ZipFile zf = ZipHelper.openZipFile(file);
  731. assertNotNull(zf);
  732. Enumeration<? extends ZipEntry> entries = zf.entries();
  733. while (entries.hasMoreElements()) {
  734. ZipEntry ze = entries.nextElement();
  735. double ratio = (double)ze.getCompressedSize() / (double)ze.getSize();
  736. min_ratio = Math.min(min_ratio, ratio);
  737. max_size = Math.max(max_size, ze.getSize());
  738. }
  739. zf.close();
  740. // use values close to, but within the limits
  741. ZipSecureFile.setMinInflateRatio(min_ratio-0.002);
  742. assertEquals(min_ratio-0.002, ZipSecureFile.getMinInflateRatio(), 0.00001);
  743. ZipSecureFile.setMaxEntrySize(max_size+1);
  744. assertEquals(max_size+1, ZipSecureFile.getMaxEntrySize());
  745. WorkbookFactory.create(file, null, true).close();
  746. // check ratio out of bounds
  747. ZipSecureFile.setMinInflateRatio(min_ratio+0.002);
  748. try {
  749. WorkbookFactory.create(file, null, true).close();
  750. // this is a bit strange, as there will be different exceptions thrown
  751. // depending if this executed via "ant test" or within eclipse
  752. // maybe a difference in JDK ...
  753. } catch (InvalidFormatException e) {
  754. checkForZipBombException(e);
  755. } catch (POIXMLException e) {
  756. checkForZipBombException(e);
  757. }
  758. // check max entry size ouf of bounds
  759. ZipSecureFile.setMinInflateRatio(min_ratio-0.002);
  760. ZipSecureFile.setMaxEntrySize(max_size-1);
  761. try {
  762. WorkbookFactory.create(file, null, true).close();
  763. } catch (InvalidFormatException e) {
  764. checkForZipBombException(e);
  765. } catch (POIXMLException e) {
  766. checkForZipBombException(e);
  767. }
  768. } finally {
  769. // reset otherwise a lot of ooxml tests will fail
  770. ZipSecureFile.setMinInflateRatio(0.01d);
  771. ZipSecureFile.setMaxEntrySize(0xFFFFFFFFL);
  772. }
  773. }
  774. private void checkForZipBombException(Throwable e) {
  775. if(e instanceof InvocationTargetException) {
  776. InvocationTargetException t = (InvocationTargetException)e;
  777. IOException t2 = (IOException)t.getTargetException();
  778. if(t2.getMessage().startsWith("Zip bomb detected!")) {
  779. return;
  780. }
  781. }
  782. String msg = e.getMessage();
  783. if(msg != null && msg.startsWith("Zip bomb detected!")) {
  784. return;
  785. }
  786. // recursively check the causes for the message as it can be nested further down in the exception-tree
  787. if(e.getCause() != null && e.getCause() != e) {
  788. checkForZipBombException(e.getCause());
  789. return;
  790. }
  791. 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);
  792. }
  793. @Test
  794. public void testConstructors() throws IOException {
  795. // verify the various ways to construct a ZipSecureFile
  796. File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx");
  797. ZipSecureFile zipFile = new ZipSecureFile(file);
  798. assertNotNull(zipFile.getName());
  799. zipFile.close();
  800. zipFile = new ZipSecureFile(file, ZipFile.OPEN_READ);
  801. assertNotNull(zipFile.getName());
  802. zipFile.close();
  803. zipFile = new ZipSecureFile(file.getAbsolutePath());
  804. assertNotNull(zipFile.getName());
  805. zipFile.close();
  806. }
  807. @Test
  808. public void testMaxTextSize() {
  809. long before = ZipSecureFile.getMaxTextSize();
  810. try {
  811. ZipSecureFile.setMaxTextSize(12345);
  812. assertEquals(12345, ZipSecureFile.getMaxTextSize());
  813. } finally {
  814. ZipSecureFile.setMaxTextSize(before);
  815. }
  816. }
  817. // bug 60128
  818. @Test
  819. public void testCorruptFile() throws IOException {
  820. OPCPackage pkg = null;
  821. File file = OpenXML4JTestDataSamples.getSampleFile("invalid.xlsx");
  822. try {
  823. pkg = OPCPackage.open(file, PackageAccess.READ);
  824. } catch (Exception e) {
  825. System.out.println(e.getClass().getName());
  826. System.out.println(e.getMessage());
  827. e.printStackTrace();
  828. } finally {
  829. if (pkg != null) {
  830. pkg.close();
  831. }
  832. }
  833. }
  834. }