diff --git a/Case/nbproject/genfiles.properties b/Case/nbproject/genfiles.properties
index 9c721222f6..6a27bbe1d2 100644
--- a/Case/nbproject/genfiles.properties
+++ b/Case/nbproject/genfiles.properties
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=a2330d9e
+build.xml.data.CRC32=2b495fd9
build.xml.script.CRC32=601bc2ba
-build.xml.stylesheet.CRC32=a56c6a5b@2.47.1
+build.xml.stylesheet.CRC32=a56c6a5b@1.46.2
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=a2330d9e
+nbproject/build-impl.xml.data.CRC32=2b495fd9
nbproject/build-impl.xml.script.CRC32=65e93a36
-nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.47.1
+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.46.2
diff --git a/Case/nbproject/project.xml b/Case/nbproject/project.xml
index ed0e258923..1620ff17fa 100644
--- a/Case/nbproject/project.xml
+++ b/Case/nbproject/project.xml
@@ -123,6 +123,15 @@
1.0
+
+ org.sleuthkit.autopsy.ingest
+
+
+
+ 0-1
+ 1.0
+
+
diff --git a/Case/src/org/sleuthkit/autopsy/casemodule/layer.xml b/Case/src/org/sleuthkit/autopsy/casemodule/layer.xml
index 13aca85875..25182317bd 100644
--- a/Case/src/org/sleuthkit/autopsy/casemodule/layer.xml
+++ b/Case/src/org/sleuthkit/autopsy/casemodule/layer.xml
@@ -188,6 +188,11 @@
+
+
+
+
+
diff --git a/Case/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestService.java b/Case/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestService.java
new file mode 100644
index 0000000000..69e2d7b916
--- /dev/null
+++ b/Case/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestService.java
@@ -0,0 +1,76 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.hashdatabase;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.sleuthkit.autopsy.ingest.IngestManager;
+import org.sleuthkit.autopsy.ingest.IngestServiceFsContent;
+import org.sleuthkit.datamodel.FsContent;
+
+public class HashDbIngestService implements IngestServiceFsContent {
+
+ private static HashDbIngestService instance = null;
+
+ private static String SERVICE_NAME = "Hash Db";
+
+ private static final Logger logger = Logger.getLogger(HashDbIngestService.class.getName());
+
+ private HashDbIngestService() {
+
+ }
+
+ public static synchronized HashDbIngestService getDefault() {
+ if (instance == null) {
+ instance = new HashDbIngestService();
+ }
+ return instance;
+ }
+
+ @Override
+ public void process(FsContent fsContent) {
+ logger.log(Level.INFO, "Processing fsContent: " + fsContent.getName());
+ }
+
+ @Override
+ public void complete() {
+ logger.log(Level.INFO, "complete()");
+ }
+
+ @Override
+ public String getName() {
+ return SERVICE_NAME;
+ }
+
+ @Override
+ public void init(IngestManager manager) {
+ logger.log(Level.INFO, "init()");
+ }
+
+ @Override
+ public void stop() {
+ logger.log(Level.INFO, "stop()");
+ }
+
+
+
+}
diff --git a/Ingest/build.xml b/Ingest/build.xml
new file mode 100644
index 0000000000..c92889e28a
--- /dev/null
+++ b/Ingest/build.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+ Builds, tests, and runs the project org.sleuthkit.autopsy.ingest.
+
+
diff --git a/Ingest/manifest.mf b/Ingest/manifest.mf
new file mode 100644
index 0000000000..77e0a0d1dd
--- /dev/null
+++ b/Ingest/manifest.mf
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-Localization: org/sleuthkit/autopsy/ingest/Bundle
+Bundle-Name: %OpenIDE-Module-Name
+Bundle-SymbolicName: org.sleuthkit.autopsy.ingest/1
+OpenIDE-Module-Implementation-Version: 1
+OpenIDE-Module-Layer: org/sleuthkit/autopsy/ingest/layer.xml
+OpenIDE-Module-Requires: org.openide.windows.WindowManager
+OpenIDE-Module: org.sleuthkit.autopsy.ingest/0
+
diff --git a/Ingest/nbproject/build-impl.xml b/Ingest/nbproject/build-impl.xml
new file mode 100644
index 0000000000..3362f1bf27
--- /dev/null
+++ b/Ingest/nbproject/build-impl.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ You must set 'suite.dir' to point to your containing module suite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ingest/nbproject/genfiles.properties b/Ingest/nbproject/genfiles.properties
new file mode 100644
index 0000000000..f4f204a4e0
--- /dev/null
+++ b/Ingest/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=1833dad1
+build.xml.script.CRC32=7d5a04db
+build.xml.stylesheet.CRC32=a56c6a5b@1.46.2
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=1833dad1
+nbproject/build-impl.xml.script.CRC32=8b2da6d1
+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.46.2
diff --git a/Ingest/nbproject/project.properties b/Ingest/nbproject/project.properties
new file mode 100644
index 0000000000..e84197de12
--- /dev/null
+++ b/Ingest/nbproject/project.properties
@@ -0,0 +1,3 @@
+javac.source=1.6
+javac.compilerargs=-Xlint -Xlint:-serial
+spec.version.base=1.0
diff --git a/Ingest/nbproject/project.xml b/Ingest/nbproject/project.xml
new file mode 100644
index 0000000000..15cae5495e
--- /dev/null
+++ b/Ingest/nbproject/project.xml
@@ -0,0 +1,121 @@
+
+
+ org.netbeans.modules.apisupport.project
+
+
+ org.sleuthkit.autopsy.ingest
+
+
+
+ org.netbeans.api.progress
+
+
+
+ 1
+ 1.24.1
+
+
+
+ org.netbeans.libs.osgi
+
+
+
+
+ org.netbeans.modules.settings
+
+
+
+ 1
+ 1.31.1
+
+
+
+ org.openide.awt
+
+
+
+ 7.31.1
+
+
+
+ org.openide.dialogs
+
+
+
+ 7.20.1
+
+
+
+ org.openide.modules
+
+
+
+ 7.23.1
+
+
+
+ org.openide.nodes
+
+
+
+ 7.21.1
+
+
+
+ org.openide.util
+
+
+
+ 8.15.1
+
+
+
+ org.openide.util.lookup
+
+
+
+ 8.8.1
+
+
+
+ org.openide.windows
+
+
+
+ 6.40.1
+
+
+
+ org.sleuthkit.autopsy.corecomponentinterfaces
+
+
+
+ 1
+ 1.0
+
+
+
+ org.sleuthkit.autopsy.coreutils
+
+
+
+ 0-1
+ 0.0
+
+
+
+ org.sleuthkit.autopsy.datamodel
+
+
+
+ 1
+ 1.0
+
+
+
+
+ org.sleuthkit.autopsy.ingest
+
+
+
+
diff --git a/Ingest/nbproject/suite.properties b/Ingest/nbproject/suite.properties
new file mode 100644
index 0000000000..29d7cc9bd6
--- /dev/null
+++ b/Ingest/nbproject/suite.properties
@@ -0,0 +1 @@
+suite.dir=${basedir}/..
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties b/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties
new file mode 100644
index 0000000000..3efb782510
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties
@@ -0,0 +1,9 @@
+CTL_IngestAction=Ingest
+CTL_IngestTopComponent=Ingest
+HINT_IngestTopComponent=Ingest window
+OpenIDE-Module-Name=Ingest
+IngestTopComponent.topLable.text=Image ingest services
+IngestTopComponent.startButton.text=Start
+IngestTopComponent.refreshFreqLabel.text=Refresh frequency
+IngestTopComponent.fileProgressLabel.text=File progress
+IngestTopComponent.imageProgressLabel.text=Image progress
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java
new file mode 100644
index 0000000000..66e0612e67
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java
@@ -0,0 +1,206 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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 java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+import org.openide.util.Lookup;
+import org.sleuthkit.datamodel.FsContent;
+import org.sleuthkit.datamodel.Image;
+
+/**
+ * IngestManager sets up and manages ingest services
+ * runs them in a background thread
+ * notifies services when work is complete or should be interrupted
+ * processes messages from services in postMessage() and posts them to GUI
+ *
+ */
+public class IngestManager {
+
+ private static final Logger logger = Logger.getLogger(IngestManager.class.getName());
+ private IngestTopComponent tc;
+ private IngestManagerStats stats;
+ private int updateFrequency;
+
+
+
+ /**
+ *
+ * @param tc handle to Ingest top component
+ */
+ IngestManager(IngestTopComponent tc) {
+ this.tc = tc;
+ stats = new IngestManagerStats();
+ }
+
+ /**
+ * IngestManager entry point, enqueues image to be processed.
+ * Spawns background thread which enumerates all sorted files and executes chosen services per file in a pre-determined order.
+ * Notifies services when work is complete or should be interrupted using complete() and stop() calls.
+ * Does not block and can be called multiple times to enqueue more work to already running background process.
+ */
+ void execute(Collection services, Image image) {
+ }
+
+ /**
+ * returns the current minimal update frequency setting
+ * Services should call this between processing iterations to get current setting
+ * and use the setting to change notification and data refresh intervals
+ */
+ public synchronized int getUpdateFrequency() {
+ return updateFrequency;
+ }
+
+ /**
+ * set new minimal update frequency services should use
+ * @param frequency
+ */
+ synchronized void setUpdateFrequency(int frequency) {
+ this.updateFrequency = frequency;
+ }
+
+ /**
+ * returns ingest summary report (how many files ingested, any errors, etc)
+ */
+ String getReport() {
+ return stats.toString();
+ }
+
+ /**
+ * Service publishes message using InegestManager handle
+ * Does not block.
+ * The message gets enqueued in the GUI thread and displayed in a widget
+ * IngestService should make an attempt not to publish the same message multiple times.
+ * Viewer will attempt to identify duplicate messages and filter them out (slower)
+ */
+ public synchronized void postMessage(final IngestMessage message) {
+
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+ tc.displayMessage(message);
+ }
+ });
+ }
+
+ /**
+ * helper to return all image services managed (using Lookup API)
+ */
+ public static Collection enumerateImageServices() {
+ return (Collection) Lookup.getDefault().lookupAll(IngestServiceImage.class);
+ }
+
+ /**
+ * helper to return all file/dir services managed (using Lookup API)
+ */
+ public static Collection enumerateFsContentServices() {
+ return (Collection) Lookup.getDefault().lookupAll(IngestServiceFsContent.class);
+ }
+
+ /**
+ * get next file/dir to process
+ * the queue of FsContent to process is maintained internally
+ * and could be dynamically sorted as data comes in
+ */
+ private synchronized FsContent getNextFsContent() {
+ return null;
+ }
+
+ private synchronized boolean hasNextFsContent() {
+ return true;
+ }
+
+ /**
+ * get next Image to process
+ * the queue of Images to process is maintained internally
+ * and could be dynamically sorted as data comes in
+ */
+ private synchronized Image getNextImage() {
+ return null;
+ }
+
+ private synchronized boolean hasNextImage() {
+ return true;
+ }
+
+ /**
+ * collects IngestManager statistics during runtime
+ */
+ private static class IngestManagerStats {
+
+ Date startTime;
+ Date endTime;
+ int errorsTotal;
+ Map errors;
+ private static DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ IngestManagerStats() {
+ errors = new HashMap();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (startTime != null) {
+ sb.append("Start time: ").append(dateFormatter.format(startTime));
+ }
+ if (endTime != null) {
+ sb.append("End time: ").append(dateFormatter.format(endTime));
+ }
+ sb.append("Total ingest time: ").append(getTotalTime());
+ sb.append("Total errors: ").append(errorsTotal);
+ if (errorsTotal > 0) {
+ sb.append("Errors per service:");
+ for (IngestServiceAbstract service : errors.keySet()) {
+ final int errorsService = errors.get(service);
+ sb.append("\t").append(service.getName()).append(": ").append(errorsService);
+ }
+ }
+ return sb.toString();
+ }
+
+ void start() {
+ startTime = new Date();
+ }
+
+ void end() {
+ endTime = new Date();
+ }
+
+ long getTotalTime() {
+ if (startTime == null || endTime == null) {
+ return 0;
+ }
+ return endTime.getTime() - startTime.getTime();
+ }
+
+ void addError(IngestServiceAbstract source) {
+ ++errorsTotal;
+ int curServiceError = errors.get(source);
+ errors.put(source, curServiceError + 1);
+ }
+ }
+}
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java
new file mode 100644
index 0000000000..95a86cdff9
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java
@@ -0,0 +1,142 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.sleuthkit.autopsy.datamodel.KeyValueThing;
+
+/**
+ * Representation of text posted by ingest services
+ *
+ * Message should have a unique ID within context of originating source
+ *
+ */
+public class IngestMessage {
+
+ public enum MessageType {
+
+ DATA, INFO, WARNING, ERROR
+ };
+
+ private long ID;
+ private MessageType messageType;
+ private IngestServiceAbstract source;
+ private String text;
+ private KeyValueThing data;
+
+ private IngestMessage(long ID, MessageType messageType, IngestServiceAbstract source, String text) {
+ this.ID = ID;
+ this.source = source;
+ this.messageType = messageType;
+ this.text = text;
+ }
+
+ //getters
+ public long getID() {
+ return ID;
+ }
+
+ public IngestServiceAbstract getSource() {
+ return source;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public KeyValueThing getData() {
+ return data;
+ }
+
+ public MessageType getMessageType() {
+ return messageType;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Long.toString(ID)).append(": ");
+ sb.append("type: ").append(messageType.name());
+ sb.append("source: ").append(source.getName());
+ sb.append("text: ").append(text);
+ if (data != null)
+ sb.append("data: ").append(data.toString());
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final IngestMessage other = (IngestMessage) obj;
+ if (this.ID != other.ID) {
+ return false;
+ }
+ if (this.messageType != other.messageType) {
+ return false;
+ }
+ if (this.source != other.source && (this.source == null || !this.source.equals(other.source))) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 83 * hash + (int) (this.ID ^ (this.ID >>> 32));
+ hash = 83 * hash + (this.messageType != null ? this.messageType.hashCode() : 0);
+ hash = 83 * hash + (this.source != null ? this.source.hashCode() : 0);
+ hash = 83 * hash + (this.text != null ? this.text.hashCode() : 0);
+ return hash;
+ }
+
+
+
+
+ //factory methods
+ public static IngestMessage createMessage(long ID, MessageType messageType, IngestServiceAbstract source, String message) {
+ if (messageType == null || source == null || message == null) {
+ throw new IllegalArgumentException("message type, source and message cannot be null");
+ }
+ IngestMessage im = new IngestMessage(ID, messageType, source, message);
+ return im;
+ }
+
+ public static IngestMessage createErrorMessage(long ID, IngestServiceAbstract source, String message) {
+ if (source == null || message == null) {
+ throw new IllegalArgumentException("message type, source and message cannot be null");
+ }
+ IngestMessage im = new IngestMessage(ID, MessageType.ERROR, source, message);
+ return im;
+ }
+
+ public static IngestMessage createDataMessage(long ID, IngestServiceAbstract source, String message, KeyValueThing data) {
+ if (source == null || message == null) {
+ throw new IllegalArgumentException("source and message cannot be null");
+ }
+ IngestMessage im = new IngestMessage(ID, MessageType.DATA, source, message);
+ im.data = data;
+ return im;
+ }
+
+}
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceAbstract.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceAbstract.java
new file mode 100644
index 0000000000..fe683cf154
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceAbstract.java
@@ -0,0 +1,51 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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;
+
+/**
+ * Base interface for ingest services
+ */
+public interface IngestServiceAbstract {
+
+ /**
+ * notification from manager that brand new processing should be initiated.
+ * Service loads its configuration and performs initialization
+ *
+ * @param IngestManager handle to the manager to postMessage() to
+ */
+ public void init(IngestManager manager);
+
+ /**
+ * notification from manager that there is no more content to process and all work is done.
+ * Service performs any clean-up, notifies viewers and may also write results to the black-board
+ */
+ public void complete();
+
+ /**
+ * notification from manager to stop processing due to some interruption (user, error, exception)
+ */
+ public void stop();
+
+ /**
+ * get specific name of the service
+ * should be unique across services, a user-friendly name of the service shown in GUI
+ */
+ public String getName();
+}
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceFsContent.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceFsContent.java
new file mode 100644
index 0000000000..23701b7885
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceFsContent.java
@@ -0,0 +1,36 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.sleuthkit.datamodel.FsContent;
+
+/**
+ * ingest service that acts on every FsContent in image
+ *
+ */
+public interface IngestServiceFsContent extends IngestServiceAbstract {
+
+ /**
+ * notification from manager to process file / directory.
+ * Service may choose to perform an action or enqueue processing of a group of FsContents.
+ * The service notifies viewers via IngestManager.postMessage()
+ * and may also write results to the black-board as it is processing
+ */
+ public void process(FsContent fsContent);
+}
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java
new file mode 100644
index 0000000000..6eb7cfe124
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java
@@ -0,0 +1,35 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.sleuthkit.datamodel.Image;
+
+/**
+ * ingest service that acts on entire image (such as Internet history)
+ *
+ */
+public interface IngestServiceImage extends IngestServiceAbstract {
+
+ /**
+ * notification from manager to process image
+ * The service notifies viewers via IngestManager.postMessage()
+ * and may also write results to the black-board as it is processing.
+ */
+ public void process(Image image);
+}
\ No newline at end of file
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form
new file mode 100644
index 0000000000..c6bc17e530
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form
@@ -0,0 +1,201 @@
+
+
+
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java
new file mode 100644
index 0000000000..ebe0f8e982
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java
@@ -0,0 +1,332 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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 java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import org.openide.util.NbBundle;
+import org.openide.windows.TopComponent;
+import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer;
+
+/**
+ * Top component explorer for the Ingest module.
+ */
+public final class IngestTopComponent extends TopComponent implements DataExplorer {
+
+ private static IngestTopComponent instance;
+ private static final Logger logger = Logger.getLogger(IngestTopComponent.class.getName());
+ private IngestManager manager = null;
+ private Collection services;
+ private Map serviceStates;
+ private ActionListener serviceSelListener = new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent ev) {
+ JCheckBox box = (JCheckBox) ev.getSource();
+ serviceStates.put(box.getName(), box.isSelected());
+ }
+ };
+
+ private IngestTopComponent() {
+ manager = new IngestManager(this);
+ services = new ArrayList();
+ serviceStates = new HashMap();
+ initComponents();
+ customizeComponents();
+ setName(NbBundle.getMessage(IngestTopComponent.class, "CTL_IngestTopComponent"));
+ setToolTipText(NbBundle.getMessage(IngestTopComponent.class, "HINT_IngestTopComponent"));
+ //putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
+ putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
+
+ }
+
+ public static synchronized IngestTopComponent getDefault() {
+ if (instance == null) {
+ instance = new IngestTopComponent();
+ }
+ return instance;
+ }
+
+ @Override
+ public TopComponent getTopComponent() {
+ return this;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ logger.log(Level.INFO, "Unhandled property change: " + evt.getPropertyName());
+ }
+
+ @Override
+ public int getPersistenceType() {
+ return TopComponent.PERSISTENCE_NEVER;
+ }
+
+ private void customizeComponents() {
+ //custom GUI setup not done by builder
+ freqSlider.setToolTipText("Lower update frequency can optimize performance of certain ingest services, but also reduce real time status feedback");
+
+ JScrollPane scrollPane = new JScrollPane(servicesPanel);
+ scrollPane.setPreferredSize(this.getSize());
+ this.add(scrollPane, BorderLayout.CENTER);
+
+ servicesPanel.setLayout(new BoxLayout(servicesPanel, BoxLayout.Y_AXIS));
+
+ Collection imageServices = IngestManager.enumerateImageServices();
+ for (IngestServiceImage service : imageServices) {
+ services.add(service);
+ JCheckBox checkbox = new JCheckBox(service.getName(), true);
+ checkbox.addActionListener(serviceSelListener);
+ servicesPanel.add(checkbox);
+ }
+
+ Collection fsServices = IngestManager.enumerateFsContentServices();
+ for (IngestServiceFsContent service : fsServices) {
+ services.add(service);
+ JCheckBox checkbox = new JCheckBox(service.getName(), true);
+ checkbox.addActionListener(serviceSelListener);
+ servicesPanel.add(checkbox);
+ }
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ mainScrollPane = new javax.swing.JScrollPane();
+ mainPanel = new javax.swing.JPanel();
+ topLable = new javax.swing.JLabel();
+ servicesPanel = new javax.swing.JPanel();
+ freqSlider = new javax.swing.JSlider();
+ startButton = new javax.swing.JButton();
+ refreshFreqLabel = new javax.swing.JLabel();
+ imageProgressBar = new javax.swing.JProgressBar();
+ imageProgressLabel = new javax.swing.JLabel();
+ jProgressBar1 = new javax.swing.JProgressBar();
+ fileProgressLabel = new javax.swing.JLabel();
+
+ mainScrollPane.setPreferredSize(new java.awt.Dimension(289, 509));
+
+ topLable.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(topLable, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.topLable.text")); // NOI18N
+
+ servicesPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+ servicesPanel.setMinimumSize(new java.awt.Dimension(200, 150));
+ servicesPanel.setPreferredSize(new java.awt.Dimension(200, 150));
+
+ javax.swing.GroupLayout servicesPanelLayout = new javax.swing.GroupLayout(servicesPanel);
+ servicesPanel.setLayout(servicesPanelLayout);
+ servicesPanelLayout.setHorizontalGroup(
+ servicesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 198, Short.MAX_VALUE)
+ );
+ servicesPanelLayout.setVerticalGroup(
+ servicesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 148, Short.MAX_VALUE)
+ );
+
+ freqSlider.setMajorTickSpacing(1);
+ freqSlider.setMaximum(10);
+ freqSlider.setMinimum(1);
+ freqSlider.setPaintLabels(true);
+ freqSlider.setPaintTicks(true);
+ freqSlider.setSnapToTicks(true);
+ freqSlider.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ freqSliderStateChanged(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(startButton, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.startButton.text")); // NOI18N
+ startButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ startButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(refreshFreqLabel, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.refreshFreqLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(imageProgressLabel, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.imageProgressLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(fileProgressLabel, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.fileProgressLabel.text")); // NOI18N
+
+ javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+ mainPanel.setLayout(mainPanelLayout);
+ mainPanelLayout.setHorizontalGroup(
+ mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, mainPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(topLable))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, mainPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jProgressBar1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(imageProgressBar, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(servicesPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(freqSlider, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ .addGap(173, 173, 173))
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(startButton)
+ .addContainerGap(316, Short.MAX_VALUE))
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGap(81, 81, 81)
+ .addComponent(imageProgressLabel)
+ .addContainerGap(227, Short.MAX_VALUE))
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGap(74, 74, 74)
+ .addComponent(refreshFreqLabel)
+ .addContainerGap(219, Short.MAX_VALUE))
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGap(84, 84, 84)
+ .addComponent(fileProgressLabel)
+ .addContainerGap(238, Short.MAX_VALUE))
+ );
+ mainPanelLayout.setVerticalGroup(
+ mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGap(24, 24, 24)
+ .addComponent(topLable)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(servicesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(startButton)
+ .addGap(18, 18, 18)
+ .addComponent(freqSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(refreshFreqLabel)
+ .addGap(28, 28, 28)
+ .addComponent(imageProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(imageProgressLabel)
+ .addGap(27, 27, 27)
+ .addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(fileProgressLabel)
+ .addContainerGap(75, Short.MAX_VALUE))
+ );
+
+ mainScrollPane.setViewportView(mainPanel);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 289, Short.MAX_VALUE)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 509, Short.MAX_VALUE)
+ );
+ }// //GEN-END:initComponents
+
+ private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonActionPerformed
+ //pick the services
+ ListservicesToStart = new ArrayList();
+ for (IngestServiceAbstract service : services) {
+ boolean serviceEnabled = serviceStates.get(service.getName());
+ if (serviceEnabled)
+ servicesToStart.add(service);
+ }
+
+ //pick the image
+ //TODO which image ? just enqueue all, and manager will skip already processed image
+
+ manager.execute(services, null);
+ }//GEN-LAST:event_startButtonActionPerformed
+
+ private void freqSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_freqSliderStateChanged
+ JSlider source = (JSlider) evt.getSource();
+ if (!source.getValueIsAdjusting()) {
+ final int refresh = (int) source.getValue();
+ manager.setUpdateFrequency(refresh);
+
+ }
+ }//GEN-LAST:event_freqSliderStateChanged
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel fileProgressLabel;
+ private javax.swing.JSlider freqSlider;
+ private javax.swing.JProgressBar imageProgressBar;
+ private javax.swing.JLabel imageProgressLabel;
+ private javax.swing.JProgressBar jProgressBar1;
+ private javax.swing.JPanel mainPanel;
+ private javax.swing.JScrollPane mainScrollPane;
+ private javax.swing.JLabel refreshFreqLabel;
+ private javax.swing.JPanel servicesPanel;
+ private javax.swing.JButton startButton;
+ private javax.swing.JLabel topLable;
+ // End of variables declaration//GEN-END:variables
+
+ @Override
+ public void componentOpened() {
+ logger.log(Level.INFO, "IngestTopComponent opened()");
+ }
+
+ @Override
+ public void componentClosed() {
+ logger.log(Level.INFO, "IngestTopComponent closed()");
+ }
+
+ void writeProperties(java.util.Properties p) {
+ // better to version settings since initial version as advocated at
+ // http://wiki.apidesign.org/wiki/PropertyFiles
+ p.setProperty("version", "1.0");
+
+ }
+
+ void readProperties(java.util.Properties p) {
+ String version = p.getProperty("version");
+
+ }
+
+ /**
+ * Display ingest summary report in some dialog
+ */
+ void displayReport(String ingestReport) {
+ //TODO widget
+ logger.log(Level.INFO, "INGEST REPORT: " + ingestReport);
+ }
+
+ /**
+ * Display IngestMessage from service (forwarded by IngestManager)
+ */
+ void displayMessage(IngestMessage ingestMessage) {
+ //TODO widget
+ logger.log(Level.INFO, "INGEST MESSAGE: " + ingestMessage.toString());
+ }
+}
diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml b/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml
new file mode 100644
index 0000000000..4c998edccd
--- /dev/null
+++ b/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/KeywordSearch/nbproject/genfiles.properties b/KeywordSearch/nbproject/genfiles.properties
index cc36a74245..6b82ce00cf 100644
--- a/KeywordSearch/nbproject/genfiles.properties
+++ b/KeywordSearch/nbproject/genfiles.properties
@@ -3,6 +3,6 @@ build.xml.script.CRC32=87b97b04
build.xml.stylesheet.CRC32=a56c6a5b@1.46.2
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=957d4757
+nbproject/build-impl.xml.data.CRC32=d7ecf067
nbproject/build-impl.xml.script.CRC32=fe1f48d2
-nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.47.1
+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.46.2
diff --git a/KeywordSearch/nbproject/project.xml b/KeywordSearch/nbproject/project.xml
index 3dad2b3c4c..7059590a8d 100644
--- a/KeywordSearch/nbproject/project.xml
+++ b/KeywordSearch/nbproject/project.xml
@@ -117,6 +117,15 @@
1.0
+
+ org.sleuthkit.autopsy.ingest
+
+
+
+ 0-1
+ 1.0
+
+
org.sleuthkit.autopsy.keywordsearch
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java
index 7082e80cfc..ab0e9a6d8e 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java
@@ -32,15 +32,13 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentatio
/**
* Provides a data explorer to perform Solr searches with
*/
-@ServiceProvider(service = DataExplorer.class, position = 300)
public class KeywordSearchDataExplorer implements DataExplorer {
- private static KeywordSearchDataExplorer theInstance;
+ private static KeywordSearchDataExplorer theInstance = null;
private KeywordSearchTabsTopComponent tc;
private int filesIndexed;
- public KeywordSearchDataExplorer() {
- this.setTheInstance();
+ private KeywordSearchDataExplorer() {
this.filesIndexed = 0;
this.tc = new KeywordSearchTabsTopComponent();
@@ -64,12 +62,11 @@ public class KeywordSearchDataExplorer implements DataExplorer {
KeywordSearch.changeSupport.addPropertyChangeListener(KeywordSearch.NUM_FILES_CHANGE_EVT, new IndexChangeListener());
}
- private synchronized void setTheInstance() {
+ public static synchronized KeywordSearchDataExplorer getDefault() {
if (theInstance == null) {
- theInstance = this;
- } else {
- throw new RuntimeException("Tried to instantiate mulitple instances of KeywordSearchTopComponent.");
- }
+ theInstance = new KeywordSearchDataExplorer();
+ }
+ return theInstance;
}
/**
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java
new file mode 100644
index 0000000000..ca3c433baa
--- /dev/null
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java
@@ -0,0 +1,64 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.keywordsearch;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.sleuthkit.autopsy.ingest.IngestManager;
+import org.sleuthkit.autopsy.ingest.IngestServiceFsContent;
+import org.sleuthkit.datamodel.FsContent;
+
+//service provider registered in layer.xml
+public final class KeywordSearchIngestService implements IngestServiceFsContent {
+
+ private static final Logger logger = Logger.getLogger(KeywordSearchIngestService.class.getName());
+ private static KeywordSearchIngestService instance = null;
+
+ public static synchronized KeywordSearchIngestService getDefault() {
+ if (instance == null) {
+ instance = new KeywordSearchIngestService();
+ }
+ return instance;
+ }
+
+ @Override
+ public void process(FsContent fsContent) {
+ logger.log(Level.INFO, "Processing fsContent: " + fsContent.getName());
+ }
+
+ @Override
+ public void complete() {
+ logger.log(Level.INFO, "complete()");
+ }
+
+ @Override
+ public String getName() {
+ return "Keyword Search";
+ }
+
+ @Override
+ public void init(IngestManager manager) {
+ logger.log(Level.INFO, "init()");
+ }
+
+ @Override
+ public void stop() {
+ logger.log(Level.INFO, "stop()");
+ }
+}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml
index 40f6e5e00b..06e2d7629d 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml
@@ -1,3 +1,16 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nbproject/project.properties b/nbproject/project.properties
index cce454d130..6e6debae3a 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -18,13 +18,15 @@ modules=\
${project.org.sleuthkit.autopsy.datamodel}:\
${project.org.sleuthkit.autopsy.casemodule}:\
${project.org.sleuthkit.autopsy.keywordsearch}:\
- ${project.org.sleuthkit.autopsy.coreutils}
+ ${project.org.sleuthkit.autopsy.coreutils}:\
+ ${project.org.sleuthkit.autopsy.ingest}
project.org.sleuthkit.autopsy.casemodule=Case
project.org.sleuthkit.autopsy.corecomponentinterfaces=CoreComponentInterfaces
project.org.sleuthkit.autopsy.corecomponents=CoreComponents
project.org.sleuthkit.autopsy.coreutils=CoreUtils
project.org.sleuthkit.autopsy.directorytree=DirectoryTree
project.org.sleuthkit.autopsy.filesearch=FileSearch
+project.org.sleuthkit.autopsy.ingest=Ingest
project.org.sleuthkit.autopsy.keywordsearch=KeywordSearch
project.org.sleuthkit.autopsy.menuactions=MenuActions
project.org.sleuthkit.autopsy.datamodel=DataModel