whitespace and +svn:eol-style=native

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1751019 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2016-07-02 04:28:41 +00:00
parent c985706686
commit 6645d8a80f

@ -1,174 +1,174 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.macros; package org.apache.poi.poifs.macros;
import static org.apache.poi.util.StringUtil.startsWithIgnoreCase; import static org.apache.poi.util.StringUtil.startsWithIgnoreCase;
import static org.apache.poi.util.StringUtil.endsWithIgnoreCase; import static org.apache.poi.util.StringUtil.endsWithIgnoreCase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PushbackInputStream; import java.io.PushbackInputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.DocumentNode; import org.apache.poi.poifs.filesystem.DocumentNode;
import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException; import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.util.RLEDecompressingInputStream; import org.apache.poi.util.RLEDecompressingInputStream;
/** /**
* Finds all VBA Macros in an office file (OLE2/POIFS and OOXML/OPC), * Finds all VBA Macros in an office file (OLE2/POIFS and OOXML/OPC),
* and returns them. * and returns them.
* *
* @since 3.15-beta2 * @since 3.15-beta2
*/ */
public class VBAMacroReader implements Closeable { public class VBAMacroReader implements Closeable {
protected static final String VBA_PROJECT_OOXML = "vbaProject.bin"; protected static final String VBA_PROJECT_OOXML = "vbaProject.bin";
protected static final String VBA_PROJECT_POIFS = "VBA"; protected static final String VBA_PROJECT_POIFS = "VBA";
private NPOIFSFileSystem fs; private NPOIFSFileSystem fs;
public VBAMacroReader(InputStream rstream) throws IOException { public VBAMacroReader(InputStream rstream) throws IOException {
PushbackInputStream stream = new PushbackInputStream(rstream, 8); PushbackInputStream stream = new PushbackInputStream(rstream, 8);
byte[] header8 = IOUtils.peekFirst8Bytes(stream); byte[] header8 = IOUtils.peekFirst8Bytes(stream);
if (NPOIFSFileSystem.hasPOIFSHeader(header8)) { if (NPOIFSFileSystem.hasPOIFSHeader(header8)) {
fs = new NPOIFSFileSystem(stream); fs = new NPOIFSFileSystem(stream);
} else { } else {
openOOXML(stream); openOOXML(stream);
} }
} }
public VBAMacroReader(File file) throws IOException { public VBAMacroReader(File file) throws IOException {
try { try {
this.fs = new NPOIFSFileSystem(file); this.fs = new NPOIFSFileSystem(file);
} catch (OfficeXmlFileException e) { } catch (OfficeXmlFileException e) {
openOOXML(new FileInputStream(file)); openOOXML(new FileInputStream(file));
} }
} }
public VBAMacroReader(NPOIFSFileSystem fs) { public VBAMacroReader(NPOIFSFileSystem fs) {
this.fs = fs; this.fs = fs;
} }
private void openOOXML(InputStream zipFile) throws IOException { private void openOOXML(InputStream zipFile) throws IOException {
ZipInputStream zis = new ZipInputStream(zipFile); ZipInputStream zis = new ZipInputStream(zipFile);
ZipEntry zipEntry; ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) { while ((zipEntry = zis.getNextEntry()) != null) {
if (endsWithIgnoreCase(zipEntry.getName(), VBA_PROJECT_OOXML)) { if (endsWithIgnoreCase(zipEntry.getName(), VBA_PROJECT_OOXML)) {
try { try {
// Make a NPOIFS from the contents, and close the stream // Make a NPOIFS from the contents, and close the stream
this.fs = new NPOIFSFileSystem(zis); this.fs = new NPOIFSFileSystem(zis);
return; return;
} catch (IOException e) { } catch (IOException e) {
// Tidy up // Tidy up
zis.close(); zis.close();
// Pass on // Pass on
throw e; throw e;
} }
} }
} }
zis.close(); zis.close();
throw new IllegalArgumentException("No VBA project found"); throw new IllegalArgumentException("No VBA project found");
} }
public void close() throws IOException { public void close() throws IOException {
fs.close(); fs.close();
fs = null; fs = null;
} }
/** /**
* Reads all macros from all modules of the opened office file. * Reads all macros from all modules of the opened office file.
* @return All the macros and their contents * @return All the macros and their contents
* *
* @since 3.15-beta2 * @since 3.15-beta2
*/ */
public Map<String, String> readMacros() throws IOException { public Map<String, String> readMacros() throws IOException {
final ModuleMap modules = new ModuleMap(); final ModuleMap modules = new ModuleMap();
findMacros(fs.getRoot(), modules); findMacros(fs.getRoot(), modules);
Map<String, String> moduleSources = new HashMap<String, String>(); Map<String, String> moduleSources = new HashMap<String, String>();
for (Map.Entry<String, Module> entry : modules.entrySet()) { for (Map.Entry<String, Module> entry : modules.entrySet()) {
Module module = entry.getValue(); Module module = entry.getValue();
if (module.buf != null && module.buf.length > 0) { // Skip empty modules if (module.buf != null && module.buf.length > 0) { // Skip empty modules
moduleSources.put(entry.getKey(), new String(module.buf, modules.charset)); moduleSources.put(entry.getKey(), new String(module.buf, modules.charset));
} }
} }
return moduleSources; return moduleSources;
} }
protected static class Module { protected static class Module {
Integer offset; Integer offset;
byte[] buf; byte[] buf;
} }
protected static class ModuleMap extends HashMap<String, Module> { protected static class ModuleMap extends HashMap<String, Module> {
Charset charset = Charset.forName("Cp1252"); // default charset Charset charset = Charset.forName("Cp1252"); // default charset
} }
/** /**
* Recursively traverses directory structure rooted at <tt>dir</tt>. * Recursively traverses directory structure rooted at <tt>dir</tt>.
* For each macro module that is found, the module's name and code are * For each macro module that is found, the module's name and code are
* added to <tt>modules<tt>. * added to <tt>modules<tt>.
* *
* @param dir * @param dir
* @param modules * @param modules
* @throws IOException * @throws IOException
* @since 3.15-beta2 * @since 3.15-beta2
*/ */
protected void findMacros(DirectoryNode dir, ModuleMap modules) throws IOException { protected void findMacros(DirectoryNode dir, ModuleMap modules) throws IOException {
if (VBA_PROJECT_POIFS.equalsIgnoreCase(dir.getName())) { if (VBA_PROJECT_POIFS.equalsIgnoreCase(dir.getName())) {
// VBA project directory, process // VBA project directory, process
readMacros(dir, modules); readMacros(dir, modules);
} else { } else {
// Check children // Check children
for (Entry child : dir) { for (Entry child : dir) {
if (child instanceof DirectoryNode) { if (child instanceof DirectoryNode) {
findMacros((DirectoryNode)child, modules); findMacros((DirectoryNode)child, modules);
} }
} }
} }
} }
/** /**
* Read <tt>length</tt> bytes of MBCS (multi-byte character set) characters from the stream * Read <tt>length</tt> bytes of MBCS (multi-byte character set) characters from the stream
* *
* @param stream the inputstream to read from * @param stream the inputstream to read from
* @param length number of bytes to read from stream * @param length number of bytes to read from stream
* @param charset the character set encoding of the bytes in the stream * @param charset the character set encoding of the bytes in the stream
* @return a java String in the supplied character set * @return a java String in the supplied character set
* @throws IOException * @throws IOException
*/ */
private static String readString(InputStream stream, int length, Charset charset) throws IOException { private static String readString(InputStream stream, int length, Charset charset) throws IOException {
byte[] buffer = new byte[length]; byte[] buffer = new byte[length];
int count = stream.read(buffer); int count = stream.read(buffer);
return new String(buffer, 0, count, charset); return new String(buffer, 0, count, charset);
} }
/** /**
@ -183,87 +183,87 @@ public class VBAMacroReader implements Closeable {
"Skipped only " + skippedBytes + " while trying to skip " + n + " bytes. " + "Skipped only " + skippedBytes + " while trying to skip " + n + " bytes. " +
" This should never happen."); " This should never happen.");
} }
} }
/* /*
* Reads VBA Project modules from a VBA Project directory located at * Reads VBA Project modules from a VBA Project directory located at
* <tt>macroDir</tt> into <tt>modules</tt>. * <tt>macroDir</tt> into <tt>modules</tt>.
* *
* @since 3.15-beta2 * @since 3.15-beta2
*/ */
protected void readMacros(DirectoryNode macroDir, ModuleMap modules) throws IOException { protected void readMacros(DirectoryNode macroDir, ModuleMap modules) throws IOException {
for (Entry entry : macroDir) { for (Entry entry : macroDir) {
if (! (entry instanceof DocumentNode)) { continue; } if (! (entry instanceof DocumentNode)) { continue; }
String name = entry.getName(); String name = entry.getName();
DocumentNode document = (DocumentNode)entry; DocumentNode document = (DocumentNode)entry;
DocumentInputStream dis = new DocumentInputStream(document); DocumentInputStream dis = new DocumentInputStream(document);
if ("dir".equalsIgnoreCase(name)) { if ("dir".equalsIgnoreCase(name)) {
// process DIR // process DIR
RLEDecompressingInputStream in = new RLEDecompressingInputStream(dis); RLEDecompressingInputStream in = new RLEDecompressingInputStream(dis);
String streamName = null; String streamName = null;
while (true) { while (true) {
int id = in.readShort(); int id = in.readShort();
if (id == -1 || id == 0x0010) { if (id == -1 || id == 0x0010) {
break; // EOF or TERMINATOR break; // EOF or TERMINATOR
} }
int len = in.readInt(); int len = in.readInt();
switch (id) { switch (id) {
case 0x0009: // PROJECTVERSION case 0x0009: // PROJECTVERSION
trySkip(in, 6); trySkip(in, 6);
break; break;
case 0x0003: // PROJECTCODEPAGE case 0x0003: // PROJECTCODEPAGE
int codepage = in.readShort(); int codepage = in.readShort();
modules.charset = Charset.forName("Cp" + codepage); modules.charset = Charset.forName("Cp" + codepage);
break; break;
case 0x001A: // STREAMNAME case 0x001A: // STREAMNAME
streamName = readString(in, len, modules.charset); streamName = readString(in, len, modules.charset);
break; break;
case 0x0031: // MODULEOFFSET case 0x0031: // MODULEOFFSET
int moduleOffset = in.readInt(); int moduleOffset = in.readInt();
Module module = modules.get(streamName); Module module = modules.get(streamName);
if (module != null) { if (module != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
RLEDecompressingInputStream stream = new RLEDecompressingInputStream(new ByteArrayInputStream( RLEDecompressingInputStream stream = new RLEDecompressingInputStream(new ByteArrayInputStream(
module.buf, moduleOffset, module.buf.length - moduleOffset)); module.buf, moduleOffset, module.buf.length - moduleOffset));
IOUtils.copy(stream, out); IOUtils.copy(stream, out);
stream.close(); stream.close();
out.close(); out.close();
module.buf = out.toByteArray(); module.buf = out.toByteArray();
} else { } else {
module = new Module(); module = new Module();
module.offset = moduleOffset; module.offset = moduleOffset;
modules.put(streamName, module); modules.put(streamName, module);
} }
break; break;
default: default:
trySkip(in, len); trySkip(in, len);
break; break;
} }
} }
in.close(); in.close();
} else if (!startsWithIgnoreCase(name, "__SRP") } else if (!startsWithIgnoreCase(name, "__SRP")
&& !startsWithIgnoreCase(name, "_VBA_PROJECT")) { && !startsWithIgnoreCase(name, "_VBA_PROJECT")) {
// process module, skip __SRP and _VBA_PROJECT since these do not contain macros // process module, skip __SRP and _VBA_PROJECT since these do not contain macros
Module module = modules.get(name); Module module = modules.get(name);
final InputStream in; final InputStream in;
// TODO Refactor this to fetch dir then do the rest // TODO Refactor this to fetch dir then do the rest
if (module == null) { if (module == null) {
// no DIR stream with offsets yet, so store the compressed bytes for later // no DIR stream with offsets yet, so store the compressed bytes for later
module = new Module(); module = new Module();
modules.put(name, module); modules.put(name, module);
in = dis; in = dis;
} else { } else {
// we know the offset already, so decompress immediately on-the-fly // we know the offset already, so decompress immediately on-the-fly
dis.skip(module.offset); dis.skip(module.offset);
in = new RLEDecompressingInputStream(dis); in = new RLEDecompressingInputStream(dis);
} }
final ByteArrayOutputStream out = new ByteArrayOutputStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copy(in, out); IOUtils.copy(in, out);
in.close(); in.close();
out.close(); out.close();
module.buf = out.toByteArray(); module.buf = out.toByteArray();
} }
} }
} }
} }