diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties
index a3977b7197..15e3b1d2c9 100644
--- a/Core/nbproject/project.properties
+++ b/Core/nbproject/project.properties
@@ -1,6 +1,9 @@
file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar
file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar
+file.reference.jython.jar=C:\\autopsy\\Core\\release\\modules\\ext\\jython.jar
file.reference.metadata-extractor-2.6.2.jar=release/modules/ext/metadata-extractor-2.6.2.jar
+file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar
+file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.0-SNAPSHOT.jar=release/modules/ext/sqlite-jdbc-3.8.0-SNAPSHOT.jar
file.reference.tika-core-1.2.jar=release/modules/ext/tika-core-1.2.jar
file.reference.Tsk_DataModel.jar=release/modules/ext/Tsk_DataModel.jar
diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index d5e7aa87ab..308f254688 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -217,34 +217,38 @@
ext/sevenzipjbinding.jar
release/modules/ext/sevenzipjbinding.jar
-
- ext/xmpcore.jar
- release/modules/ext/xmpcore.jar
-
-
- ext/jdom-2.0.5-contrib.jar
- release/modules/ext/jdom-2.0.5-contrib.jar
-
ext/sevenzipjbinding-AllPlatforms.jar
release/modules/ext/sevenzipjbinding-AllPlatforms.jar
-
- ext/Tsk_DataModel.jar
- release/modules/ext/Tsk_DataModel.jar
-
-
- ext/sqlite-jdbc-3.8.0-SNAPSHOT.jar
- release/modules/ext/sqlite-jdbc-3.8.0-SNAPSHOT.jar
-
ext/metadata-extractor-2.6.2.jar
release/modules/ext/metadata-extractor-2.6.2.jar
+
+ ext/xmpcore.jar
+ release/modules/ext/xmpcore.jar
+
ext/tika-core-1.2.jar
release/modules/ext/tika-core-1.2.jar
+
+ ext/jython.jar
+ C:\autopsy\Core\release\modules\ext\jython.jar
+
+
+ ext/jdom-2.0.5-contrib.jar
+ release/modules/ext/jdom-2.0.5-contrib.jar
+
+
+ ext/sqlite-jdbc-3.8.0-SNAPSHOT.jar
+ release/modules/ext/sqlite-jdbc-3.8.0-SNAPSHOT.jar
+
+
+ ext/Tsk_DataModel.jar
+ release/modules/ext/Tsk_DataModel.jar
+
diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleJythonFileIngestModule.py b/Core/src/org/sleuthkit/autopsy/examples/SampleJythonFileIngestModule.py
new file mode 100755
index 0000000000..d9dbda8dcf
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/examples/SampleJythonFileIngestModule.py
@@ -0,0 +1,29 @@
+import jarray
+from org.sleuthkit.autopsy.ingest import FileIngestModule
+from org.sleuthkit.datamodel import AbstractFile
+from org.sleuthkit.datamodel import ReadContentInputStream
+from org.sleuthkit.datamodel import BlackboardArtifact
+from org.sleuthkit.datamodel import BlackboardAttribute
+
+class SampleJythonFileIngestModule(FileIngestModule):
+
+ def startUp(self, context):
+ pass
+
+ def process(self, file):
+ if file.getName().endswith("txt"):
+ # Make an artifact
+ art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)
+ att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), "pythonModule", "Text FILE")
+ art.addAttribute(att)
+
+ # Read the contents of the file
+ inputStream = ReadContentInputStream(file)
+ buffer = jarray.zeros(1024, "b")
+ totLen = 0
+ len = inputStream.read(buffer)
+ while (len != -1):
+ totLen = totLen + len
+ len = inputStream.read(buffer)
+
+ return totLen
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleJythonIngestModuleMetadata.xml b/Core/src/org/sleuthkit/autopsy/examples/SampleJythonIngestModuleMetadata.xml
new file mode 100755
index 0000000000..6d6f459977
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/examples/SampleJythonIngestModuleMetadata.xml
@@ -0,0 +1,8 @@
+
+
+ Sample Jython Ingest Module
+ A sample Jython ingest module.
+ 1.0
+ SampleJythonDataSourceIngestModule
+ SampleJythonFileIngestModule
+
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
index 50889a1c9c..29f1b58b1d 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
@@ -28,6 +28,7 @@ import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.examples.SampleExecutableIngestModuleFactory;
import org.sleuthkit.autopsy.examples.SampleIngestModuleFactory;
+import org.sleuthkit.autopsy.ingest.JythonIngestModuleFactory.JythonIngestModuleFactoryException;
import org.sleuthkit.autopsy.modules.android.AndroidModuleFactory;
import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory;
import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory;
@@ -107,6 +108,13 @@ final class IngestModuleFactoryLoader {
orderedModuleFactories.add(nonCoreFactory);
}
+ // RJCTODO:
+ try {
+ orderedModuleFactories.add(new JythonIngestModuleFactory("C:\\autopsy\\Core\\src\\org\\sleuthkit\\autopsy\\examples\\SampleJythonFileIngestModule.py", ""));
+ } catch (JythonIngestModuleFactoryException ex) {
+ // RJCTODO
+ }
+
return orderedModuleFactories;
}
}
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/JythonIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/ingest/JythonIngestModuleFactory.java
new file mode 100755
index 0000000000..b761a49f20
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/ingest/JythonIngestModuleFactory.java
@@ -0,0 +1,174 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2014 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.ingest;
+
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.python.util.PythonInterpreter;
+
+/**
+ * Creates Java objects associated with instantiated Jython ingest module
+ * classes.
+ */
+class JythonIngestModuleFactory extends IngestModuleFactoryAdapter {
+
+ private PythonInterpreter interpreter;
+ private String moduleDisplayName;
+ private String moduleDescription;
+ private String moduleVersionNumber;
+ private String dataSourceIngestModuleClassName;
+ private String fileIngestModuleClassName;
+ private int instanceNumber;
+
+ // RJCTODO: With this approach a Jython ingest module developer needs to
+ // write either one or two ingest module classes and a few lines of XML. We could
+ // offer a Jython module import UI that allows an Autopsy user to choose a
+ // script file and an associated metadata file, which we would copy into a
+ // subdirectory (we would create a new subdirectory for each imported Jython
+ // module) of a Jython ingest modules directory created by the installer.
+ // Perhaps we could ask the Jython module developer to create an archive
+ // with the two files in it, so the user would only need to import a
+ // single file, sort of like an NBM file.
+ /**
+ * Constructs an ingest module factory capable of producing Java objects
+ * associated with instantiated Jython ingest module classes.
+ *
+ * @param jythonScriptPath The full path to a Jython script with the ingest
+ * module class definitions.
+ * @param moduleMetadataFilePath The full path to an XML file of metadata
+ * for the ingest module classes defined in the Jython script.
+ * @throws
+ * org.sleuthkit.autopsy.ingest.JythonIngestModuleFactory.JythonIngestModuleFactoryException
+ */
+ JythonIngestModuleFactory(String jythonScriptPath, String moduleMetadataFilePath) throws JythonIngestModuleFactoryException {
+ executeJythonScript(jythonScriptPath);
+ parseModuleMetadataFile(moduleMetadataFilePath);
+ }
+
+ private void executeJythonScript(String jythonScriptPath) throws JythonIngestModuleFactoryException {
+ try {
+ interpreter = new PythonInterpreter();
+ interpreter.execfile(jythonScriptPath);
+ } catch (Exception ex) {
+ throw new JythonIngestModuleFactoryException("Error executing Jython script at " + jythonScriptPath + ": " + ex.getLocalizedMessage());
+ }
+ }
+
+ private void parseModuleMetadataFile(String moduleMetadataFilePath) throws JythonIngestModuleFactoryException {
+ // RJCTODO: Parse something like this:
+//
+//
+// Sample Jython Ingest Module
+// A sample Jython ingest module.
+// 1.0
+// SampleJythonDataSourceIngestModule
+// SampleJythonFileIngestModule
+//
+ this.moduleDisplayName = "Sample Jython Ingest Module";
+ this.moduleDescription = "A sample Jython ingest module";
+ this.moduleVersionNumber = "1.0";
+ this.fileIngestModuleClassName = "SampleJythonFileIngestModule";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getModuleDisplayName() {
+ return this.moduleDisplayName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getModuleDescription() {
+ return this.moduleDescription;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getModuleVersionNumber() {
+ return this.moduleVersionNumber;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDataSourceIngestModuleFactory() {
+ return this.dataSourceIngestModuleClassName != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings ingestOptions) {
+ return (DataSourceIngestModule) createIngestModuleInstance(this.dataSourceIngestModuleClassName, DataSourceIngestModule.class);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isFileIngestModuleFactory() {
+ return this.fileIngestModuleClassName != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) {
+ return (FileIngestModule) createIngestModuleInstance(this.fileIngestModuleClassName, FileIngestModule.class);
+ }
+
+ // RJCTODO: There just doesn't seem to be a way to make this a generic...
+ private Object createIngestModuleInstance(String moduleClassName, Class clazz) {
+ try {
+ String instanceName = moduleClassName + "_" + instanceNumber++;
+ this.interpreter.exec(instanceName + " = " + moduleClassName + "()");
+ return this.interpreter.get(instanceName).__tojava__(clazz);
+ } catch (Exception ex) {
+ // RJCTODO: Do error handling this way? Add an exception specification to API (perhaps with deprecated version with no spec)?
+ // The generated message is not user-friendly!
+ StringBuilder errorMessage = new StringBuilder("Error creating instance of ");
+ errorMessage.append(moduleClassName);
+ errorMessage.append(" extending ");
+ errorMessage.append(clazz.getSimpleName());
+ errorMessage.append(": ");
+ errorMessage.append(ex.toString()); // Jython exceptions apparently don't support getMessage()
+ DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(errorMessage.toString(), NotifyDescriptor.ERROR_MESSAGE));
+ return null;
+ }
+ }
+
+ static class JythonIngestModuleFactoryException extends Exception {
+
+ public JythonIngestModuleFactoryException() {
+ }
+
+ public JythonIngestModuleFactoryException(String message) {
+ super(message);
+ }
+ }
+}