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