git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1894453 13f79535-47bb-0310-9956-ffa450edef68tags/REL_5_2_0
@@ -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<XSLFShape, XSLFTextParagraph> { | |||
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<XSLFSlide> _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()); | |||
} |
@@ -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; |
@@ -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; | |||
@@ -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. |
@@ -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 | |||
*/ |
@@ -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 |
@@ -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, |
@@ -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); |
@@ -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 | |||
* <p> | |||
* 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 | |||
* <p> | |||
* 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; | |||
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] <filename>"); | |||
return; | |||
/** | |||
* 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; | |||
/** | |||
* @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); | |||
/** | |||
* 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] <filename>"); | |||
return; | |||
} | |||
String filename = args[0]; | |||
if (args.length > 1) { | |||
filename = args[1]; | |||
} | |||
if(args.length > 1) { | |||
if(args[0].equalsIgnoreCase("-escher")) { | |||
foo.setDDFEscher(true); | |||
} else { | |||
foo.setBasicEscher(true); | |||
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); | |||
} | |||
} | |||
foo.printDump(); | |||
} | |||
} | |||
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); | |||
} | |||
} | |||
/** | |||
* 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: | |||
// <little endian 2 byte "info"> | |||
// <little endian 2 byte "type"> | |||
// <little endian 4 byte "length"> | |||
// If it has a zero length, following it will be another record | |||
// <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz> | |||
// If it has a length, depending on its type it may have children or data | |||
// If it has children, these will follow straight away | |||
// <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>> | |||
// If it has data, this will come straigh after, and run for the length | |||
// <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd> | |||
// 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]; | |||
/** | |||
* Control dumping of any Escher records found - should our built in | |||
* basic groker be used? | |||
*/ | |||
public void setBasicEscher(boolean grok) { | |||
basicEscher = grok; | |||
ddfEscher = !(grok); | |||
} | |||
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)); | |||
public void printDump() throws IOException { | |||
// The format of records in a powerpoint file are: | |||
// <little endian 2 byte "info"> | |||
// <little endian 2 byte "type"> | |||
// <little endian 4 byte "length"> | |||
// If it has a zero length, following it will be another record | |||
// <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz> | |||
// If it has a length, depending on its type it may have children or data | |||
// If it has children, these will follow straight away | |||
// <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>> | |||
// If it has data, this will come straigh after, and run for the length | |||
// <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd> | |||
// 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); | |||
} | |||
// See if we know about the type of it | |||
String recordName = RecordTypes.forTypeID((short)type).name(); | |||
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]; | |||
// Jump over header, and think about going on more | |||
pos += 8; | |||
out.println(String.format(Locale.ROOT, ind+"That's a %2$s", "", recordName)); | |||
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)); | |||
// Now check if it's a container or not | |||
int container = opt & 0x0f; | |||
// See if we know about the type of it | |||
String recordName = RecordTypes.forTypeID((short) type).name(); | |||
// BinaryTagData seems to contain records, but it | |||
// isn't tagged as doing so. Try stepping in anyway | |||
if(type == 5003L && opt == 0L) { | |||
container = 0x0f; | |||
} | |||
// Jump over header, and think about going on more | |||
pos += 8; | |||
out.println(String.format(Locale.ROOT, ind + "That's a %2$s", "", recordName)); | |||
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); | |||
// 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); | |||
} | |||
} else { | |||
// General container record handling code | |||
walkTree((depth+2),pos,(int)len); | |||
} | |||
} | |||
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; } | |||
/** | |||
* 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"; | |||
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); | |||
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); | |||
// 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(); | |||
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)); | |||
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)); | |||
} | |||
// 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)); | |||
// 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 ); | |||
} | |||
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 !")); | |||
// 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 !")); | |||
} | |||
} | |||
} | |||
// 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 ); | |||
// 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; | |||
} | |||
// 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); | |||
// 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; | |||
} | |||
// Move on to the next one, if we're not at the end yet | |||
if(len >= 8) { | |||
walkEscherDDF(indent, pos, len ); | |||
// Move on to the next one, if we're not at the end yet | |||
if (len >= 8) { | |||
walkEscherDDF(indent, pos, len); | |||
} | |||
} | |||
} | |||
/** | |||
* 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; } | |||
/** | |||
* 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; | |||
} | |||
final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s"; | |||
final String ind = (indent == 0) ? "%1$s" : "%1$" + indent + "s"; | |||
long type = LittleEndian.getUShort(docstream,pos+2); | |||
long atomlen = LittleEndian.getUInt(docstream,pos+4); | |||
long type = LittleEndian.getUShort(docstream, pos + 2); | |||
long atomlen = LittleEndian.getUInt(docstream, pos + 4); | |||
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 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)); | |||
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); | |||
out.println(); | |||
} | |||
// 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(); | |||
} | |||
// Blank line before next entry | |||
out.println(); | |||
// 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); | |||
} | |||
// 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); | |||
// Keep going if not yet at end | |||
if (atomlen < len) { | |||
int atomleni = (int) atomlen; | |||
walkEscherBasic(indent, pos + atomleni + 8, len - atomleni - 8); | |||
} | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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. | |||
*/ |
@@ -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; |
@@ -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<IndentProp> 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. | |||
*/ |
@@ -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. |
@@ -85,7 +85,8 @@ public final class HSLFSlideShow extends POIDocument implements SlideShow<HSLFSh | |||
private static final Logger LOG = LogManager.getLogger(HSLFSlideShow.class); | |||
//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; | |||
enum LoadSavePhase { | |||
INIT, LOADED | |||
@@ -112,6 +113,19 @@ public final class HSLFSlideShow extends POIDocument implements SlideShow<HSLFSh | |||
private final List<HSLFNotes> _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 |
@@ -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. | |||
*/ |
@@ -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 | |||
*/ |
@@ -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];//???? | |||
// zero terminated string that records name of font, cuurently not | |||
// supporting Extended chars | |||
private char [] _xszFfn; | |||
// 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; | |||
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; | |||
// extra facilitator members | |||
private int _xszFfnLength; | |||
/** | |||
* @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; | |||
// 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; | |||
} | |||
} | |||
public int get_cbFfnM1() { | |||
return _cbFfnM1; | |||
} | |||
public short getWeight() { | |||
return _wWeight; | |||
} | |||
public byte getChs() { | |||
return _chs; | |||
} | |||
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 int get_cbFfnM1() | |||
{ | |||
return _cbFfnM1; | |||
} | |||
public short getWeight() | |||
{ | |||
return _wWeight; | |||
} | |||
public byte getChs() | |||
{ | |||
return _chs; | |||
} | |||
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; | |||
} | |||
public void set_cbFfnM1(int _cbFfnM1) { | |||
this._cbFfnM1 = _cbFfnM1; | |||
} | |||
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; | |||
// 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; | |||
} | |||
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 | |||
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 |
@@ -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); | |||
} | |||
} |
@@ -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; |
@@ -37,7 +37,8 @@ import org.apache.poi.util.Removal; | |||
public final class EscherArrayProperty extends EscherComplexProperty implements Iterable<byte[]> { | |||
// 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. |
@@ -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; |
@@ -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; |
@@ -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; |
@@ -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; |
@@ -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; |
@@ -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; |
@@ -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<EscherRecord> _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) { |
@@ -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; |
@@ -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; |
@@ -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; |
@@ -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); | |||
} |
@@ -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); |
@@ -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!) | |||
*/ |
@@ -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); |
@@ -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; |
@@ -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() { | |||
} |
@@ -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 |
@@ -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) { |
@@ -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 | |||
*/ |
@@ -40,139 +40,133 @@ import org.apache.poi.util.IOUtils; | |||
*/ | |||
public final class POIFSDocument implements POIFSViewable, Iterable<ByteBuffer> { | |||
//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; | |||
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 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; | |||
// Store it | |||
int length = store(stream); | |||
// 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; | |||
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); | |||
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, 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; | |||
} | |||
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; | |||
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 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; | |||
// Store it | |||
int length = store(stream); | |||
// 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; | |||
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); | |||
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<ByteBuffer> iterator() { | |||
@@ -180,83 +174,83 @@ public final class POIFSDocument implements POIFSViewable, Iterable<ByteBuffer> | |||
} | |||
Iterator<ByteBuffer> getBlockIterator() { | |||
return (getSize() > 0 ? _stream : Collections.<ByteBuffer>emptyList()).iterator(); | |||
return (getSize() > 0 ? _stream : Collections.<ByteBuffer>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 the instance's DocumentProperty | |||
*/ | |||
DocumentProperty getDocumentProperty() { | |||
return _property; | |||
} | |||
/** | |||
* @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 the instance's DocumentProperty | |||
*/ | |||
DocumentProperty getDocumentProperty() { | |||
return _property; | |||
} | |||
/** | |||
* 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 = "<NO DATA>"; | |||
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 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<Object> getViewableIterator() { | |||
return emptyIterator(); | |||
} | |||
/** | |||
* Give viewers a hint as to whether to call getViewableArray or | |||
* getViewableIterator | |||
* | |||
* @return <code>true</code> if a viewer should call getViewableArray, | |||
* <code>false</code> 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(); | |||
} | |||
/** | |||
* 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 = "<NO DATA>"; | |||
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; | |||
} | |||
result = HexDump.dump(data, 0, 0); | |||
} | |||
return new 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<Object> getViewableIterator() { | |||
return emptyIterator(); | |||
} | |||
/** | |||
* Give viewers a hint as to whether to call getViewableArray or | |||
* getViewableIterator | |||
* | |||
* @return <code>true</code> if a viewer should call getViewableArray, | |||
* <code>false</code> 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(); | |||
} | |||
} |
@@ -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); |
@@ -68,14 +68,14 @@ public class EmbeddedExtractor implements Iterable<EmbeddedExtractor> { | |||
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; |
@@ -57,14 +57,14 @@ final class FunctionMetadataReader { | |||
private static final Set<String> 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; |
@@ -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; |
@@ -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; |