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:
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user