/* * Autopsy Forensic Browser * * Copyright 2014 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * 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 org.sleuthkit.autopsy.python; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Scanner; import java.util.Set; import java.util.logging.Level; import java.util.regex.Matcher; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.modules.InstalledFileLocator; import org.openide.util.NbBundle; import org.python.util.PythonInterpreter; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.report.GeneralReportModule; /** * Finds and loads Autopsy modules written using the Jython variant of the * Python scripting language. */ public final class JythonModuleLoader { private static final Logger logger = Logger.getLogger(JythonModuleLoader.class.getName()); /** * Get ingest module factories implemented using Jython. * * @return A list of objects that implement the IngestModuleFactory * interface. */ public static List<IngestModuleFactory> getIngestModuleFactories() { return getInterfaceImplementations(new IngestModuleFactoryDefFilter(), IngestModuleFactory.class); } /** * Get general report modules implemented using Jython. * * @return A list of objects that implement the GeneralReportModule * interface. */ public static List<GeneralReportModule> getGeneralReportModules() { return getInterfaceImplementations(new GeneralReportModuleDefFilter(), GeneralReportModule.class); } private static <T> List<T> getInterfaceImplementations(LineFilter filter, Class<T> interfaceClass) { List<T> objects = new ArrayList<>(); Set<File> pythonModuleDirs = new HashSet<>(); PythonInterpreter interpreter = new PythonInterpreter(); // add python modules from 'autospy/build/cluster/InternalPythonModules' folder // which are copied from 'autopsy/*/release/InternalPythonModules' folders. for (File f : InstalledFileLocator.getDefault().locateAll("InternalPythonModules", "org.sleuthkit.autopsy.core", false)) { //NON-NLS Collections.addAll(pythonModuleDirs, f.listFiles()); } // add python modules from 'testuserdir/python_modules' folder Collections.addAll(pythonModuleDirs, new File(PlatformUtil.getUserPythonModulesPath()).listFiles()); for (File file : pythonModuleDirs) { if (file.isDirectory()) { File[] pythonScripts = file.listFiles(new PythonScriptFileFilter()); for (File script : pythonScripts) { try { Scanner fileScanner = new Scanner(script); while (fileScanner.hasNextLine()) { String line = fileScanner.nextLine(); if (line.startsWith("class ") && filter.accept(line)) { //NON-NLS String className = line.substring(6, line.indexOf("(")); try { objects.add(createObjectFromScript(interpreter, script, className, interfaceClass)); } catch (Exception ex) { logger.log(Level.SEVERE, String.format("Failed to load %s from %s", className, script.getAbsolutePath()), ex); //NON-NLS // NOTE: using ex.toString() because the current version is always returning null for ex.getMessage(). DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( NbBundle.getMessage(JythonModuleLoader.class, "JythonModuleLoader.errorMessages.failedToLoadModule", className, ex.toString()), NotifyDescriptor.ERROR_MESSAGE)); } } } } catch (FileNotFoundException ex) { logger.log(Level.SEVERE, String.format("Failed to open %s", script.getAbsolutePath()), ex); //NON-NLS DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( NbBundle.getMessage(JythonModuleLoader.class, "JythonModuleLoader.errorMessages.failedToOpenModule", script.getAbsolutePath()), NotifyDescriptor.ERROR_MESSAGE)); } } } } return objects; } private static <T> T createObjectFromScript(PythonInterpreter interpreter, File script, String className, Class<T> interfaceClass) { // Add the directory where the Python script resides to the Python // module search path to allow the script to use other scripts bundled // with it. interpreter.exec("import sys"); //NON-NLS String path = Matcher.quoteReplacement(script.getParent()); interpreter.exec("sys.path.append('" + path + "')"); //NON-NLS String moduleName = script.getName().replaceAll("\\.py$", ""); //NON-NLS // reload the module so that the changes made to it can be loaded. interpreter.exec("import " + moduleName); //NON-NLS interpreter.exec("reload(" + moduleName + ")"); //NON-NLS // Importing the appropriate class from the Py Script which contains multiple classes. interpreter.exec("from " + moduleName + " import " + className); //NON-NLS interpreter.exec("obj = " + className + "()"); //NON-NLS T obj = interpreter.get("obj", interfaceClass); //NON-NLS // Remove the directory where the Python script resides from the Python // module search path. interpreter.exec("sys.path.remove('" + path + "')"); //NON-NLS return obj; } private static class PythonScriptFileFilter implements FilenameFilter { @Override public boolean accept(File dir, String name) { return name.endsWith(".py"); //NON-NLS } //NON-NLS } private static interface LineFilter { boolean accept(String line); } private static class IngestModuleFactoryDefFilter implements LineFilter { @Override public boolean accept(String line) { return (line.contains("IngestModuleFactoryAdapter") || line.contains("IngestModuleFactory")); //NON-NLS } } private static class GeneralReportModuleDefFilter implements LineFilter { @Override public boolean accept(String line) { return (line.contains("GeneralReportModuleAdapter") || line.contains("GeneralReportModule")); //NON-NLS } } }