diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 1f81dc34ee..7347a785c2 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -64,7 +64,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideSize; import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument; /** - * High level representation of a ooxml slideshow. + * High level representation of an ooxml slideshow. * This is the first object most users will construct whether * they are reading or writing a slideshow. It is also the * top level object for creating new slides/etc. @@ -75,7 +75,8 @@ public class XMLSlideShow extends POIXMLDocument implements SlideShow { private static final Logger LOG = LogManager.getLogger(XMLSlideShow.class); //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private CTPresentation _presentation; private final List _slides = new ArrayList<>(); @@ -86,6 +87,20 @@ public class XMLSlideShow extends POIXMLDocument private XSLFNotesMaster _notesMaster; private XSLFCommentAuthors _commentAuthors; + /** + * @param length the max record length allowed for XMLSlideShow + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for XMLSlideShow + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public XMLSlideShow() { this(empty()); } diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBParser.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBParser.java index 398d53bc92..7786b22663 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBParser.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/binary/XSSFBParser.java @@ -36,11 +36,26 @@ import org.apache.poi.util.LittleEndianInputStream; public abstract class XSSFBParser { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private final LittleEndianInputStream is; private final SparseBitSet records; + /** + * @param length the max record length allowed for XSSFBParser + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for XSSFBParser + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public XSSFBParser(InputStream is) { this.is = new LittleEndianInputStream(is); records = null; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/chunks/ChunkFactory.java b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/chunks/ChunkFactory.java index ff7c56eac3..03f13b452f 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/chunks/ChunkFactory.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/chunks/ChunkFactory.java @@ -43,8 +43,8 @@ import static org.apache.logging.log4j.util.Unbox.box; public final class ChunkFactory { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; - + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** The version of the currently open document */ private int version; @@ -63,6 +63,20 @@ public final class ChunkFactory { /** For logging problems we spot with the file */ private static final Logger LOG = LogManager.getLogger(ChunkFactory.class); + /** + * @param length the max record length allowed for ChunkFactory + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for ChunkFactory + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public ChunkFactory(int version) throws IOException { this.version = version; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/CompressedStreamStore.java b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/CompressedStreamStore.java index 031778d07b..91ea0400e0 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/CompressedStreamStore.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/CompressedStreamStore.java @@ -30,7 +30,8 @@ import org.apache.poi.util.IOUtils; public final class CompressedStreamStore extends StreamStore { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 64_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 64_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** The raw, compressed contents */ private byte[] compressedContents; @@ -44,6 +45,20 @@ public final class CompressedStreamStore extends StreamStore { byte[] _getCompressedContents() { return compressedContents; } byte[] _getBlockHeader() { return blockHeader; } + /** + * @param length the max record length allowed for CompressedStreamStore + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for CompressedStreamStore + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Creates a new compressed StreamStore, which will handle * the decompression. diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/StreamStore.java b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/StreamStore.java index 0bb639bf47..7d31e6abec 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/StreamStore.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hdgf/streams/StreamStore.java @@ -26,10 +26,25 @@ import org.apache.poi.util.IOUtils; */ public class StreamStore { // TODO - instantiable superclass //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 10_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 10_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private byte[] contents; + /** + * @param length the max record length allowed for StreamStore + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for StreamStore + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Creates a new, non compressed Stream Store */ diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/MAPIAttribute.java b/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/MAPIAttribute.java index 0533705c2e..18f47d0654 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/MAPIAttribute.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/MAPIAttribute.java @@ -41,12 +41,27 @@ import org.apache.poi.util.StringUtil; public class MAPIAttribute { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = 1_000_000; private final MAPIProperty property; private final int type; private final byte[] data; + /** + * @param length the max record length allowed for MAPIAttribute + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for MAPIAttribute + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Constructs a single new attribute from * the contents of the stream diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/TNEFAttribute.java b/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/TNEFAttribute.java index 547bad8e11..479cb8a56f 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/TNEFAttribute.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hmef/attribute/TNEFAttribute.java @@ -35,12 +35,27 @@ import org.apache.poi.util.LittleEndian; public class TNEFAttribute { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 20_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 20_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private final TNEFProperty property; private final int type; private final byte[] data; private final int checksum; + + /** + * @param length the max record length allowed for TNEFAttribute + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for TNEFAttribute + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } /** * Constructs a single new attribute from the id, type, diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hpbf/model/qcbits/QCTextBit.java b/poi-scratchpad/src/main/java/org/apache/poi/hpbf/model/qcbits/QCTextBit.java index 61efa79df2..a8074ef085 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hpbf/model/qcbits/QCTextBit.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hpbf/model/qcbits/QCTextBit.java @@ -26,7 +26,22 @@ import org.apache.poi.util.StringUtil; public final class QCTextBit extends QCBit { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; + + /** + * @param length the max record length allowed for QCTextBit + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for QCTextBit + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } public QCTextBit(String thingType, String bitType, byte[] data) { super(thingType, bitType, data); diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/dev/SlideShowDumper.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/dev/SlideShowDumper.java index 2efe20c614..6f55f394d4 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/dev/SlideShowDumper.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/dev/SlideShowDumper.java @@ -37,288 +37,310 @@ import org.apache.poi.util.LittleEndian; /** * This class provides a way to "peek" inside a powerpoint file. It - * will print out all the types it find, and for those it know aren't - * atoms, what they contain - * + * will print out all the types it finds, and for those it knows aren't + * atoms, what they contain + *

* To figure out what things are, and if they are atoms or not, used the - * list in hslf.record.RecordTypes - * + * list in hslf.record.RecordTypes + *

* To peek inside PPDrawings, which hold Escher drawings, we use the - * DDF package from POI (but we can fake it by using the Escher listings - * from hslf.record.RecordTypes also) + * DDF package from POI (but we can fake it by using the Escher listings + * from hslf.record.RecordTypes also) */ public final class SlideShowDumper { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private byte[] docstream; - /** Do we try to use DDF to understand the escher objects? */ - private boolean ddfEscher; - /** Do we use our own built-in basic escher groker to understand the escher objects? */ - private boolean basicEscher; + /** + * Do we try to use DDF to understand the escher objects? + */ + private boolean ddfEscher; + /** + * Do we use our own built-in basic escher groker to understand the escher objects? + */ + private boolean basicEscher; - private PrintStream out; + private PrintStream out; - /** - * right now this function takes one parameter: a ppt file, and outputs - * a dump of what it contains - */ - public static void main(String[] args) throws IOException - { - if(args.length == 0) { - System.err.println("Usage: SlideShowDumper [-escher|-basicescher] "); - return; + /** + * @param length the max record length allowed for SlideShowDumper + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; } - String filename = args[0]; - if(args.length > 1) { - filename = args[1]; + /** + * @return the max record length allowed for SlideShowDumper + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; } - try (POIFSFileSystem poifs = new POIFSFileSystem(new File(filename))) { - SlideShowDumper foo = new SlideShowDumper(poifs, System.out); - - if(args.length > 1) { - if(args[0].equalsIgnoreCase("-escher")) { - foo.setDDFEscher(true); - } else { - foo.setBasicEscher(true); - } + /** + * right now this function takes one parameter: a ppt file, and outputs + * a dump of what it contains + */ + public static void main(String[] args) throws IOException { + if (args.length == 0) { + System.err.println("Usage: SlideShowDumper [-escher|-basicescher] "); + return; } - foo.printDump(); - } - } - - /** - * Constructs a Powerpoint dump from a POIFS Filesystem. Parses the - * document and dumps out the contents - * - * @param filesystem the POIFS FileSystem to read from - * @throws IOException if there is a problem while parsing the document. - */ - public SlideShowDumper(POIFSFileSystem filesystem, PrintStream out) throws IOException { - // Grab the document stream - InputStream is = filesystem.createDocumentInputStream(HSLFSlideShow.POWERPOINT_DOCUMENT); - docstream = IOUtils.toByteArray(is); - is.close(); - this.out = out; - } - - /** - * Control dumping of any Escher records found - should DDF be used? - */ - public void setDDFEscher(boolean grok) { - ddfEscher = grok; - basicEscher = !(grok); - } - - /** - * Control dumping of any Escher records found - should our built in - * basic groker be used? - */ - public void setBasicEscher(boolean grok) { - basicEscher = grok; - ddfEscher = !(grok); - } - - public void printDump() throws IOException { - // The format of records in a powerpoint file are: - // - // - // - // If it has a zero length, following it will be another record - // - // If it has a length, depending on its type it may have children or data - // If it has children, these will follow straight away - // > - // If it has data, this will come straigh after, and run for the length - // - // All lengths given exclude the 8 byte record header - // (Data records are known as Atoms) - - // Document should start with: - // 0F 00 E8 03 ## ## ## ## - // (type 1000 = document, info 00 0f is normal, rest is document length) - // 01 00 E9 03 28 00 00 00 - // (type 1001 = document atom, info 00 01 normal, 28 bytes long) - - // When parsing a document, look to see if you know about that type - // of the current record. If you know it's a type that has children, - // process the record's data area looking for more records - // If you know about the type and it doesn't have children, either do - // something with the data (eg TextRun) or skip over it - // Otherwise, check the first byte. If you do a BINARY_AND on it with - // 0x0f (15) and get back 0x0f, you know it has children. Otherwise - // it doesn't - - walkTree(0,0,docstream.length); -} - -public void walkTree(int depth, int startPos, int maxLen) throws IOException { - int pos = startPos; - int endPos = startPos + maxLen; - final String ind = (depth == 0) ? "%1$s" : "%1$"+depth+"s"; - while(pos <= endPos - 8) { - long type = LittleEndian.getUShort(docstream,pos+2); - long len = LittleEndian.getUInt(docstream,pos+4); - byte opt = docstream[pos]; - - String fmt = ind+"At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)"; - out.println(String.format(Locale.ROOT, fmt, "", pos, type, len)); - - // See if we know about the type of it - String recordName = RecordTypes.forTypeID((short)type).name(); - - // Jump over header, and think about going on more - pos += 8; - out.println(String.format(Locale.ROOT, ind+"That's a %2$s", "", recordName)); - - // Now check if it's a container or not - int container = opt & 0x0f; - - // BinaryTagData seems to contain records, but it - // isn't tagged as doing so. Try stepping in anyway - if(type == 5003L && opt == 0L) { - container = 0x0f; + String filename = args[0]; + if (args.length > 1) { + filename = args[1]; } - out.println(); - if (type != 0L && container == 0x0f) { - if (type == 1035L || type == 1036L) { - // Special Handling of 1035=PPDrawingGroup and 1036=PPDrawing - if(ddfEscher) { - // Seems to be: - walkEscherDDF((depth+3),pos+8,(int)len-8); - } else if(basicEscher) { - walkEscherBasic((depth+3),pos+8,(int)len-8); + try (POIFSFileSystem poifs = new POIFSFileSystem(new File(filename))) { + SlideShowDumper foo = new SlideShowDumper(poifs, System.out); + + if (args.length > 1) { + if (args[0].equalsIgnoreCase("-escher")) { + foo.setDDFEscher(true); + } else { + foo.setBasicEscher(true); } - } else { - // General container record handling code - walkTree((depth+2),pos,(int)len); + } + + foo.printDump(); + } + } + + /** + * Constructs a Powerpoint dump from a POIFS Filesystem. Parses the + * document and dumps out the contents + * + * @param filesystem the POIFS FileSystem to read from + * @throws IOException if there is a problem while parsing the document. + */ + public SlideShowDumper(POIFSFileSystem filesystem, PrintStream out) throws IOException { + // Grab the document stream + InputStream is = filesystem.createDocumentInputStream(HSLFSlideShow.POWERPOINT_DOCUMENT); + docstream = IOUtils.toByteArray(is); + is.close(); + this.out = out; + } + + /** + * Control dumping of any Escher records found - should DDF be used? + */ + public void setDDFEscher(boolean grok) { + ddfEscher = grok; + basicEscher = !(grok); + } + + /** + * Control dumping of any Escher records found - should our built in + * basic groker be used? + */ + public void setBasicEscher(boolean grok) { + basicEscher = grok; + ddfEscher = !(grok); + } + + public void printDump() throws IOException { + // The format of records in a powerpoint file are: + // + // + // + // If it has a zero length, following it will be another record + // + // If it has a length, depending on its type it may have children or data + // If it has children, these will follow straight away + // > + // If it has data, this will come straigh after, and run for the length + // + // All lengths given exclude the 8 byte record header + // (Data records are known as Atoms) + + // Document should start with: + // 0F 00 E8 03 ## ## ## ## + // (type 1000 = document, info 00 0f is normal, rest is document length) + // 01 00 E9 03 28 00 00 00 + // (type 1001 = document atom, info 00 01 normal, 28 bytes long) + + // When parsing a document, look to see if you know about that type + // of the current record. If you know it's a type that has children, + // process the record's data area looking for more records + // If you know about the type and it doesn't have children, either do + // something with the data (eg TextRun) or skip over it + // Otherwise, check the first byte. If you do a BINARY_AND on it with + // 0x0f (15) and get back 0x0f, you know it has children. Otherwise + // it doesn't + + walkTree(0, 0, docstream.length); + } + + public void walkTree(int depth, int startPos, int maxLen) throws IOException { + int pos = startPos; + int endPos = startPos + maxLen; + final String ind = (depth == 0) ? "%1$s" : "%1$" + depth + "s"; + while (pos <= endPos - 8) { + long type = LittleEndian.getUShort(docstream, pos + 2); + long len = LittleEndian.getUInt(docstream, pos + 4); + byte opt = docstream[pos]; + + String fmt = ind + "At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)"; + out.println(String.format(Locale.ROOT, fmt, "", pos, type, len)); + + // See if we know about the type of it + String recordName = RecordTypes.forTypeID((short) type).name(); + + // Jump over header, and think about going on more + pos += 8; + out.println(String.format(Locale.ROOT, ind + "That's a %2$s", "", recordName)); + + // Now check if it's a container or not + int container = opt & 0x0f; + + // BinaryTagData seems to contain records, but it + // isn't tagged as doing so. Try stepping in anyway + if (type == 5003L && opt == 0L) { + container = 0x0f; + } + + out.println(); + if (type != 0L && container == 0x0f) { + if (type == 1035L || type == 1036L) { + // Special Handling of 1035=PPDrawingGroup and 1036=PPDrawing + if (ddfEscher) { + // Seems to be: + walkEscherDDF((depth + 3), pos + 8, (int) len - 8); + } else if (basicEscher) { + walkEscherBasic((depth + 3), pos + 8, (int) len - 8); + } + } else { + // General container record handling code + walkTree((depth + 2), pos, (int) len); + } + } + + pos += (int) len; + } + } + + /** + * Use the DDF code to walk the Escher records + */ + public void walkEscherDDF(int indent, int pos, int len) { + if (len < 8) { + return; + } + + final String ind = (indent == 0) ? "%1$s" : "%1$" + indent + "s"; + + byte[] contents = IOUtils.safelyClone(docstream, pos, len, MAX_RECORD_LENGTH); + DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory(); + EscherRecord record = erf.createRecord(contents, 0); + + // For now, try filling in the fields + record.fillFields(contents, 0, erf); + + long atomType = LittleEndian.getUShort(contents, 2); + // This lacks the 8 byte header size + long atomLen = LittleEndian.getUShort(contents, 4); + // This (should) include the 8 byte header size + int recordLen = record.getRecordSize(); + + String fmt = ind + "At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x) (%5$d) - record claims %6$d"; + out.println(String.format(Locale.ROOT, fmt, "", pos, atomType, atomLen, atomLen + 8, recordLen)); + + + // Check for corrupt / lying ones + if (recordLen != 8 && (recordLen != (atomLen + 8))) { + out.println(String.format(Locale.ROOT, ind + "** Atom length of $2d ($3d) doesn't match record length of %4d", "", atomLen, atomLen + 8, recordLen)); + } + + // Print the record's details + String recordStr = record.toString().replace("\n", String.format(Locale.ROOT, "\n" + ind, "")); + out.println(String.format(Locale.ROOT, ind + "%2$s", "", recordStr)); + + if (record instanceof EscherContainerRecord) { + walkEscherDDF((indent + 3), pos + 8, (int) atomLen); + } + + // Handle records that seem to lie + if (atomType == 61451L) { + // Normally claims a size of 8 + recordLen = (int) atomLen + 8; + } + if (atomType == 61453L) { + // Returns EscherContainerRecord, but really msofbtClientTextbox + recordLen = (int) atomLen + 8; + record.fillFields(contents, 0, erf); + if (!(record instanceof EscherTextboxRecord)) { + out.println(String.format(Locale.ROOT, ind + "%2$s", "", "** Really a msofbtClientTextbox !")); } } - pos += (int)len; - } - } + // Decide on what to do, based on how the lengths match up + if (recordLen == 8 && atomLen > 8) { + // Assume it has children, rather than being corrupted + walkEscherDDF((indent + 3), pos + 8, (int) atomLen); - /** - * Use the DDF code to walk the Escher records - */ - public void walkEscherDDF(int indent, int pos, int len) { - if(len < 8) { return; } + // Wind on our length + our header + pos += atomLen; + pos += 8; + len -= atomLen; + len -= 8; + } else { + // No children, wind on our real length + pos += atomLen; + pos += 8; + len -= atomLen; + len -= 8; + } - final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s"; - - byte[] contents = IOUtils.safelyClone(docstream, pos, len, MAX_RECORD_LENGTH); - DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory(); - EscherRecord record = erf.createRecord(contents,0); - - // For now, try filling in the fields - record.fillFields(contents,0,erf); - - long atomType = LittleEndian.getUShort(contents,2); - // This lacks the 8 byte header size - long atomLen = LittleEndian.getUShort(contents,4); - // This (should) include the 8 byte header size - int recordLen = record.getRecordSize(); - - String fmt = ind+"At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x) (%5$d) - record claims %6$d"; - out.println(String.format(Locale.ROOT, fmt, "", pos, atomType, atomLen, atomLen+8, recordLen)); - - - // Check for corrupt / lying ones - if(recordLen != 8 && (recordLen != (atomLen+8))) { - out.println(String.format(Locale.ROOT, ind+"** Atom length of $2d ($3d) doesn't match record length of %4d", "", atomLen, atomLen+8, recordLen)); - } - - // Print the record's details - String recordStr = record.toString().replace("\n", String.format(Locale.ROOT, "\n"+ind, "")); - out.println(String.format(Locale.ROOT, ind+"%2$s", "", recordStr)); - - if(record instanceof EscherContainerRecord) { - walkEscherDDF((indent+3), pos + 8, (int)atomLen ); - } - - // Handle records that seem to lie - if(atomType == 61451L) { - // Normally claims a size of 8 - recordLen = (int)atomLen + 8; - } - if(atomType == 61453L) { - // Returns EscherContainerRecord, but really msofbtClientTextbox - recordLen = (int)atomLen + 8; - record.fillFields( contents, 0, erf ); - if(! (record instanceof EscherTextboxRecord)) { - out.println(String.format(Locale.ROOT, ind+"%2$s", "", "** Really a msofbtClientTextbox !")); + // Move on to the next one, if we're not at the end yet + if (len >= 8) { + walkEscherDDF(indent, pos, len); } } - // Decide on what to do, based on how the lengths match up - if(recordLen == 8 && atomLen > 8 ) { - // Assume it has children, rather than being corrupted - walkEscherDDF((indent+3), pos + 8, (int)atomLen ); + /** + * Use the basic record format groking code to walk the Escher records + */ + public void walkEscherBasic(int indent, int pos, int len) throws IOException { + if (len < 8) { + return; + } - // Wind on our length + our header - pos += atomLen; - pos += 8; - len -= atomLen; - len -= 8; - } else { - // No children, wind on our real length - pos += atomLen; - pos += 8; - len -= atomLen; - len -= 8; - } + final String ind = (indent == 0) ? "%1$s" : "%1$" + indent + "s"; - // Move on to the next one, if we're not at the end yet - if(len >= 8) { - walkEscherDDF(indent, pos, len ); - } - } + long type = LittleEndian.getUShort(docstream, pos + 2); + long atomlen = LittleEndian.getUInt(docstream, pos + 4); - /** - * Use the basic record format groking code to walk the Escher records - */ - public void walkEscherBasic(int indent, int pos, int len) throws IOException { - if(len < 8) { return; } + String fmt = ind + "At position %2$d ($2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)"; + out.println(String.format(Locale.ROOT, fmt, "", pos, type, atomlen)); - final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s"; + String typeName = RecordTypes.forTypeID((short) type).name(); + out.println(String.format(Locale.ROOT, ind + "%2$s", "That's an Escher Record: ", typeName)); - long type = LittleEndian.getUShort(docstream,pos+2); - long atomlen = LittleEndian.getUInt(docstream,pos+4); + // Record specific dumps + if (type == 61453L) { + // Text Box. Print out first 8 bytes of data, then 8 4 later + HexDump.dump(docstream, 0, out, pos + 8, 8); + HexDump.dump(docstream, 0, out, pos + 20, 8); + out.println(); + } - String fmt = ind+"At position %2$d ($2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)"; - out.println(String.format(Locale.ROOT, fmt, "", pos, type, atomlen)); - String typeName = RecordTypes.forTypeID((short)type).name(); - out.println(String.format(Locale.ROOT, ind+"%2$s", "That's an Escher Record: ", typeName)); - - // Record specific dumps - if(type == 61453L) { - // Text Box. Print out first 8 bytes of data, then 8 4 later - HexDump.dump(docstream, 0, out, pos+8, 8); - HexDump.dump(docstream, 0, out, pos+20, 8); + // Blank line before next entry out.println(); + + // Look in children if we are a container + if (type == 61443L || type == 61444L) { + walkEscherBasic((indent + 3), pos + 8, (int) atomlen); + } + + // Keep going if not yet at end + if (atomlen < len) { + int atomleni = (int) atomlen; + walkEscherBasic(indent, pos + atomleni + 8, len - atomleni - 8); + } } - - - // Blank line before next entry - out.println(); - - // Look in children if we are a container - if(type == 61443L || type == 61444L) { - walkEscherBasic((indent+3), pos+8, (int)atomlen); - } - - // Keep going if not yet at end - if(atomlen < len) { - int atomleni = (int)atomlen; - walkEscherBasic(indent, pos+atomleni+8, len-atomleni-8); - } - } } diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/CString.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/CString.java index 0dfd545cba..e0004baeee 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/CString.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/CString.java @@ -31,13 +31,14 @@ import org.apache.poi.util.StringUtil; /** * A CString (type 4026). Holds a unicode string, and the first two bytes * of the record header normally encode the count. Typically attached to - * some complex sequence of records, eg Commetns. + * some complex sequence of records, eg Comments. */ public final class CString extends RecordAtom { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = 1_000_000; private byte[] _header; @@ -49,6 +50,20 @@ public final class CString extends RecordAtom { return StringUtil.getFromUnicodeLE(_text); } + /** + * @param length the max record length allowed for CString + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for CString + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** Updates the text in the Atom. */ public void setText(String text) { // Convert to little endian unicode diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/ExObjListAtom.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/ExObjListAtom.java index 9bd90201d4..2baf3f400c 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/ExObjListAtom.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/ExObjListAtom.java @@ -36,7 +36,8 @@ public class ExObjListAtom extends RecordAtom { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** * Record header. @@ -48,6 +49,20 @@ public class ExObjListAtom extends RecordAtom */ private byte[] _data; + /** + * @param length the max record length allowed for MasterTextPropAtom + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for MasterTextPropAtom + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Constructs a brand new link related atom record. */ diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/FontEmbeddedData.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/FontEmbeddedData.java index 0be9d5e782..155344d2bd 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/FontEmbeddedData.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/FontEmbeddedData.java @@ -51,14 +51,14 @@ public class FontEmbeddedData extends RecordAtom implements FontFacet { private FontHeader fontHeader; /** - * @param length the max length allowed for FontEmbeddedData + * @param length the max record length allowed for FontEmbeddedData */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for FontEmbeddedData + * @return the max record length allowed for FontEmbeddedData */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/MasterTextPropAtom.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/MasterTextPropAtom.java index 1e3cf364a4..1b96b35260 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/MasterTextPropAtom.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/MasterTextPropAtom.java @@ -38,7 +38,8 @@ import org.apache.poi.util.LittleEndian; public final class MasterTextPropAtom extends RecordAtom { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** * Record header. @@ -53,6 +54,20 @@ public final class MasterTextPropAtom extends RecordAtom { // indent details private List indents; + /** + * @param length the max record length allowed for MasterTextPropAtom + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for MasterTextPropAtom + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Constructs a new empty master text prop atom. */ diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/StyleTextProp9Atom.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/StyleTextProp9Atom.java index 20317e3074..3878057cae 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/StyleTextProp9Atom.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/record/StyleTextProp9Atom.java @@ -36,7 +36,8 @@ import org.apache.poi.util.LittleEndian; public final class StyleTextProp9Atom extends RecordAtom { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private final TextPFException9[] autoNumberSchemes; /** Record header. */ @@ -47,6 +48,20 @@ public final class StyleTextProp9Atom extends RecordAtom { private short recordId; private int length; + /** + * @param length the max record length allowed for StyleTextProp9Atom + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for StyleTextProp9Atom + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Constructs the link related atom record from its * source data. diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideShow.java index 9975f3c9d4..466f780336 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideShow.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFSlideShow.java @@ -85,7 +85,8 @@ public final class HSLFSlideShow extends POIDocument implements SlideShow _notes = new ArrayList<>(); private FontCollection _fonts; + /** + * @param length the max record length allowed for HSLFSlideShow + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for HSLFSlideShow + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } /** * Constructs a Powerpoint document from the underlying diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java index 377dec1615..de28c70bbb 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java @@ -60,7 +60,8 @@ public abstract class PropertiesChunk extends Chunk { public static final String NAME = "__properties_version1.0"; // arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; // standard prefix, defined in the spec public static final String VARIABLE_LENGTH_PROPERTY_PREFIX = "__substg1.0_"; @@ -84,6 +85,20 @@ public abstract class PropertiesChunk extends Chunk { */ private final ChunkGroup parentGroup; + /** + * @param length the max record length allowed for PropertiesChunk + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for PropertiesChunk + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Creates a Properties Chunk. */ diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/DocumentProperties.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/DocumentProperties.java index b2bfbe8fc3..963c0f5942 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/DocumentProperties.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/DocumentProperties.java @@ -29,10 +29,25 @@ public final class DocumentProperties extends DOPAbstractType { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private byte[] _preserved; + /** + * @param length the max record length allowed for DocumentProperties + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for DocumentProperties + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * @deprecated Use {@link #DocumentProperties(byte[],int,int)} instead */ diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/Ffn.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/Ffn.java index 559e1af71e..346f5aabf8 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/Ffn.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/Ffn.java @@ -32,175 +32,172 @@ import org.apache.poi.util.LittleEndianConsts; * that stores info about the whole structure and the fonts */ @Internal -public final class Ffn -{ +public final class Ffn { - //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + //arbitrarily selected; may need to increase + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; - private int _cbFfnM1;//total length of FFN - 1. - private byte _info; + private int _cbFfnM1;//total length of FFN - 1. + private byte _info; private static BitField _prq = BitFieldFactory.getInstance(0x0003);// pitch request private static BitField _fTrueType = BitFieldFactory.getInstance(0x0004);// when 1, font is a TrueType font private static BitField _ff = BitFieldFactory.getInstance(0x0070); - private short _wWeight;// base weight of font - private byte _chs;// character set identifier - private byte _ixchSzAlt; // index into ffn.szFfn to the name of - // the alternate font - private byte [] _panose = new byte[10];//???? - private byte [] _fontSig = new byte[24];//???? + private short _wWeight;// base weight of font + private byte _chs;// character set identifier + private byte _ixchSzAlt; // index into ffn.szFfn to the name of + // the alternate font + private byte[] _panose = new byte[10];//???? + private byte[] _fontSig = new byte[24];//???? - // zero terminated string that records name of font, cuurently not - // supporting Extended chars - private char [] _xszFfn; + // zero terminated string that records name of font, cuurently not + // supporting Extended chars + private char[] _xszFfn; - // extra facilitator members - private int _xszFfnLength; + // extra facilitator members + private int _xszFfnLength; - public Ffn(byte[] buf, int offset) - { - int offsetTmp = offset; - - _cbFfnM1 = LittleEndian.getUByte(buf,offset); - offset += LittleEndianConsts.BYTE_SIZE; - _info = buf[offset]; - offset += LittleEndianConsts.BYTE_SIZE; - _wWeight = LittleEndian.getShort(buf, offset); - offset += LittleEndianConsts.SHORT_SIZE; - _chs = buf[offset]; - offset += LittleEndianConsts.BYTE_SIZE; - _ixchSzAlt = buf[offset]; - offset += LittleEndianConsts.BYTE_SIZE; - - // read panose and fs so we can write them back out. - System.arraycopy(buf, offset, _panose, 0, _panose.length); - offset += _panose.length; - System.arraycopy(buf, offset, _fontSig, 0, _fontSig.length); - offset += _fontSig.length; - - offsetTmp = offset - offsetTmp; - _xszFfnLength = (this.getSize() - offsetTmp)/2; - _xszFfn = new char[_xszFfnLength]; - - for(int i = 0; i < _xszFfnLength; i++) - { - _xszFfn[i] = (char)LittleEndian.getShort(buf, offset); - offset += LittleEndianConsts.SHORT_SIZE; + /** + * @param length the max record length allowed for Ffn + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for Ffn + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; } + public Ffn(byte[] buf, int offset) { + int offsetTmp = offset; - } + _cbFfnM1 = LittleEndian.getUByte(buf, offset); + offset += LittleEndianConsts.BYTE_SIZE; + _info = buf[offset]; + offset += LittleEndianConsts.BYTE_SIZE; + _wWeight = LittleEndian.getShort(buf, offset); + offset += LittleEndianConsts.SHORT_SIZE; + _chs = buf[offset]; + offset += LittleEndianConsts.BYTE_SIZE; + _ixchSzAlt = buf[offset]; + offset += LittleEndianConsts.BYTE_SIZE; - public int get_cbFfnM1() - { - return _cbFfnM1; - } + // read panose and fs so we can write them back out. + System.arraycopy(buf, offset, _panose, 0, _panose.length); + offset += _panose.length; + System.arraycopy(buf, offset, _fontSig, 0, _fontSig.length); + offset += _fontSig.length; - public short getWeight() - { - return _wWeight; - } + offsetTmp = offset - offsetTmp; + _xszFfnLength = (this.getSize() - offsetTmp) / 2; + _xszFfn = new char[_xszFfnLength]; - public byte getChs() - { - return _chs; - } + for (int i = 0; i < _xszFfnLength; i++) { + _xszFfn[i] = (char) LittleEndian.getShort(buf, offset); + offset += LittleEndianConsts.SHORT_SIZE; + } - public byte [] getPanose() - { - return _panose; - } - public byte [] getFontSig() - { - return _fontSig; - } - - public int getSize() - { - return (_cbFfnM1 + 1); - } - - public String getMainFontName() - { - int index = 0; - for (;index < _xszFfnLength; index++) - { - if (_xszFfn[index] == '\0') - { - break; - } - } - return new String(_xszFfn, 0, index); - } - - public String getAltFontName() - { - int index = _ixchSzAlt; - for (;index < _xszFfnLength; index++) - { - if (_xszFfn[index] == '\0') - { - break; - } - } - return new String(_xszFfn, _ixchSzAlt, index); - - } - - public void set_cbFfnM1(int _cbFfnM1) - { - this._cbFfnM1 = _cbFfnM1; - } - - // changed protected to public - public byte[] toByteArray() - { - int offset = 0; - byte[] buf = IOUtils.safelyAllocate(this.getSize(), MAX_RECORD_LENGTH); - - buf[offset] = (byte)_cbFfnM1; - offset += LittleEndianConsts.BYTE_SIZE; - buf[offset] = _info; - offset += LittleEndianConsts.BYTE_SIZE; - LittleEndian.putShort(buf, offset, _wWeight); - offset += LittleEndianConsts.SHORT_SIZE; - buf[offset] = _chs; - offset += LittleEndianConsts.BYTE_SIZE; - buf[offset] = _ixchSzAlt; - offset += LittleEndianConsts.BYTE_SIZE; - - System.arraycopy(_panose,0,buf, offset,_panose.length); - offset += _panose.length; - System.arraycopy(_fontSig,0,buf, offset, _fontSig.length); - offset += _fontSig.length; - - for(int i = 0; i < _xszFfn.length; i++) - { - LittleEndian.putShort(buf, offset, (short)_xszFfn[i]); - offset += LittleEndianConsts.SHORT_SIZE; } - return buf; + public int get_cbFfnM1() { + return _cbFfnM1; + } - } + public short getWeight() { + return _wWeight; + } - @Override - public boolean equals(Object other) { - if (!(other instanceof Ffn)) return false; - Ffn o = (Ffn)other; + public byte getChs() { + return _chs; + } - return ( - o._cbFfnM1 == this._cbFfnM1 - && o._info == this._info - && o._wWeight == _wWeight - && o._chs == _chs - && o._ixchSzAlt == _ixchSzAlt - && Arrays.equals(o._panose,_panose) - && Arrays.equals(o._fontSig,_fontSig) - && Arrays.equals(o._xszFfn,_xszFfn) - ); - } + public byte[] getPanose() { + return _panose; + } + + public byte[] getFontSig() { + return _fontSig; + } + + public int getSize() { + return (_cbFfnM1 + 1); + } + + public String getMainFontName() { + int index = 0; + for (; index < _xszFfnLength; index++) { + if (_xszFfn[index] == '\0') { + break; + } + } + return new String(_xszFfn, 0, index); + } + + public String getAltFontName() { + int index = _ixchSzAlt; + for (; index < _xszFfnLength; index++) { + if (_xszFfn[index] == '\0') { + break; + } + } + return new String(_xszFfn, _ixchSzAlt, index); + + } + + public void set_cbFfnM1(int _cbFfnM1) { + this._cbFfnM1 = _cbFfnM1; + } + + // changed protected to public + public byte[] toByteArray() { + int offset = 0; + byte[] buf = IOUtils.safelyAllocate(this.getSize(), MAX_RECORD_LENGTH); + + buf[offset] = (byte) _cbFfnM1; + offset += LittleEndianConsts.BYTE_SIZE; + buf[offset] = _info; + offset += LittleEndianConsts.BYTE_SIZE; + LittleEndian.putShort(buf, offset, _wWeight); + offset += LittleEndianConsts.SHORT_SIZE; + buf[offset] = _chs; + offset += LittleEndianConsts.BYTE_SIZE; + buf[offset] = _ixchSzAlt; + offset += LittleEndianConsts.BYTE_SIZE; + + System.arraycopy(_panose, 0, buf, offset, _panose.length); + offset += _panose.length; + System.arraycopy(_fontSig, 0, buf, offset, _fontSig.length); + offset += _fontSig.length; + + for (int i = 0; i < _xszFfn.length; i++) { + LittleEndian.putShort(buf, offset, (short) _xszFfn[i]); + offset += LittleEndianConsts.SHORT_SIZE; + } + + return buf; + + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Ffn)) return false; + Ffn o = (Ffn) other; + + return ( + o._cbFfnM1 == this._cbFfnM1 + && o._info == this._info + && o._wWeight == _wWeight + && o._chs == _chs + && o._ixchSzAlt == _ixchSzAlt + && Arrays.equals(o._panose, _panose) + && Arrays.equals(o._fontSig, _fontSig) + && Arrays.equals(o._xszFfn, _xszFfn) + ); + } @Override diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmBuffer.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmBuffer.java index 201d069742..38a0efaa02 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmBuffer.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmBuffer.java @@ -28,9 +28,6 @@ import org.apache.poi.util.LittleEndianConsts; @Internal public final class SprmBuffer implements Duplicatable { - //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; - byte[] _buf; boolean _istd; int _offset; @@ -56,7 +53,7 @@ public final class SprmBuffer implements Duplicatable { } public SprmBuffer(int sprmsStartOffset) { - _buf = IOUtils.safelyAllocate(sprmsStartOffset + 4L, MAX_RECORD_LENGTH); + _buf = IOUtils.safelyAllocate(sprmsStartOffset + 4L, SprmUtils.MAX_RECORD_LENGTH); _offset = sprmsStartOffset; _sprmsStartOffset = sprmsStartOffset; } @@ -118,7 +115,7 @@ public final class SprmBuffer implements Duplicatable { // commented - buffer shall not contain any additional bytes -- // sergey // byte[] newBuf = new byte[_offset + addition + 6]; - IOUtils.safelyAllocateCheck(_offset + (long)addition, MAX_RECORD_LENGTH); + IOUtils.safelyAllocateCheck(_offset + (long)addition, SprmUtils.MAX_RECORD_LENGTH); _buf = Arrays.copyOf(_buf, _offset + addition); } } diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmUtils.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmUtils.java index 4e978396ed..2c30f29a01 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmUtils.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/SprmUtils.java @@ -30,14 +30,14 @@ public final class SprmUtils { static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** - * @param length the max length allowed for SPRM data + * @param length the max record length allowed for SPRM data */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for SPRM data + * @return the max record length allowed for SPRM data */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java b/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java index f2c555496e..07731c97bb 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherArrayProperty.java @@ -37,7 +37,8 @@ import org.apache.poi.util.Removal; public final class EscherArrayProperty extends EscherComplexProperty implements Iterable { // arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; /** * The size of the header that goes at the start of the array, before the data @@ -54,6 +55,20 @@ public final class EscherArrayProperty extends EscherComplexProperty implements */ private final boolean emptyComplexPart; + /** + * @param length the max record length allowed for EscherArrayProperty + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for EscherArrayProperty + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + /** * Create an instance of an escher array property. * This constructor can be used to create emptyComplexParts with a complexSize = 0. diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherBSERecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherBSERecord.java index 0d6ad788fa..255fa212ec 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherBSERecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherBSERecord.java @@ -57,14 +57,14 @@ public final class EscherBSERecord extends EscherRecord { private byte[] _remainingData = new byte[0]; /** - * @param length the max length allowed for EscherBSERecord + * @param length the max record length allowed for EscherBSERecord */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherBSERecord + * @return the max record length allowed for EscherBSERecord */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherBlipRecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherBlipRecord.java index 860798b6fe..8a08fe29ee 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherBlipRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherBlipRecord.java @@ -38,14 +38,14 @@ public class EscherBlipRecord extends EscherRecord { private byte[] field_pictureData; /** - * @param length the max length allowed for EscherBlipRecord + * @param length the max record length allowed for EscherBlipRecord */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherBlipRecord + * @return the max record length allowed for EscherBlipRecord */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherClientAnchorRecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherClientAnchorRecord.java index b684014be5..a24c816fd6 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherClientAnchorRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherClientAnchorRecord.java @@ -62,14 +62,14 @@ public class EscherClientAnchorRecord extends EscherRecord { private boolean shortRecord; /** - * @param length the max length allowed for EscherClientAnchorRecord + * @param length the max record length allowed for EscherClientAnchorRecord */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherClientAnchorRecord + * @return the max record length allowed for EscherClientAnchorRecord */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherClientDataRecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherClientDataRecord.java index aab9249f8e..8023438aec 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherClientDataRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherClientDataRecord.java @@ -41,14 +41,14 @@ public class EscherClientDataRecord extends EscherRecord { private byte[] remainingData; /** - * @param length the max length allowed for EscherClientDataRecord + * @param length the max record length allowed for EscherClientDataRecord */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherClientDataRecord + * @return the max record length allowed for EscherClientDataRecord */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherMetafileBlip.java b/poi/src/main/java/org/apache/poi/ddf/EscherMetafileBlip.java index bb63ce40f5..8feafedcc1 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherMetafileBlip.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherMetafileBlip.java @@ -79,14 +79,14 @@ public final class EscherMetafileBlip extends EscherBlipRecord { private byte[] remainingData; /** - * @param length the max length allowed for EscherMetafileBlip + * @param length the max record length allowed for EscherMetafileBlip */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherMetafileBlip + * @return the max record length allowed for EscherMetafileBlip */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/EscherTextboxRecord.java b/poi/src/main/java/org/apache/poi/ddf/EscherTextboxRecord.java index 0105bc5c1e..06e21f2fcd 100644 --- a/poi/src/main/java/org/apache/poi/ddf/EscherTextboxRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/EscherTextboxRecord.java @@ -45,14 +45,14 @@ public final class EscherTextboxRecord extends EscherRecord { private byte[] thedata = NO_BYTES; /** - * @param length the max length allowed for EscherTextboxRecord + * @param length the max record length allowed for EscherTextboxRecord */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EscherTextboxRecord + * @return the max record length allowed for EscherTextboxRecord */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java b/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java index e3b2b8c5c0..28fbfb28c4 100644 --- a/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java +++ b/poi/src/main/java/org/apache/poi/ddf/UnknownEscherRecord.java @@ -34,7 +34,8 @@ import org.apache.poi.util.LittleEndian; public final class UnknownEscherRecord extends EscherRecord { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private static final byte[] NO_BYTES = new byte[0]; @@ -42,6 +43,20 @@ public final class UnknownEscherRecord extends EscherRecord { private byte[] thedata = NO_BYTES; private final List _childRecords = new ArrayList<>(); + /** + * @param length the max record length allowed for UnknownEscherRecord + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for UnknownEscherRecord + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public UnknownEscherRecord() {} public UnknownEscherRecord(UnknownEscherRecord other) { diff --git a/poi/src/main/java/org/apache/poi/hpsf/Blob.java b/poi/src/main/java/org/apache/poi/hpsf/Blob.java index 737462c667..8ac5d7bdfe 100644 --- a/poi/src/main/java/org/apache/poi/hpsf/Blob.java +++ b/poi/src/main/java/org/apache/poi/hpsf/Blob.java @@ -30,14 +30,14 @@ public class Blob { private byte[] _value; /** - * @param length the max length allowed for Blob + * @param length the max record length allowed for Blob */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for Blob + * @return the max record length allowed for Blob */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/hpsf/ClipboardData.java b/poi/src/main/java/org/apache/poi/hpsf/ClipboardData.java index 94e8edd3c2..8872b7efc1 100644 --- a/poi/src/main/java/org/apache/poi/hpsf/ClipboardData.java +++ b/poi/src/main/java/org/apache/poi/hpsf/ClipboardData.java @@ -38,14 +38,14 @@ public class ClipboardData { private byte[] _value; /** - * @param length the max length allowed for ClipboardData + * @param length the max record length allowed for ClipboardData */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for ClipboardData + * @return the max record length allowed for ClipboardData */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/hpsf/CodePageString.java b/poi/src/main/java/org/apache/poi/hpsf/CodePageString.java index ee2c69487a..44fde36a21 100644 --- a/poi/src/main/java/org/apache/poi/hpsf/CodePageString.java +++ b/poi/src/main/java/org/apache/poi/hpsf/CodePageString.java @@ -42,14 +42,14 @@ public class CodePageString { private byte[] _value; /** - * @param length the max length allowed for CodePageString + * @param length the max record length allowed for CodePageString */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for CodePageString + * @return the max record length allowed for CodePageString */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/hssf/extractor/OldExcelExtractor.java b/poi/src/main/java/org/apache/poi/hssf/extractor/OldExcelExtractor.java index 7d359c228b..d50ec09d4d 100644 --- a/poi/src/main/java/org/apache/poi/hssf/extractor/OldExcelExtractor.java +++ b/poi/src/main/java/org/apache/poi/hssf/extractor/OldExcelExtractor.java @@ -63,8 +63,8 @@ public class OldExcelExtractor implements POITextExtractor { private static final int FILE_PASS_RECORD_SID = 0x2f; //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; - + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private RecordInputStream ris; @@ -74,6 +74,20 @@ public class OldExcelExtractor implements POITextExtractor { private int biffVersion; private int fileType; + /** + * @param length the max record length allowed for OldExcelExtractor + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for OldExcelExtractor + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public OldExcelExtractor(InputStream input) throws IOException { open(input); } diff --git a/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java b/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java index 36776807f2..6f7370d180 100644 --- a/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java +++ b/poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java @@ -38,12 +38,27 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia public static final int RC4_REKEYING_INTERVAL = 1024; //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private final ChunkedCipherInputStream ccis; private final byte[] buffer = new byte[LittleEndianConsts.LONG_SIZE]; private boolean shouldSkipEncryptionOnCurrentRecord; + /** + * @param length the max record length allowed for Biff8DecryptingStream + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for Biff8DecryptingStream + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + public Biff8DecryptingStream(InputStream in, int initialOffset, EncryptionInfo info) throws RecordFormatException { try { byte[] initialBuf = IOUtils.safelyAllocate(initialOffset, MAX_RECORD_LENGTH); diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 65297e5405..f1e190b89a 100644 --- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -138,8 +138,10 @@ import org.apache.poi.util.Removal; public final class HSSFWorkbook extends POIDocument implements Workbook { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; - private static final int MAX_IMAGE_LENGTH = 50_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; + private static final int DEFAULT_MAX_IMAGE_LENGTH = 50_000_000; + private static int MAX_IMAGE_LENGTH = DEFAULT_MAX_IMAGE_LENGTH; private static final Pattern COMMA_PATTERN = Pattern.compile(","); @@ -218,6 +220,34 @@ public final class HSSFWorkbook extends POIDocument implements Workbook { return new HSSFWorkbook(book); } + /** + * @param length the max record length allowed for HSSFWorkbook + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for HSSFWorkbook + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + + /** + * @param length the max image length allowed for HSSFWorkbook + */ + public static void setMaxImageLength(int length) { + MAX_IMAGE_LENGTH = length; + } + + /** + * @return the max image length allowed for HSSFWorkbook + */ + public static int getMaxImageLength() { + return MAX_IMAGE_LENGTH; + } + /** * Creates new HSSFWorkbook from scratch (start here!) */ diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java b/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java index c0ff0fc2d3..c845d93b1d 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java @@ -31,10 +31,6 @@ import org.apache.poi.util.LittleEndianInputStream; @Internal public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { - //arbitrarily selected; may need to increase - private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; - private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; - private final int chunkSize; private final int chunkBits; @@ -46,20 +42,6 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { private long pos; private boolean chunkIsValid; - /** - * @param length the max length allowed for ChunkedCipherInputStream - */ - public static void setMaxRecordLength(int length) { - MAX_RECORD_LENGTH = length; - } - - /** - * @return the max length allowed for ChunkedCipherInputStream - */ - public static int getMaxRecordLength() { - return MAX_RECORD_LENGTH; - } - public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize) throws GeneralSecurityException { this(stream, size, chunkSize, 0); @@ -72,8 +54,8 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { this.pos = initialPos; this.chunkSize = chunkSize; int cs = chunkSize == -1 ? 4096 : chunkSize; - this.chunk = IOUtils.safelyAllocate(cs, MAX_RECORD_LENGTH); - this.plain = IOUtils.safelyAllocate(cs, MAX_RECORD_LENGTH); + this.chunk = IOUtils.safelyAllocate(cs, CryptoFunctions.MAX_RECORD_LENGTH); + this.plain = IOUtils.safelyAllocate(cs, CryptoFunctions.MAX_RECORD_LENGTH); this.chunkBits = Integer.bitCount(chunk.length-1); this.lastIndex = (int)(pos >> chunkBits); this.cipher = initCipherForBlock(null, lastIndex); diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java b/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java index 014a60c16d..b600b20073 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java @@ -43,9 +43,6 @@ import org.apache.poi.util.TempFile; @Internal public abstract class ChunkedCipherOutputStream extends FilterOutputStream { private static final Logger LOG = LogManager.getLogger(ChunkedCipherOutputStream.class); - //arbitrarily selected; may need to increase - private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; - private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private static final int STREAMING = -1; @@ -66,25 +63,11 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { private Cipher cipher; private boolean isClosed; - /** - * @param length the max length allowed for ChunkedCipherOutputStream - */ - public static void setMaxRecordLength(int length) { - MAX_RECORD_LENGTH = length; - } - - /** - * @return the max length allowed for ChunkedCipherOutputStream - */ - public static int getMaxRecordLength() { - return MAX_RECORD_LENGTH; - } - public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException { super(null); this.chunkSize = chunkSize; int cs = chunkSize == STREAMING ? 4096 : chunkSize; - this.chunk = IOUtils.safelyAllocate(cs, MAX_RECORD_LENGTH); + this.chunk = IOUtils.safelyAllocate(cs, CryptoFunctions.MAX_RECORD_LENGTH); this.plainByteFlags = new SparseBitSet(cs); this.chunkBits = Integer.bitCount(cs-1); this.fileOut = TempFile.createTempFile("encrypted_package", "crypt"); @@ -98,7 +81,7 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { super(stream); this.chunkSize = chunkSize; int cs = chunkSize == STREAMING ? 4096 : chunkSize; - this.chunk = IOUtils.safelyAllocate(cs, MAX_RECORD_LENGTH); + this.chunk = IOUtils.safelyAllocate(cs, CryptoFunctions.MAX_RECORD_LENGTH); this.plainByteFlags = new SparseBitSet(cs); this.chunkBits = Integer.bitCount(cs-1); this.fileOut = null; diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java b/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java index 1cc07539e9..ea0298a829 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/CryptoFunctions.java @@ -47,7 +47,22 @@ import org.apache.poi.util.StringUtil; public final class CryptoFunctions { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; + + /** + * @param length the max record length allowed for CryptoFunctions + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for CryptoFunctions + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } private CryptoFunctions() { } diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java b/poi/src/main/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java index d83754c6f6..bebd3643cf 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java @@ -35,24 +35,6 @@ import org.apache.poi.util.StringUtil; public class DataSpaceMapUtils { - //arbitrarily selected; may need to increase - private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; - private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; - - /** - * @param length the max length allowed for EscherTextboxRecord - */ - public static void setMaxRecordLength(int length) { - MAX_RECORD_LENGTH = length; - } - - /** - * @return the max length allowed for EscherTextboxRecord - */ - public static int getMaxRecordLength() { - return MAX_RECORD_LENGTH; - } - public static void addDefaultDataSpace(DirectoryEntry dir) throws IOException { DataSpaceMapEntry dsme = new DataSpaceMapEntry( new int[]{ 0 } @@ -352,7 +334,7 @@ public class DataSpaceMapUtils { return length == 0 ? null : ""; } - byte[] data = IOUtils.safelyAllocate(length, MAX_RECORD_LENGTH); + byte[] data = IOUtils.safelyAllocate(length, CryptoFunctions.MAX_RECORD_LENGTH); is.readFully(data); // Padding (variable): A set of bytes that MUST be of correct size such that the size of the UTF-8-LP-P4 diff --git a/poi/src/main/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java b/poi/src/main/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java index 97e172915e..0acf5d8241 100644 --- a/poi/src/main/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java +++ b/poi/src/main/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java @@ -68,11 +68,26 @@ import org.w3c.dom.Document; public class AgileEncryptor extends Encryptor { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 1_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private byte[] integritySalt; private byte[] pwHash; + /** + * @param length the max record length allowed for AgileEncryptor + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for AgileEncryptor + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + protected AgileEncryptor() {} protected AgileEncryptor(AgileEncryptor other) { diff --git a/poi/src/main/java/org/apache/poi/poifs/filesystem/Ole10Native.java b/poi/src/main/java/org/apache/poi/poifs/filesystem/Ole10Native.java index 1ce4238f63..9b1545e3b0 100644 --- a/poi/src/main/java/org/apache/poi/poifs/filesystem/Ole10Native.java +++ b/poi/src/main/java/org/apache/poi/poifs/filesystem/Ole10Native.java @@ -50,9 +50,11 @@ public class Ole10Native { public static final String OLE10_NATIVE = "\u0001Ole10Native"; private static final Charset ISO1 = StandardCharsets.ISO_8859_1; // arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000_000; + private static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; // arbitrarily selected; may need to increase - private static final int MAX_STRING_LENGTH = 1024; + private static final int DEFAULT_MAX_STRING_LENGTH = 1024; + private static int MAX_STRING_LENGTH = DEFAULT_MAX_STRING_LENGTH; /** * Default content of the \u0001Ole entry @@ -138,6 +140,34 @@ public class Ole10Native { } } + /** + * @param length the max record length allowed for Ole10Native + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for Ole10Native + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + + /** + * @param length the max string length allowed for Ole10Native + */ + public static void setMaxStringLength(int length) { + MAX_STRING_LENGTH = length; + } + + /** + * @return the max string length allowed for Ole10Native + */ + public static int getMaxStringLength() { + return MAX_STRING_LENGTH; + } + /** * Creates an instance and fills the fields based on ... the fields */ diff --git a/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSDocument.java b/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSDocument.java index 2dfb4c3c62..d8b6b2cd57 100644 --- a/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSDocument.java +++ b/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSDocument.java @@ -40,139 +40,133 @@ import org.apache.poi.util.IOUtils; */ public final class POIFSDocument implements POIFSViewable, Iterable { - //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; - private DocumentProperty _property; - private POIFSFileSystem _filesystem; - private POIFSStream _stream; - private int _block_size; - - /** - * Constructor for an existing Document - */ - public POIFSDocument(DocumentNode document) { - this((DocumentProperty)document.getProperty(), - ((DirectoryNode)document.getParent()).getFileSystem()); - } - - /** - * Constructor for an existing Document - */ - public POIFSDocument(DocumentProperty property, POIFSFileSystem filesystem) { - this._property = property; - this._filesystem = filesystem; + private POIFSFileSystem _filesystem; + private POIFSStream _stream; + private int _block_size; - if(property.getSize() < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) { - _stream = new POIFSStream(_filesystem.getMiniStore(), property.getStartBlock()); - _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); - } else { - _stream = new POIFSStream(_filesystem, property.getStartBlock()); - _block_size = _filesystem.getBlockStoreBlockSize(); - } - } + /** + * Constructor for an existing Document + */ + public POIFSDocument(DocumentNode document) { + this((DocumentProperty) document.getProperty(), + ((DirectoryNode) document.getParent()).getFileSystem()); + } - /** - * Constructor for a new Document - * - * @param name the name of the POIFSDocument - * @param stream the InputStream we read data from - */ - public POIFSDocument(String name, POIFSFileSystem filesystem, InputStream stream) - throws IOException - { - this._filesystem = filesystem; + /** + * Constructor for an existing Document + */ + public POIFSDocument(DocumentProperty property, POIFSFileSystem filesystem) { + this._property = property; + this._filesystem = filesystem; - // Store it - int length = store(stream); + if (property.getSize() < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) { + _stream = new POIFSStream(_filesystem.getMiniStore(), property.getStartBlock()); + _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); + } else { + _stream = new POIFSStream(_filesystem, property.getStartBlock()); + _block_size = _filesystem.getBlockStoreBlockSize(); + } + } - // Build the property for it - this._property = new DocumentProperty(name, length); - _property.setStartBlock(_stream.getStartBlock()); - _property.setDocument(this); - } - - public POIFSDocument(String name, final int size, POIFSFileSystem filesystem, POIFSWriterListener writer) - throws IOException - { - this._filesystem = filesystem; + /** + * Constructor for a new Document + * + * @param name the name of the POIFSDocument + * @param stream the InputStream we read data from + */ + public POIFSDocument(String name, POIFSFileSystem filesystem, InputStream stream) + throws IOException { + this._filesystem = filesystem; - if (size < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) { - _stream = new POIFSStream(filesystem.getMiniStore()); - _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); - } else { - _stream = new POIFSStream(filesystem); - _block_size = _filesystem.getBlockStoreBlockSize(); - } - - this._property = new DocumentProperty(name, size); - _property.setStartBlock(_stream.getStartBlock()); - _property.setDocument(this); + // Store it + int length = store(stream); - try (DocumentOutputStream os = new DocumentOutputStream(this, size)) { - POIFSDocumentPath path = new POIFSDocumentPath(name.split("\\\\")); - String docName = path.getComponent(path.length() - 1); - POIFSWriterEvent event = new POIFSWriterEvent(os, path, docName, size); - writer.processPOIFSWriterEvent(event); - } - } - - /** - * Stores the given data for this Document - */ - private int store(InputStream stream) throws IOException { - final int bigBlockSize = POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE; - BufferedInputStream bis = new BufferedInputStream(stream, bigBlockSize+1); - bis.mark(bigBlockSize); + // Build the property for it + this._property = new DocumentProperty(name, length); + _property.setStartBlock(_stream.getStartBlock()); + _property.setDocument(this); + } - // Do we need to store as a mini stream or a full one? - long streamBlockSize = IOUtils.skipFully(bis, bigBlockSize); - if (streamBlockSize < bigBlockSize) { - _stream = new POIFSStream(_filesystem.getMiniStore()); - _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); - } else { - _stream = new POIFSStream(_filesystem); - _block_size = _filesystem.getBlockStoreBlockSize(); - } + public POIFSDocument(String name, final int size, POIFSFileSystem filesystem, POIFSWriterListener writer) + throws IOException { + this._filesystem = filesystem; - // start from the beginning - bis.reset(); - - // Store it - final long length; - try (OutputStream os = _stream.getOutputStream()) { - length = IOUtils.copy(bis, os); + if (size < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) { + _stream = new POIFSStream(filesystem.getMiniStore()); + _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); + } else { + _stream = new POIFSStream(filesystem); + _block_size = _filesystem.getBlockStoreBlockSize(); + } - // Pad to the end of the block with -1s - int usedInBlock = (int) (length % _block_size); - if (usedInBlock != 0 && usedInBlock != _block_size) { - int toBlockEnd = _block_size - usedInBlock; - byte[] padding = IOUtils.safelyAllocate(toBlockEnd, MAX_RECORD_LENGTH); - Arrays.fill(padding, (byte) 0xFF); - os.write(padding); - } - } + this._property = new DocumentProperty(name, size); + _property.setStartBlock(_stream.getStartBlock()); + _property.setDocument(this); - return Math.toIntExact(length); - } - - /** - * Frees the underlying stream and property - */ - void free() throws IOException { - _stream.free(); - _property.setStartBlock(POIFSConstants.END_OF_CHAIN); - } - - POIFSFileSystem getFileSystem() - { - return _filesystem; - } - - int getDocumentBlockSize() { - return _block_size; - } + try (DocumentOutputStream os = new DocumentOutputStream(this, size)) { + POIFSDocumentPath path = new POIFSDocumentPath(name.split("\\\\")); + String docName = path.getComponent(path.length() - 1); + POIFSWriterEvent event = new POIFSWriterEvent(os, path, docName, size); + writer.processPOIFSWriterEvent(event); + } + } + + /** + * Stores the given data for this Document + */ + private int store(InputStream stream) throws IOException { + final int bigBlockSize = POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE; + BufferedInputStream bis = new BufferedInputStream(stream, bigBlockSize + 1); + bis.mark(bigBlockSize); + + // Do we need to store as a mini stream or a full one? + long streamBlockSize = IOUtils.skipFully(bis, bigBlockSize); + if (streamBlockSize < bigBlockSize) { + _stream = new POIFSStream(_filesystem.getMiniStore()); + _block_size = _filesystem.getMiniStore().getBlockStoreBlockSize(); + } else { + _stream = new POIFSStream(_filesystem); + _block_size = _filesystem.getBlockStoreBlockSize(); + } + + // start from the beginning + bis.reset(); + + // Store it + final long length; + try (OutputStream os = _stream.getOutputStream()) { + length = IOUtils.copy(bis, os); + + // Pad to the end of the block with -1s + int usedInBlock = (int) (length % _block_size); + if (usedInBlock != 0 && usedInBlock != _block_size) { + int toBlockEnd = _block_size - usedInBlock; + byte[] padding = IOUtils.safelyAllocate(toBlockEnd, POIFSFileSystem.MAX_RECORD_LENGTH); + Arrays.fill(padding, (byte) 0xFF); + os.write(padding); + } + } + + return Math.toIntExact(length); + } + + /** + * Frees the underlying stream and property + */ + void free() throws IOException { + _stream.free(); + _property.setStartBlock(POIFSConstants.END_OF_CHAIN); + } + + POIFSFileSystem getFileSystem() { + return _filesystem; + } + + int getDocumentBlockSize() { + return _block_size; + } @Override public Iterator iterator() { @@ -180,83 +174,83 @@ public final class POIFSDocument implements POIFSViewable, Iterable } Iterator getBlockIterator() { - return (getSize() > 0 ? _stream : Collections.emptyList()).iterator(); + return (getSize() > 0 ? _stream : Collections.emptyList()).iterator(); } - /** - * @return size of the document - */ - public int getSize() { - return _property.getSize(); - } - - public void replaceContents(InputStream stream) throws IOException { - free(); - int size = store(stream); - _property.setStartBlock(_stream.getStartBlock()); - _property.updateSize(size); - } + /** + * @return size of the document + */ + public int getSize() { + return _property.getSize(); + } - /** - * @return the instance's DocumentProperty - */ - DocumentProperty getDocumentProperty() { - return _property; - } + public void replaceContents(InputStream stream) throws IOException { + free(); + int size = store(stream); + _property.setStartBlock(_stream.getStartBlock()); + _property.updateSize(size); + } - /** - * Get an array of objects, some of which may implement POIFSViewable - * - * @return an array of Object; may not be null, but may be empty - */ - public Object[] getViewableArray() { - String result = ""; + /** + * @return the instance's DocumentProperty + */ + DocumentProperty getDocumentProperty() { + return _property; + } - if(getSize() > 0) { - // Get all the data into a single array - byte[] data = IOUtils.safelyAllocate(getSize(), MAX_RECORD_LENGTH); - int offset = 0; - for(ByteBuffer buffer : _stream) { - int length = Math.min(_block_size, data.length-offset); - buffer.get(data, offset, length); - offset += length; - } - - result = HexDump.dump(data, 0, 0); - } - - return new String[]{ result }; - } + /** + * Get an array of objects, some of which may implement POIFSViewable + * + * @return an array of Object; may not be null, but may be empty + */ + public Object[] getViewableArray() { + String result = ""; - /** - * Get an Iterator of objects, some of which may implement POIFSViewable - * - * @return an Iterator; may not be null, but may have an empty back end - * store - */ - public Iterator getViewableIterator() { - return emptyIterator(); - } + if (getSize() > 0) { + // Get all the data into a single array + byte[] data = IOUtils.safelyAllocate(getSize(), POIFSFileSystem.MAX_RECORD_LENGTH); + int offset = 0; + for (ByteBuffer buffer : _stream) { + int length = Math.min(_block_size, data.length - offset); + buffer.get(data, offset, length); + offset += length; + } - /** - * Give viewers a hint as to whether to call getViewableArray or - * getViewableIterator - * - * @return true if a viewer should call getViewableArray, - * false if a viewer should call getViewableIterator - */ - public boolean preferArray() { - return true; - } + result = HexDump.dump(data, 0, 0); + } - /** - * Provides a short description of the object, to be used when a - * POIFSViewable object has not provided its contents. - * - * @return short description - */ - public String getShortDescription() { + return new String[]{result}; + } - return "Document: \"" + _property.getName() + "\" size = " + getSize(); - } + /** + * Get an Iterator of objects, some of which may implement POIFSViewable + * + * @return an Iterator; may not be null, but may have an empty back end + * store + */ + public Iterator getViewableIterator() { + return emptyIterator(); + } + + /** + * Give viewers a hint as to whether to call getViewableArray or + * getViewableIterator + * + * @return true if a viewer should call getViewableArray, + * false if a viewer should call getViewableIterator + */ + public boolean preferArray() { + return true; + } + + /** + * Provides a short description of the object, to be used when a + * POIFSViewable object has not provided its contents. + * + * @return short description + */ + public String getShortDescription() { + + return "Document: \"" + _property.getName() + "\" size = " + getSize(); + } } diff --git a/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java b/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java index 3f5e8f4507..343aba35d3 100644 --- a/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java +++ b/poi/src/main/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java @@ -61,7 +61,8 @@ import org.apache.poi.util.Internal; public class POIFSFileSystem extends BlockStore implements POIFSViewable, Closeable { //arbitrarily selected; may need to increase - private static final int MAX_RECORD_LENGTH = 100_000; + private static final int DEFAULT_MAX_RECORD_LENGTH = 100_000; + static int MAX_RECORD_LENGTH = DEFAULT_MAX_RECORD_LENGTH; private static final Logger LOG = LogManager.getLogger(POIFSFileSystem.class); @@ -94,6 +95,20 @@ public class POIFSFileSystem extends BlockStore private POIFSBigBlockSize bigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS; + /** + * @param length the max record length allowed for POIFSFileSystem + */ + public static void setMaxRecordLength(int length) { + MAX_RECORD_LENGTH = length; + } + + /** + * @return the max record length allowed for POIFSFileSystem + */ + public static int getMaxRecordLength() { + return MAX_RECORD_LENGTH; + } + private POIFSFileSystem(boolean newFS) { _header = new HeaderBlock(bigBlockSize); _property_table = new PropertyTable(_header); diff --git a/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java b/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java index bfb4caabf5..bdaccb7d6c 100644 --- a/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java +++ b/poi/src/main/java/org/apache/poi/ss/extractor/EmbeddedExtractor.java @@ -68,14 +68,14 @@ public class EmbeddedExtractor implements Iterable { private static final String CONTENT_TYPE_XLS = "application/vnd.ms-excel"; /** - * @param length the max length allowed for EmbeddedExtractor + * @param length the max record length allowed for EmbeddedExtractor */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for EmbeddedExtractor + * @return the max record length allowed for EmbeddedExtractor */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java b/poi/src/main/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java index a44379c37a..e7949ecc92 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java @@ -57,14 +57,14 @@ final class FunctionMetadataReader { private static final Set DIGIT_ENDING_FUNCTION_NAMES_SET = new HashSet<>(Arrays.asList(DIGIT_ENDING_FUNCTION_NAMES)); /** - * @param length the max length allowed for FunctionMetadataReader + * @param length the max record length allowed for FunctionMetadataReader */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for FunctionMetadataReader + * @return the max record length allowed for FunctionMetadataReader */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/util/LZWDecompresser.java b/poi/src/main/java/org/apache/poi/util/LZWDecompresser.java index 1fc9573ee0..a883ee3abb 100644 --- a/poi/src/main/java/org/apache/poi/util/LZWDecompresser.java +++ b/poi/src/main/java/org/apache/poi/util/LZWDecompresser.java @@ -61,14 +61,14 @@ public abstract class LZWDecompresser { private final boolean positionIsBigEndian; /** - * @param length the max length allowed for LZWDecompresser + * @param length the max record length allowed for LZWDecompresser */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for LZWDecompresser + * @return the max record length allowed for LZWDecompresser */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH; diff --git a/poi/src/main/java/org/apache/poi/util/StringUtil.java b/poi/src/main/java/org/apache/poi/util/StringUtil.java index c805dd4517..fb9d61b43d 100644 --- a/poi/src/main/java/org/apache/poi/util/StringUtil.java +++ b/poi/src/main/java/org/apache/poi/util/StringUtil.java @@ -37,14 +37,14 @@ public final class StringUtil { public static final Charset WIN_1252 = Charset.forName("cp1252"); /** - * @param length the max length allowed for StringUtil + * @param length the max record length allowed for StringUtil */ public static void setMaxRecordLength(int length) { MAX_RECORD_LENGTH = length; } /** - * @return the max length allowed for StringUtil + * @return the max record length allowed for StringUtil */ public static int getMaxRecordLength() { return MAX_RECORD_LENGTH;