/*
* Copyright, Aspect Security, Inc.
*
* This file is part of JavaSnoop.
*
* JavaSnoop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JavaSnoop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JavaSnoop. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aspect.snoop.util;
import com.aspect.snoop.JavaSnoop;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class ClasspathUtil {
//private static Logger logger = Logger.getLogger(ClasspathUtil.class);
/*
* This may all be unnecessary, but it's useful to have around.
*/
public static byte[] getClassBytes (String clazz, String classpath, boolean useSystemClasspath) {
String completeClasspath = "";
if ( useSystemClasspath ) {
completeClasspath += getSystemClasspath();
}
completeClasspath += classpath;
String simpleClassName = ReflectionUtil.getSimpleClassName(clazz);
StringTokenizer st = new StringTokenizer(completeClasspath, File.pathSeparator);
while (st.hasMoreTokens()) {
String token = st.nextToken();
File classpathElement = new File(token);
if (classpathElement.isDirectory() ) {
String classFiles[] = classpathElement.list(new ClassFilter());
for ( String file : classFiles ) {
if ( file.equals(simpleClassName + ".class") ) {
File targetFile = new File(classpathElement.getAbsolutePath() + File.separatorChar + file);
try {
return IOUtil.getBytesFromFile(targetFile);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
String[] jarFiles = classpathElement.list(new JarFilter());
for ( String file : jarFiles ) {
byte[] classBytes = null;
try {
File jarFile = new File(classpathElement.getAbsolutePath() + File.separator + file);
classBytes = findClassInJar(clazz, jarFile);
} catch(IOException ex) {
ex.printStackTrace();
}
if ( classBytes != null ) {
return classBytes;
}
}
} else { // it's a jar
byte[] classBytes = null;
try {
classBytes = findClassInJar(clazz, classpathElement);
} catch (IOException ex) {
ex.printStackTrace();
}
if ( classBytes != null ) {
return classBytes;
}
}
}
return null;
}
public static String getSystemClasspath() {
// Get the system classpath
String classpath = System.getProperty("java.class.path", ".");
if (classpath.equals("")) {
System.err.println("error: classpath is not set");
}
if ( ! "".equals(classpath) ) {
classpath += ";";
}
String javaHome = System.getProperty("java.home");
if ( ! "".equals(javaHome) ) {
javaHome += File.separator + "lib";
classpath += javaHome;
classpath += ";";
}
return classpath;
}
/**
* This function retrieves all unique system classpath classes
* and classes added to the classpath in the method parameter.
* @param appClasspath A set of classpath entries, delimited by semicolons.
* @return A List<String> contains the String name of all available classes.
*/
public static List<String> getClasses(String appClasspath) {
return getClasses(appClasspath, true);
}
public static List<String> getClasses(String appClasspath, boolean useSystemClasspath) {
List<String> classes = new ArrayList<String>();
try {
String classpath = "";
if ( useSystemClasspath ) {
classpath = getSystemClasspath();
}
classpath += appClasspath;
StringTokenizer st =
new StringTokenizer(classpath, File.pathSeparator);
while (st.hasMoreTokens()) {
String token = st.nextToken();
File classpathElement = new File(token);
if ( classpathElement.isDirectory() ) {
// The classpath element is a directory, containing
// class files and possibly jar files. Search it recursively
// for both.
classes.addAll ( recursiveFindClasses(classpathElement.getAbsolutePath(),"") );
} else if ( classpathElement.getName().endsWith(".jar")) {
// The classpath element is a jar file. Add all its classes.
classes.addAll( loadClassesFromJar(classpathElement) );
} else if ( classpathElement.getName().endsWith(".class") ) {
// The classpath element is a class file itself. Assume the
// default package.
String clz = classpathElement.getName();
clz = clz.substring(0,clz.length()-6);
classes.add ( clz );
}
}
} catch (Exception e) {
e.printStackTrace();
}
classes = removeDuplicates(classes);
return classes;
}
/**
* Gets the bytes of a class file from within a jar.
* @param clazz The name of the class whose bytes are needed
* @param jarFile The jar File object to find it in
* @return The bytes of the class file requested
* @throws IOException
*/
private static byte[] findClassInJar(String clazz, File jarFile) throws IOException {
if ( ! jarFile.getName().endsWith(".jar") ) {
return null;
}
JarFile jar = new JarFile(jarFile);
Enumeration<JarEntry> classes = jar.entries();
while ( classes.hasMoreElements() ) {
JarEntry entry = (JarEntry)classes.nextElement();
String completeClass = entry.getName().replace("/", ".");
if (completeClass.equals(clazz + ".class") ) {
return IOUtil.getBytesFromStream(jar.getInputStream(entry));
}
}
return null;
}
private static List<String> loadClassesFromJar(File jarFile) {
List<String> files = new ArrayList<String>();
try {
//logger.debug(jarFile + " is being scanned");
Enumeration<JarEntry> fileNames;
fileNames = new JarFile(jarFile).entries();
JarEntry entry = null;
while (fileNames.hasMoreElements()) {
entry = fileNames.nextElement();
if (entry.getName().endsWith(".class")) {
String converted = entry.getName().replace("/", ".");
converted = converted.substring(0,converted.length()-6);
files.add(converted);
}
}
} catch (IOException e) {
e.printStackTrace();
}
Set noDupes = new LinkedHashSet(files);
files.clear();
files.addAll(noDupes);
return files;
}
public static Class[] getAllSubtypes(Class c) {
return new Class[] { c };
}
private static List recursiveFindClasses(String root, String dir) {
List<String> classes = new ArrayList<String>();
File currentDir;
if ( dir.length() > 0 ) {
currentDir = new File(dir);
} else {
currentDir = new File(root);
}
File[] classFiles = currentDir.listFiles(new ClassFilter());
File[] jarFiles = currentDir.listFiles(new JarFilter());
File[] subDirectories = currentDir.listFiles(new DirFilter());
String pkg = "";
if ( dir.length() > 0 ) {
pkg = dir.substring(root.length()+1);
}
for ( File classFile : classFiles ) {
String s = pkg + "." + classFile.getName();
s = s.substring(0,s.length()-6); // remove .class
s = s.replace(File.separator,".");
classes.add(s);
}
for ( File jarFile : jarFiles ) {
classes.addAll( loadClassesFromJar(jarFile) );
}
for ( File subDir : subDirectories ) {
classes.addAll( recursiveFindClasses(root, subDir.getAbsolutePath() ) );
}
return classes;
}
/**
* Return an array of Class objects based on the String class names passed in.
* @param types the name of the classes to be returned
* @return the Class representation of the class names passed in types
* @throws ClassNotFoundException
*/
public static Class[] asClasses(String[] types) throws ClassNotFoundException {
Class[] classes = new Class[types.length];
for(int i=0;i<types.length;i++) {
if ( "boolean".equals(types[i]) ) {
types[i] = "java.lang.Boolean";
} else if ( "byte".equals(types[i]) ) {
types[i] = "java.lang.Byte";
} else if ( "short".equals(types[i]) ) {
types[i] = "java.lang.Short";
} else if ( "char".equals(types[i]) ) {
types[i] = "java.lang.Character";
} else if ( "long".equals(types[i]) ) {
types[i] = "java.lang.Long";
} else if ( "double".equals(types[i]) ) {
types[i] = "java.lang.Double";
} else if ( "float".equals(types[i]) ) {
types[i] = "java.lang.Float";
}
classes[i] = Class.forName( types[i] );
}
return classes;
}
public static String[] asStrings(Class[] types) throws ClassNotFoundException {
String[] classes = new String[types.length];
for(int i=0;i<types.length;i++) {
classes[i] = types[i].getName();
}
return classes;
}
public static String getManifestSystemClasspathString() {
return getManifestClasspathString(System.getProperty("java.class.path"));
}
public static String getManifestClasspathString(String cp) {
String classpath = new String(cp);
classpath = classpath.replaceAll(" ", "%20");
StringBuffer sb = new StringBuffer();
int firstLineLength = 72-("Class-Path: ".length() + 1);
String s = classpath.substring(0,firstLineLength);
sb.append(s + " \r\n");
int index = firstLineLength;
while ( index < classpath.length() ) {
int toBeCopied = 70;
if ( classpath.length() - index < 70 ) {
toBeCopied = classpath.length()-index;
}
sb.append(" ");
sb.append(classpath.substring(index,index+toBeCopied));
index += toBeCopied;
if ( toBeCopied == 70 ) {
sb.append(" ");
}
sb.append("\r\n");
}
return sb.toString();
}
private static ArrayList removeDuplicates(List<String> classes) {
Set set = new LinkedHashSet();
set.addAll(classes);
return new ArrayList(set);
}
public static boolean isJavaOrSunClass(String cls) {
String[] pkgs = new String[]{
"java.",
"javax.",
"sun.",
"sunw.",
"netscape.",
"apple.",
"com.apple.",
"com.sun."
};
for(String pkg : pkgs) {
if ( cls.startsWith(pkg) ) {
return true;
}
}
return false;
}
public static boolean isJavaSnoopClass(String cls) {
String[] pkgs = new String[] {
"com.aspect.snoop",
"org.fife",
"org.apache.log4j",
"org.codehaus",
"org.xmlpull",
"org.relaxng",
"org.jaxen",
"org.jcp",
"org.joda",
"com.aspect.org.jdesktop",
"org.jdom",
"org.ietf",
"org.omg",
"org.dom4j",
"org.xml.sax",
"org.w3c.dom",
"org.codehaus.jettison",
"net.sf.cglib",
"com.wutka",
"com.thoughtworks.xstream",
"com.megginson",
"com.ctc.wstx",
"com.bea.xml.stream",
"JDOMAbout",
"javassist.",
"net.sf.cgilib",
"nu.xom",
"org.python"
};
for(String pkg : pkgs) {
if ( cls.startsWith(pkg) ) {
return true;
}
}
return false;
}
public static String getMainClassFromJarFile(String path) throws IOException {
if ( ! new File(path).exists() ) {
return "(unknown)";
}
JarFile jar = new JarFile(path);
Manifest manifest = jar.getManifest();
Attributes attrs = manifest.getMainAttributes();
return attrs.getValue(Attributes.Name.MAIN_CLASS);
}
}
class ClassFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.endsWith(".class"));
}
}
class JarFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.endsWith(".jar"));
}
}
class DirFilter implements FileFilter {
public boolean accept(File pathname) {
return pathname.isDirectory();
}
}