diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index bd4f7dab0..9eb163fc8 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -36,6 +36,7 @@ + 44536 - Improved support for detecting read-only recommended files 43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one) 28231 - For apparently truncated files, which are somehow still valid, now issue a truncation warning but carry on, rather than giving an exception as before 44504 - Added initial support for recognising external functions like YEARFRAC and ISEVEN (using NameXPtg), via LinkTable support diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 62d0a8dce..201c4936d 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,7 @@ + 44536 - Improved support for detecting read-only recommended files 43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one) 44504 - Added initial support for recognising external functions like YEARFRAC and ISEVEN (using NameXPtg), via LinkTable support 44504 - Improvements to FormulaParser - operators, precedence, error literals, quotes in string literals, range checking on IntPtg, formulas with extra un-parsed stuff at the end, improved parse error handling diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 9bf1c8f47..8fa3010a4 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -2300,6 +2300,17 @@ public class Workbook implements Model } return this.fileShare; } + + /** + * is the workbook protected with a password (not encrypted)? + */ + public boolean isWriteProtected() { + if (this.fileShare == null) { + return false; + } + FileSharingRecord frec = getFileSharing(); + return (frec.getReadOnly() == 1); + } /** * protect a workbook with a password (not encypted, just sets writeprotect diff --git a/src/java/org/apache/poi/hssf/record/FileSharingRecord.java b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java index 17f8cda64..2f56eb092 100644 --- a/src/java/org/apache/poi/hssf/record/FileSharingRecord.java +++ b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java @@ -19,6 +19,8 @@ package org.apache.poi.hssf.record; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; import org.apache.poi.util.StringUtil; /** @@ -29,6 +31,8 @@ import org.apache.poi.util.StringUtil; */ public class FileSharingRecord extends Record { + private static POILogger logger = POILogFactory.getLogger(FileSharingRecord.class); + public final static short sid = 0x5b; private short field_1_readonly; private short field_2_password; @@ -58,8 +62,23 @@ public class FileSharingRecord extends Record { field_1_readonly = in.readShort(); field_2_password = in.readShort(); field_3_username_length = in.readByte(); + + // Is this really correct? The latest docs + // seem to hint there's nothing between the + // username length and the username string field_4_unknown = in.readShort(); - field_5_username = in.readCompressedUnicode(field_3_username_length); + + // Ensure we don't try to read more data than + // there actually is + if(field_3_username_length > in.remaining()) { + logger.log(POILogger.WARN, "FileSharingRecord defined a username of length " + field_3_username_length + ", but only " + in.remaining() + " bytes were left, truncating"); + field_3_username_length = (byte)in.remaining(); + } + if(field_3_username_length > 0) { + field_5_username = in.readCompressedUnicode(field_3_username_length); + } else { + field_5_username = ""; + } } //this is the world's lamest "security". thanks to Wouter van Vugt for making me diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index 32094287f..431558ccc 100755 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -267,7 +267,7 @@ public class RecordInputStream extends InputStream public String readCompressedUnicode(int length) { if ((length < 0) || ((remaining() < length) && !isContinueNext())) { - throw new IllegalArgumentException("Illegal length"); + throw new IllegalArgumentException("Illegal length " + length); } StringBuffer buf = new StringBuffer(length); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index e112da221..1e89d6beb 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -1379,6 +1379,13 @@ public class HSSFWorkbook extends POIDocument } } + /** + * Is the workbook protected with a password (not encrypted)? + */ + public boolean isWriteProtected() { + return this.workbook.isWriteProtected(); + } + /** * protect a workbook with a password (not encypted, just sets writeprotect * flags and the password. diff --git a/src/testcases/org/apache/poi/hssf/data/ReadOnlyRecommended.xls b/src/testcases/org/apache/poi/hssf/data/ReadOnlyRecommended.xls new file mode 100644 index 000000000..d479b94f6 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ReadOnlyRecommended.xls differ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 04c729b3b..f9bb362c7 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -1104,6 +1104,29 @@ extends TestCase { assertEquals(1, wb.getNumberOfSheets()); } + + /** + * Files with "read only recommended" were giving + * grief on the FileSharingRecord + */ + public void test44536() throws Exception { + FileInputStream in = new FileInputStream(new File(cwd, "ReadOnlyRecommended.xls")); + + // Used to blow up with an IllegalArgumentException + // when creating a FileSharingRecord + HSSFWorkbook wb = new HSSFWorkbook(in); + in.close(); + + // Check read only advised + assertEquals(3, wb.getNumberOfSheets()); + assertTrue(wb.isWriteProtected()); + + // But also check that another wb isn't + in = new FileInputStream(new File(cwd, "SimpleWithChoose.xls")); + wb = new HSSFWorkbook(in); + in.close(); + assertFalse(wb.isWriteProtected()); + } }