Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -0,0 +1,269 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.*;
import java.util.*;
public class ClassModuleTree {
private FileNode root = new FileNode(null, "");
public ClassModuleTree() {
}
public ClassModuleTree(ResourceFile treeFile) throws IOException {
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(treeFile.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
String[] split = line.split(" ");
String path = split[0];
String module = split[1].equals("null") ? null : split[1];
addNode(path, module);
}
}
}
public void addNode(String path, String moduleName) {
String[] split = path.split("/");
FileNode file = root;
for (String string : split) {
file = file.createNode(string);
}
file.setModule(moduleName);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
ClassModuleTree other = (ClassModuleTree) obj;
return root.equals(other.root);
}
@Override
public int hashCode() {
return root.hashCode();
}
public void trim() {
root.trim();
}
public void printRecursively() {
printRecursively(root);
}
public void saveFile(File outputFile) throws IOException {
try (FileWriter writer = new FileWriter(outputFile)) {
List<FileNode> children = root.getChildren();
for (FileNode child : children) {
writeRecursively(writer, child);
}
}
}
private void writeRecursively(FileWriter writer, FileNode node) throws IOException {
writer.write(node.getPath());
writer.write(" ");
writer.write(node.module == null ? "null" : node.module);
writer.write("\n");
List<FileNode> children = node.getChildren();
for (FileNode child : children) {
writeRecursively(writer, child);
}
}
private void printRecursively(FileNode node) {
System.out.println(node.getPath() + " : " + node.module);
List<FileNode> children = node.getChildren();
for (FileNode child : children) {
printRecursively(child);
}
}
public int getNodeCount() {
return root.getCount();
}
static class FileNode implements Comparable<FileNode> {
private FileNode parent;
private Map<String, FileNode> children;
private String module;
private String name;
public FileNode(FileNode parent, String name) {
this.parent = parent;
this.name = name;
}
public int getCount() {
int count = 1;
if (children != null) {
List<FileNode> childList = getChildren();
for (FileNode child : childList) {
count += child.getCount();
}
}
return count;
}
public String trim() {
if (module != null) {
return module;
}
if (children == null) {
return null;
}
Set<String> set = new HashSet<String>();
for (FileNode node : children.values()) {
set.add(node.trim());
}
if (set.size() == 1) {
module = set.iterator().next();
if (module != null) {
children = null; // trim the children
}
}
return module;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((children == null) ? 0 : children.hashCode());
result = prime * result + ((module == null) ? 0 : module.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FileNode other = (FileNode) obj;
if (children == null) {
if (other.children != null)
return false;
}
else if (!children.equals(other.children))
return false;
if (module == null) {
if (other.module != null)
return false;
}
else if (!module.equals(other.module))
return false;
if (name == null) {
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
public void setModule(String moduleName) {
this.module = moduleName;
}
public String getPath() {
if (parent == null) {
return "";
}
String parentPath = parent.getPath();
if (parentPath.length() == 0) {
return name;
}
return parentPath + "/" + name;
}
public FileNode createNode(String nodeName) {
if (children == null) {
children = new HashMap<String, ClassModuleTree.FileNode>();
}
FileNode child = children.get(nodeName);
if (child == null) {
child = new FileNode(this, nodeName);
children.put(nodeName, child);
}
return child;
}
public List<FileNode> getChildren() {
if (children == null) {
return new ArrayList<FileNode>();
}
return new ArrayList<FileNode>(children.values());
}
@Override
public int compareTo(FileNode o) {
return name.compareTo(o.name);
}
public FileNode getChild(String childName) {
if (children == null) {
return null;
}
return children.get(childName);
}
}
public String getModuleName(String className) {
String[] split = className.split("/");
FileNode node = root;
for (String name : split) {
node = node.getChild(name);
if (node == null) {
return null;
}
if (node.module != null) {
return node.module;
}
}
return null;
}
public static void main(String[] args) {
ClassModuleTree tree = new ClassModuleTree();
tree.addNode("a/b/c", "module1");
tree.addNode("a/b/d", "module1");
tree.addNode("a/b/e", "module1");
tree.addNode("a/x/a", "module2");
tree.addNode("a/x/b", "module3");
tree.printRecursively();
System.out.println("------");
tree.trim();
tree.printRecursively();
}
}

View file

@ -0,0 +1,208 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
//
public class FileResource implements Resource {
private File file;
public FileResource(File file) {
this.file = file;
}
@Override
public Resource getResource(String childPath) {
return new FileResource(new File(file, childPath));
}
@Override
public String getAbsolutePath() {
return file.getAbsolutePath();
}
@Override
public ResourceFile[] listFiles() {
File[] listFiles = file.listFiles();
if (listFiles == null) {
return null;
}
ResourceFile[] resourceFiles = new ResourceFile[listFiles.length];
for (int i = 0; i < listFiles.length; i++) {
resourceFiles[i] = new ResourceFile(new FileResource(listFiles[i]));
}
return resourceFiles;
}
@Override
public ResourceFile[] listFiles(ResourceFileFilter filter) {
File[] listFiles = file.listFiles();
if (listFiles == null) {
return null;
}
List<ResourceFile> fileList = new ArrayList<>();
for (File listFile : listFiles) {
ResourceFile resourceFile = new ResourceFile(new FileResource(listFile));
if (filter.accept(resourceFile)) {
fileList.add(resourceFile);
}
}
return fileList.toArray(new ResourceFile[fileList.size()]);
}
@Override
public String getName() {
return file.getName();
}
@Override
public boolean isDirectory() {
return file.isDirectory();
}
@Override
public URL toURL() throws MalformedURLException {
return file.toURI().toURL();
}
@Override
public InputStream getInputStream() throws FileNotFoundException {
return new FileInputStream(file);
}
@Override
public OutputStream getOutputStream() throws FileNotFoundException {
return new FileOutputStream(file);
}
@Override
public FileResource getParent() {
File parent = file.getParentFile();
if (parent == null) {
parent = file.getAbsoluteFile().getParentFile();
if (parent == null) {
return null;
}
}
return new FileResource(parent);
}
@Override
public long lastModified() {
return file.lastModified();
}
@Override
public int hashCode() {
return file.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
return file.equals(((FileResource) obj).file);
}
@Override
public boolean delete() {
return file.delete();
}
@Override
public boolean exists() {
return file.exists();
}
@Override
public File getFile() {
return file;
}
@Override
public File getResourceAsFile(ResourceFile resourceFile) {
return file;
}
@Override
public long length() {
return file.length();
}
@Override
public String getCanonicalPath() throws IOException {
return file.getCanonicalPath();
}
@Override
public boolean isFile() {
return file.isFile();
}
@Override
public Resource getCanonicalResource() {
try {
return new FileResource(file.getCanonicalFile());
}
catch (IOException e) {
return this;
}
}
@Override
public boolean canWrite() {
return file.canWrite();
}
@Override
public boolean mkdir() {
return file.mkdir();
}
@Override
public String toString() {
return getAbsolutePath();
}
@Override
public File getFileSystemRoot() {
File testFile = file;
File parentFile = testFile.getParentFile();
while (parentFile != null) {
testFile = parentFile;
parentFile = testFile.getParentFile();
}
return testFile;
}
@Override
public URI toURI() {
return file.toURI();
}
}

View file

@ -0,0 +1,72 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.File;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import ghidra.util.exception.AssertException;
public class GClassLoader extends URLClassLoader {
public GClassLoader(List<File> moduleDirs) {
super(findUrls(moduleDirs), ClassLoader.getSystemClassLoader());
}
private static URL[] findUrls(List<File> moduleDirs) {
List<URL> urls = new ArrayList<URL>();
for (File moduleDir : moduleDirs) {
File binDir = new File(moduleDir, "bin/");
if (binDir.exists()) {
addFileURL(urls, binDir);
}
addModuleJars(urls, new File(moduleDir, "lib"));
}
return urls.toArray(new URL[urls.size()]);
}
private static void addFileURL(List<URL> urls, File binDir) {
try {
urls.add(binDir.toURI().toURL());
}
catch (MalformedURLException e) {
throw new AssertException("Can't happen since we checked that it exists.");
}
}
private static void addModuleJars(List<URL> urls, File libDir) {
if (!libDir.isDirectory()) {
return;
}
File[] listFiles = libDir.listFiles();
if (listFiles != null) {
for (File jarFile : listFiles) {
if (isJarFile(jarFile)) {
addFileURL(urls, jarFile);
}
}
}
}
private static boolean isJarFile(File jarFile) {
return jarFile.exists() && jarFile.getName().endsWith(".jar");
}
}

View file

@ -0,0 +1,25 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.util.jar.JarEntry;
public interface JarEntryFilter {
boolean accepts(JarEntry jarEntry);
}

View file

@ -0,0 +1,124 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarEntryNode {
private final JarEntryNode parent;
private final String name;
private Map<String, JarEntryNode> childMap;
JarEntryNode(JarEntryNode parent, String name) {
this.name = name;
this.parent = parent;
}
public JarEntryNode getNode(String childName) {
if (childMap == null) {
return null;
}
if (childName.equals(".")) {
return this;
}
if (childName.equals("..")) {
return parent;
}
return childMap.get(childName);
}
JarEntryNode createNode(String childName) {
JarEntryNode file = getNode(childName);
if (file == null) {
file = new JarEntryNode(this, childName);
if (childMap == null) {
childMap = new HashMap<String, JarEntryNode>();
}
childMap.put(childName, file);
}
return file;
}
String getPath() {
if (parent == null) {
return "";
}
String parentPath = parent.getPath();
return parentPath.length() == 0 ? name : parentPath + "/" + name;
}
public List<JarEntryNode> getChildren() {
if (childMap == null) {
return null;
}
return new ArrayList<JarEntryNode>(childMap.values());
}
public String getName() {
return name;
}
public boolean isDirectory() {
return childMap != null;
}
public boolean isFile() {
return childMap == null;
}
public InputStream getInputStream() throws IOException {
JarFile jarFile = getJarFile();
JarEntry jarEntry = jarFile.getJarEntry(getPath());
return jarFile.getInputStream(jarEntry);
}
protected JarFile getJarFile() {
return parent.getJarFile();
}
public JarEntryNode getParent() {
return parent;
}
public long lastModified() {
JarFile jarFile = getJarFile();
JarEntry jarEntry = jarFile.getJarEntry(getPath());
return jarEntry.getTime();
}
public JarEntryNode getNode(String[] path) {
JarEntryNode temp = this;
for (String childName : path) {
temp = temp.getNode(childName);
if (temp == null) {
return null;
}
}
return temp;
}
public long length() {
JarFile jarFile = getJarFile();
JarEntry jarEntry = jarFile.getJarEntry(getPath());
return jarEntry.getSize();
}
}

View file

@ -0,0 +1,98 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarEntryRootNode extends JarEntryNode {
private JarFile jarFile;
private File file;
public JarEntryRootNode(File file, JarEntryFilter filter) throws IOException {
super(null, "");
jarFile = new JarFile(file);
this.file = file;
if (filter == null) {
filter = new DefaultFilter();
}
createIndex(filter);
}
@Override
protected JarFile getJarFile() {
return jarFile;
}
protected File getFile() {
return file;
}
public URL toURL() throws MalformedURLException {
return file.toURI().toURL();
}
private void createIndex(JarEntryFilter filter) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.isDirectory()) {
continue;
}
if (filter.accepts(jarEntry)) {
addFile(jarEntry.getName());
}
}
}
private void addFile(String path) {
String[] split = path.split("/");
JarEntryNode node = this;
for (String string : split) {
node = getOrCreateNode(node, string);
}
}
private JarEntryNode getOrCreateNode(JarEntryNode node, String name) {
return node.createNode(name); // if already exists, create will return exiting node
}
private static class DefaultFilter implements JarEntryFilter {
@Override
public boolean accepts(JarEntry jarEntry) {
String name = jarEntry.getName();
if (name.endsWith(".class")) {
return false;
}
if (name.endsWith(".png")) {
return false;
}
if (name.endsWith(".gif")) {
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,300 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import utilities.util.FileUtilities;
import utility.application.ApplicationSettings;
public class JarResource implements Resource {
private final JarEntryRootNode root;
private final JarEntryNode node;
private final String name;
private final String path;
public JarResource(File jarFile, JarEntryFilter filter) throws IOException {
root = new JarEntryRootNode(jarFile, filter);
node = root;
name = root.getName();
path = root.getPath();
}
public JarResource(JarResource parent, String path) {
this.root = parent.root;
String myName;
JarEntryNode myNode;
String myPath;
path = path.replace('\\', '/');
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.length() == 0) {
myName = parent.getName();
myNode = parent.node;
myPath = parent.path;
}
else {
String[] split = path.split("/");
myName = split[split.length - 1];
myNode = parent.node == null ? null : parent.node.getNode(split);
myPath = myNode != null ? myNode.getPath() : parent.path + "/" + path;
}
this.name = myName;
this.node = myNode;
this.path = myPath;
}
JarResource(JarEntryRootNode root, JarEntryNode node) {
this.root = root;
this.node = node;
this.name = node.getName();
this.path = node.getPath();
}
@Override
public String getAbsolutePath() {
String jarPath;
try {
jarPath = root.toURL().toExternalForm();
}
catch (IOException e) {
jarPath = "file:" + root.getFile().getAbsolutePath();
}
return "jar:" + jarPath + "!/" + path;
}
@Override
public URL toURL() throws MalformedURLException {
return new URL(getAbsolutePath());
}
@Override
public URI toURI() {
try {
return new URI(getAbsolutePath());
}
catch (URISyntaxException e) {
throw new AssertException("Unexpected exception getting URI: " + this);
}
}
@Override
public ResourceFile[] listFiles() {
if (!isDirectory()) {
return null;
}
List<JarEntryNode> children = node.getChildren();
ResourceFile[] files = new ResourceFile[children.size()];
for (int i = 0; i < files.length; i++) {
files[i] = new ResourceFile(new JarResource(root, children.get(i)));
}
return files;
}
@Override
public ResourceFile[] listFiles(ResourceFileFilter filter) {
if (!isDirectory()) {
return null;
}
List<ResourceFile> fileList = new ArrayList<ResourceFile>();
List<JarEntryNode> children = node.getChildren();
for (JarEntryNode jarEntryNode : children) {
ResourceFile file = new ResourceFile(new JarResource(root, jarEntryNode));
if (filter.accept(file)) {
fileList.add(file);
}
}
return fileList.toArray(new ResourceFile[fileList.size()]);
}
@Override
public String getName() {
return name;
}
@Override
public boolean isDirectory() {
return node == null ? false : node.isDirectory();
}
@Override
public boolean isFile() {
return node == null ? false : node.isFile();
}
@Override
public InputStream getInputStream() throws IOException {
if (node == null || isDirectory()) {
throw new FileNotFoundException(path + " does not exist or is a directory");
}
return node.getInputStream();
}
@Override
public Resource getParent() {
if (node != null) {
if (node == root) {
return null;
}
return new JarResource(root, node.getParent());
}
JarResource rootResource = new JarResource(root, root);
String parentPath = getParentPath();
if (parentPath == null) {
return rootResource;
}
return new JarResource(rootResource, parentPath);
}
private String getParentPath() {
String[] split = path.split("/");
if (split.length == 1) {
return null;
}
StringBuffer buf = new StringBuffer();
buf.append(split[0]);
for (int i = 1; i < split.length; i++) {
buf.append("/");
buf.append(split[i]);
}
return buf.toString();
}
@Override
public long lastModified() {
if (node != null) {
return node.lastModified();
}
return 0;
}
@Override
public boolean delete() {
return false;
}
@Override
public boolean exists() {
return node != null;
}
@Override
public OutputStream getOutputStream() throws FileNotFoundException {
throw new FileNotFoundException("Cannot write to a file inside of a jar file!");
}
@Override
public Resource getResource(String childPath) {
if (childPath == null || childPath.length() == 0) {
return this;
}
return new JarResource(this, childPath);
}
@Override
public File getFile() {
return null;
}
@Override
public File getResourceAsFile(ResourceFile resourceFile) {
File userCopyDir = getFileCacheDirectory();
if (!userCopyDir.exists()) {
FileUtilities.mkdirs(userCopyDir);
}
File fileCopy = new File(userCopyDir, name);
if (!fileCopy.exists()) {
try {
FileUtilities.copyFile(resourceFile, fileCopy, false, null);
fileCopy.setExecutable(true);
}
catch (IOException e) {
Msg.error(this, "Resource file copy failed: " + resourceFile, e);
}
}
return fileCopy;
}
private File getFileCacheDirectory() {
File settingsDir = ApplicationSettings.getUserApplicationSettingsDirectory();
return new File(settingsDir, "jar.resource.copied.files");
}
@Override
public long length() {
return node.length();
}
@Override
public String getCanonicalPath() throws IOException {
return getAbsolutePath();
}
@Override
public Resource getCanonicalResource() {
return this;
}
@Override
public boolean canWrite() {
return false;
}
@Override
public boolean mkdir() {
return false;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
JarResource other = (JarResource) obj;
return root == other.root && name.equals(other.name) && path.equals(other.path);
}
@Override
public int hashCode() {
return path.hashCode();
}
@Override
public String toString() {
return getAbsolutePath();
}
@Override
public File getFileSystemRoot() {
return root.getFile();
}
}

View file

@ -0,0 +1,70 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.*;
//
import java.net.*;
public interface Resource {
Resource getResource(String name);
String getAbsolutePath();
ResourceFile[] listFiles();
ResourceFile[] listFiles(ResourceFileFilter filter);
String getName();
boolean isDirectory();
Resource getParent();
URL toURL() throws MalformedURLException;
long lastModified();
InputStream getInputStream() throws FileNotFoundException, IOException;
boolean delete();
boolean exists();
OutputStream getOutputStream() throws FileNotFoundException;
File getFile();
long length();
String getCanonicalPath() throws IOException;
boolean isFile();
Resource getCanonicalResource();
boolean canWrite();
boolean mkdir();
File getFileSystemRoot();
URI toURI();
File getResourceAsFile(ResourceFile resourceFile);
}

View file

@ -0,0 +1,332 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
/**
* Class for representing file object regardless of whether they are actual files in the file system or
* or files stored inside of a jar file. This class provides most all the same capabilities as the
* File class.
*
*/
public class ResourceFile implements Comparable<ResourceFile> {
private static final String JAR_FILE_PREFIX = "jar:file:";
private Resource resource;
private static Map<String, JarResource> jarRootsMap = new HashMap<String, JarResource>();
/**
* Construct a ResourceFile that represents a normal file in the file system.
* @param file the file in the file system.
*/
public ResourceFile(File file) {
resource = new FileResource(file);
}
/**
* Construct a new ResourceFile from a parent file and a relative child path.
* @param resourceFile the parent file
* @param path the child path.
*/
public ResourceFile(ResourceFile resourceFile, String path) {
if (resourceFile == null) {
throw new IllegalArgumentException("Parent ResourceFile cannot be null.");
}
resource = resourceFile.resource.getResource(path);
}
ResourceFile(Resource resource) {
this.resource = resource;
}
/**
* Constructs a Resource file from string path that can be either a file path or a jar url.
* @param absolutePath the path to the file.
*/
public ResourceFile(String absolutePath) {
this(absolutePath, null);
}
/**
* Constructs a Resource file from string path that can be either a file path or a jar url.
*
* @param absolutePath the path to the file.
* @param filter The filter used to exclude files from being loaded
*/
public ResourceFile(String absolutePath, JarEntryFilter filter) {
if (absolutePath.startsWith(JAR_FILE_PREFIX)) {
int indexOf = absolutePath.indexOf("!/");
if (indexOf < 0) {
throw new IllegalArgumentException("Invalid jar specification: " + absolutePath);
}
String filePath = absolutePath.substring(JAR_FILE_PREFIX.length(), indexOf);
String relativePath = absolutePath.substring(indexOf + 2);
Resource root = jarRootsMap.get(filePath);
try {
if (root == null) {
root = openJarResourceFile(new File(filePath), filter).resource;
}
resource = root.getResource(relativePath);
return;
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to open jar: " + filePath, e);
}
}
resource = new FileResource(new File(absolutePath));
}
/**
* Creates a new Root ResourceFile for a given jar file.
* @param jarFile the jar file to open.
* @param filter JarEntryFilter that will filter out unwanted jar entries.
* @return A Resource file that represents the root of the jarfile file system.
* @throws IOException if the jar file can't be read.
*/
public static ResourceFile openJarResourceFile(File jarFile, JarEntryFilter filter)
throws IOException {
JarResource root = new JarResource(jarFile, filter);
ResourceFile rootResourceFile = new ResourceFile(root);
jarRootsMap.put(jarFile.getCanonicalPath(), root);
return rootResourceFile;
}
/**
* Returns the absolute file path for this file.
* @return the absolute file path for this file.
*/
public String getAbsolutePath() {
return resource.getAbsolutePath();
}
/**
* Returns the canonical file path for this file.
* @return the absolute file path for this file.
*/
public String getCanonicalPath() throws IOException {
return resource.getCanonicalPath();
}
/**
* Returns a array of ResourceFiles if this ResourceFile is a directory. Otherwise return null.
* @return the child ResourceFiles if this is a directory, null otherwise.
*/
public ResourceFile[] listFiles() {
return resource.listFiles();
}
/**
* Returns a array of ResourceFiles if this ResourceFile is a directory. Otherwise return null.
* @param filter a filter to restrict the array of files returned.
* @return the child ResourceFiles if this is a directory, null otherwise.
*/
public ResourceFile[] listFiles(ResourceFileFilter filter) {
return resource.listFiles(filter);
}
/**
* Returns the simple name of the file.
* @return the simple name of the file.
*/
public String getName() {
return resource.getName();
}
/**
* Returns true if this Resource file exists and is a directory.
* @return true if this Resource file exists and is a directory.
*/
public boolean isDirectory() {
return resource.isDirectory();
}
/**
* Returns the parent of this ResourceFile or null if it is a root.
* @return the parent of this ResourceFile or null if it is a root.
*/
public ResourceFile getParentFile() {
Resource parent = resource.getParent();
return parent == null ? null : new ResourceFile(parent);
}
/**
* Returns a URL that represents this file object.
* @return a URL that represents this file object.
* @throws MalformedURLException if a URL can't be formed for this file.
*/
public URL toURL() throws MalformedURLException {
return resource.toURL();
}
/**
* Returns the time that this file was last modified.
* @return the time that this file was last modified.
*/
public long lastModified() {
return resource.lastModified();
}
/**
* If this file exists and is not a directory, it will return an InputStream for the file's
* contents.
* @return an InputStream for the file's contents.
* @throws FileNotFoundException if the file does not exist.
* @throws IOException
*/
public InputStream getInputStream() throws FileNotFoundException, IOException {
return resource.getInputStream();
}
/**
* Attempts to delete the file. Not supported (returns false) for files within a jar file.
* @return true if the file was deleted, false otherwise.
*/
public boolean delete() {
return resource.delete();
}
/**
* Returns true if the file exists.
* @return true if the file exists.
*/
public boolean exists() {
return resource.exists();
}
/**
* Returns an OutputStream if the file can be opened for writing.
* @return an OutputStream if the file can be opened for writing.
* @throws FileNotFoundException if the file can't be created or opened for writing.
*/
public OutputStream getOutputStream() throws FileNotFoundException {
return resource.getOutputStream();
}
/**
* Returns a File object. If this ResourceFile represents a standard filesystem, then no
* copy is necessary to return a file. If this ResourceFile represents a compressed
* filesystem, then a copy from that filesystem to the real filesystem is needed to create
* a File object. <tt>copyIfNeeded</tt> allows you to dictate whether a copy should take
* place, if needed.
* <p>
* If you just want the contents of a file, then call {@link #getInputStream()}.
*
* @param copyIfNeeded true to copy the file when embedded in a compressed filesystem; false
* to return null in that case.
* @return a File object or null if not a file and copyIfNeeded was false
*/
public File getFile(boolean copyIfNeeded) {
if (copyIfNeeded) {
return resource.getResourceAsFile(this);
}
return resource.getFile(); // will be null if the resource is a compressed filesystem
}
/**
* Returns the size of this file.
* @return the size of the file.
*/
public long length() {
return resource.length();
}
/**
* Returns true if this file exists and is not a directory.
* @return true if this file exists and is not a directory.
*/
public boolean isFile() {
return resource.isFile();
}
/**
* Returns the canonicalFile for this file.
* @return the canonicalFile for this file.
*/
public ResourceFile getCanonicalFile() {
Resource newResource = resource.getCanonicalResource();
if (resource == newResource) {
return this;
}
return new ResourceFile(newResource);
}
/**
* Returns true if this file can be written to.
* @return true if this file can be written to.
*/
public boolean canWrite() {
return resource.canWrite();
}
/**
* Creates a directory for the path represented by this file.
* @return true if a new directory was created.
*/
public boolean mkdir() {
return resource.mkdir();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
return resource.equals(((ResourceFile) obj).resource);
}
@Override
public int hashCode() {
return resource.hashCode();
}
@Override
public int compareTo(ResourceFile o) {
return getAbsolutePath().compareTo(o.getAbsolutePath());
}
@Override
public String toString() {
return getAbsolutePath();
}
/**
* Returns the root file for this file.
* @return the root file for this file.
*/
public File getFileSystemRoot() {
return resource.getFileSystemRoot();
}
/**
* Returns a URI for this file object.
* @return a URI for this file object.
*/
public URI toURI() {
return resource.toURI();
}
}

View file

@ -0,0 +1,21 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.jar;
public interface ResourceFileFilter {
public boolean accept(ResourceFile file);
}