From ac4e3c199eb33b387da2866dfd0dc0b6d305bc25 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 20 Feb 2026 20:19:10 +0100 Subject: [PATCH] more HSSFEventFactory changes --- .../hssf/eventusermodel/HSSFEventFactory.java | 156 +++++++++++++++--- .../eventusermodel/TestHSSFEventFactory.java | 41 ++++- 2 files changed, 170 insertions(+), 27 deletions(-) diff --git a/poi/src/main/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java b/poi/src/main/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java index 309c3a5e8f..c52000e936 100644 --- a/poi/src/main/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java +++ b/poi/src/main/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java @@ -47,7 +47,7 @@ public class HSSFEventFactory { /** * Processes a file into essentially record events. * - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners * @param fs a POIFS filesystem containing your workbook * * @throws IOException if the workbook contained errors @@ -56,15 +56,45 @@ public class HSSFEventFactory { processWorkbookEvents(req, fs.getRoot()); } + /** + * Processes a file into essentially record events. + * + * @param req an instance of HSSFRequest which has your registered listeners + * @param fs a POIFS filesystem containing your workbook + * @param password in char array format (can be null) + * + * @throws IOException if the workbook contained errors + * @since 6.0.0 + */ + public void processWorkbookEvents(HSSFRequest req, POIFSFileSystem fs, + final char[] password) throws IOException { + processWorkbookEvents(req, fs.getRoot(), password); + } + /** * Processes a file into essentially record events. * - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners * @param dir a DirectoryNode containing your workbook * * @throws IOException if the workbook contained errors */ public void processWorkbookEvents(HSSFRequest req, DirectoryNode dir) throws IOException { + processWorkbookEvents(req, dir, null); + } + + /** + * Processes a file into essentially record events. + * + * @param req an instance of HSSFRequest which has your registered listeners + * @param dir a DirectoryNode containing your workbook + * @param password in char array format (can be null) + * + * @throws IOException if the workbook contained errors + * @since 6.0.0 + */ + public void processWorkbookEvents(HSSFRequest req, DirectoryNode dir, + final char[] password) throws IOException { // some old documents have "WORKBOOK" or "BOOK" String name = null; if (dir.hasEntry(WORKBOOK)) { @@ -79,29 +109,45 @@ public class HSSFEventFactory { } try (InputStream in = dir.createDocumentInputStream(name)) { - processEvents(req, in); + processEvents(req, in, password); } } - /** - * Processes a file into essentially record events. - * - * @param req an Instance of HSSFRequest which has your registered listeners - * @param fs a POIFS filesystem containing your workbook - * @return numeric user-specified result code. - * - * @throws HSSFUserException if the processing should be aborted - * @throws IOException if the workbook contained errors - */ - public short abortableProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs) - throws IOException, HSSFUserException { - return abortableProcessWorkbookEvents(req, fs.getRoot()); - } - /** * Processes a file into essentially record events. * - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners + * @param fs a POIFS filesystem containing your workbook + * @return numeric user-specified result code. + * @throws HSSFUserException if the processing should be aborted + * @throws IOException if the workbook contained errors + */ + public short abortableProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs) + throws IOException, HSSFUserException { + return abortableProcessWorkbookEvents(req, fs.getRoot()); + } + + /** + * Processes a file into essentially record events. + * + * @param req an instance of HSSFRequest which has your registered listeners + * @param fs a POIFS filesystem containing your workbook + * @param password in char array format (can be null) + * @return numeric user-specified result code. + * @throws HSSFUserException if the processing should be aborted + * @throws IOException if the workbook contained errors + * @since 6.0.0 + */ + public short abortableProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs, + final char[] password) + throws IOException, HSSFUserException { + return abortableProcessWorkbookEvents(req, fs.getRoot(), password); + } + + /** + * Processes a file into essentially record events. + * + * @param req an instance of HSSFRequest which has your registered listeners * @param dir a DirectoryNode containing your workbook * @return numeric user-specified result code. * @@ -115,6 +161,26 @@ public class HSSFEventFactory { } } + /** + * Processes a file into essentially record events. + * + * @param req an instance of HSSFRequest which has your registered listeners + * @param dir a DirectoryNode containing your workbook + * @param password in char array format (can be null) + * @return numeric user-specified result code. + * + * @throws HSSFUserException if the processing should be aborted + * @throws IOException if the workbook contained errors + * @since 6.0.0 + */ + public short abortableProcessWorkbookEvents(HSSFRequest req, DirectoryNode dir, + final char[] password) + throws IOException, HSSFUserException { + try (InputStream in = dir.createDocumentInputStream("Workbook")) { + return abortableProcessEvents(req, in, password); + } + } + /** * Processes a DocumentInputStream into essentially Record events. * @@ -123,23 +189,43 @@ public class HSSFEventFactory { * user code or HSSFUserException will be passed back. * * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String) - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners * @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object */ public void processEvents(HSSFRequest req, InputStream in) { try { - genericProcessEvents(req, in); + genericProcessEvents(req, in, null); } catch (HSSFUserException hue) { /*If an HSSFUserException user exception is thrown, ignore it.*/ } } + /** + * Processes a DocumentInputStream into essentially Record events. + * + * If an AbortableHSSFListener causes a halt to processing during this call + * the method will return just as with abortableProcessEvents, but no + * user code or HSSFUserException will be passed back. + * + * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String) + * @param req an instance of HSSFRequest which has your registered listeners + * @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object + * @param password in char array format (can be null) + * @since 6.0.0 + */ + public void processEvents(HSSFRequest req, InputStream in, final char[] password) { + try { + genericProcessEvents(req, in, password); + } catch (HSSFUserException hue) { + /*If an HSSFUserException user exception is thrown, ignore it.*/ + } + } /** * Processes a DocumentInputStream into essentially Record events. * * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String) - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners * @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object * @return numeric user-specified result code. * @@ -147,23 +233,41 @@ public class HSSFEventFactory { */ public short abortableProcessEvents(HSSFRequest req, InputStream in) throws HSSFUserException { - return genericProcessEvents(req, in); + return genericProcessEvents(req, in, null); } /** * Processes a DocumentInputStream into essentially Record events. * * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String) - * @param req an Instance of HSSFRequest which has your registered listeners + * @param req an instance of HSSFRequest which has your registered listeners + * @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object + * @param password in char array format (can be null) + * @return numeric user-specified result code. + * + * @throws HSSFUserException if the processing should be aborted + * @since 6.0.0 + */ + public short abortableProcessEvents(HSSFRequest req, InputStream in, final char[] password) + throws HSSFUserException { + return genericProcessEvents(req, in, password); + } + + /** + * Processes a DocumentInputStream into essentially Record events. + * + * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String) + * @param req an instance of HSSFRequest which has your registered listeners * @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object * @return numeric user-specified result code. */ - private short genericProcessEvents(HSSFRequest req, InputStream in) + private short genericProcessEvents(HSSFRequest req, InputStream in, final char[] password) throws HSSFUserException { short userCode = 0; // Create a new RecordStream and use that - RecordFactoryInputStream recordStream = new RecordFactoryInputStream(in, false); + RecordFactoryInputStream recordStream = new RecordFactoryInputStream( + in, false, password); // Process each record as they come in while(true) { diff --git a/poi/src/test/java/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java b/poi/src/test/java/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java index 4f5ea89a6b..b23639647e 100644 --- a/poi/src/test/java/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java +++ b/poi/src/test/java/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java @@ -64,7 +64,7 @@ final class TestHSSFEventFactory { try (InputStream is = HSSFTestDataSamples.openSampleFileStream(sampleFileName); POIFSFileSystem fs = new POIFSFileSystem(is)) { HSSFEventFactory factory = new HSSFEventFactory(); - factory.processWorkbookEvents(req, fs); + factory.processWorkbookEvents(req, fs, password.toCharArray()); } return records; } @@ -135,6 +135,45 @@ final class TestHSSFEventFactory { @Test void testWithPasswordProtectedWorkbooks() throws Exception { + final List records = + openSample("xor-encryption-abc.xls", "abc"); + + // Check we got the sheet and the contents + assertTrue(records.size() > 50); + + // Has one sheet, with values 1,2,3 in column A rows 1-3 + boolean hasSheet = false, hasA1 = false, hasA2 = false, hasA3 = false; + for (org.apache.poi.hssf.record.Record r : records) { + if (r instanceof BoundSheetRecord) { + BoundSheetRecord bsr = (BoundSheetRecord) r; + assertEquals("Sheet1", bsr.getSheetname()); + hasSheet = true; + } + if (r instanceof NumberRecord) { + NumberRecord nr = (NumberRecord) r; + if (nr.getColumn() == 0 && nr.getRow() == 0) { + assertEquals(1, (int) nr.getValue()); + hasA1 = true; + } + if (nr.getColumn() == 0 && nr.getRow() == 1) { + assertEquals(2, (int) nr.getValue()); + hasA2 = true; + } + if (nr.getColumn() == 0 && nr.getRow() == 2) { + assertEquals(3, (int) nr.getValue()); + hasA3 = true; + } + } + } + + assertTrue(hasSheet, "Sheet record not found"); + assertTrue(hasA1, "Numeric record for A1 not found"); + assertTrue(hasA2, "Numeric record for A2 not found"); + assertTrue(hasA3, "Numeric record for A3 not found"); + } + + @Test + void testWithPasswordProtectedWorkbooksBiff8EncryptionKey() throws Exception { // With the password, is properly processed Biff8EncryptionKey.setCurrentUserPassword("abc"); try {