diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java
index 260712ad1..104195599 100644
--- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java
+++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java
@@ -40,6 +40,7 @@ import org.apache.poi.poifs.filesystem.DocumentNode;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
+import org.apache.poi.util.HexDump;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.RLEDecompressingInputStream;
@@ -179,13 +180,31 @@ public class VBAMacroReader implements Closeable {
private static void trySkip(InputStream in, long n) throws IOException {
long skippedBytes = in.skip(n);
if (skippedBytes != n) {
- throw new IOException(
- "Skipped only " + skippedBytes + " while trying to skip " + n + " bytes. " +
- " This should never happen.");
+ if (skippedBytes < 0) {
+ throw new IOException(
+ "Tried skipping " + n + " bytes, but no bytes were skipped. "
+ + "The end of the stream has been reached or the stream is closed.");
+ } else {
+ throw new IOException(
+ "Tried skipping " + n + " bytes, but only " + skippedBytes + " bytes were skipped. "
+ + "This should never happen.");
+ }
}
}
+
+ // Constants from MS-OVBA: https://msdn.microsoft.com/en-us/library/office/cc313094(v=office.12).aspx
+ private static final int EOF = -1;
+ private static final int VERSION_INDEPENDENT_TERMINATOR = 0x0010;
+ private static final int VERSION_DEPENDENT_TERMINATOR = 0x002B;
+ private static final int PROJECTVERSION = 0x0009;
+ private static final int PROJECTCODEPAGE = 0x0003;
+ private static final int STREAMNAME = 0x001A;
+ private static final int MODULEOFFSET = 0x0031;
+ private static final int MODULETYPE_PROCEDURAL = 0x0021;
+ private static final int MODULETYPE_DOCUMENT_CLASS_OR_DESIGNER = 0x0022;
+ private static final int PROJECTLCID = 0x0002;
- /*
+ /**
* Reads VBA Project modules from a VBA Project directory located at
* macroDir into modules.
*
@@ -203,50 +222,56 @@ public class VBAMacroReader implements Closeable {
// process DIR
RLEDecompressingInputStream in = new RLEDecompressingInputStream(dis);
String streamName = null;
- while (true) {
- int id = in.readShort();
- if (id == -1 || id == 0x0010) {
- break; // EOF or TERMINATOR
- }
- int len = in.readInt();
- switch (id) {
- case 0x0009: // PROJECTVERSION
- trySkip(in, 6);
- break;
- case 0x0003: // PROJECTCODEPAGE
- int codepage = in.readShort();
- modules.charset = Charset.forName("Cp" + codepage);
- break;
- case 0x001A: // STREAMNAME
- streamName = readString(in, len, modules.charset);
- break;
- case 0x0031: // MODULEOFFSET
- int moduleOffset = in.readInt();
- Module module = modules.get(streamName);
- if (module != null) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- RLEDecompressingInputStream stream = new RLEDecompressingInputStream(new ByteArrayInputStream(
- module.buf, moduleOffset, module.buf.length - moduleOffset));
- IOUtils.copy(stream, out);
- stream.close();
- out.close();
- module.buf = out.toByteArray();
- } else {
- module = new Module();
- module.offset = moduleOffset;
- modules.put(streamName, module);
+ int recordId = 0;
+ try {
+ while (true) {
+ recordId = in.readShort();
+ if (EOF == recordId
+ || VERSION_INDEPENDENT_TERMINATOR == recordId) {
+ break;
}
- break;
- default:
- try {
- trySkip(in, len);
- } catch (final IOException e) {
- throw new IOException("Error occurred while reading section id " + id, e);
+ int recordLength = in.readInt();
+ switch (recordId) {
+ case PROJECTVERSION:
+ trySkip(in, 6);
+ break;
+ case PROJECTCODEPAGE:
+ int codepage = in.readShort();
+ modules.charset = Charset.forName("Cp" + codepage);
+ break;
+ case STREAMNAME:
+ streamName = readString(in, recordLength, modules.charset);
+ break;
+ case MODULEOFFSET:
+ int moduleOffset = in.readInt();
+ Module module = modules.get(streamName);
+ if (module != null) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ RLEDecompressingInputStream stream = new RLEDecompressingInputStream(new ByteArrayInputStream(
+ module.buf, moduleOffset, module.buf.length - moduleOffset));
+ IOUtils.copy(stream, out);
+ stream.close();
+ out.close();
+ module.buf = out.toByteArray();
+ } else {
+ module = new Module();
+ module.offset = moduleOffset;
+ modules.put(streamName, module);
+ }
+ break;
+ default:
+ trySkip(in, recordLength);
+ break;
}
- break;
}
+ } catch (final IOException e) {
+ throw new IOException(
+ "Error occurred while reading macros at section id "
+ + recordId + " (" + HexDump.shortToHex(recordId) + ")", e);
+ }
+ finally {
+ in.close();
}
- in.close();
} else if (!startsWithIgnoreCase(name, "__SRP")
&& !startsWithIgnoreCase(name, "_VBA_PROJECT")) {
// process module, skip __SRP and _VBA_PROJECT since these do not contain macros