mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge remote-tracking branch 'upstream/develop' into 4444_DuplicateInterestingItems
This commit is contained in:
commit
dcc99aae4f
@ -31,6 +31,8 @@
|
||||
|
||||
<dependency conf="core->default" org="org.jsoup" name="jsoup" rev="1.10.3"/>
|
||||
<dependency conf="core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/>
|
||||
|
||||
<dependency conf="core->default" org="com.fasterxml.jackson.core" name="jackson-core" rev="2.9.7"/>
|
||||
|
||||
<dependency conf="core->default" org="commons-validator" name="commons-validator" rev="1.6"/>
|
||||
|
||||
|
@ -4,6 +4,7 @@ file.reference.commons-compress-1.14.jar=release/modules/ext/commons-compress-1.
|
||||
file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.1.jar
|
||||
file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar
|
||||
file.reference.dd-plist-1.20.jar=release/modules/ext/dd-plist-1.20.jar
|
||||
file.reference.jackson-core-2.9.7.jar=release/modules/ext/jackson-core-2.9.7.jar
|
||||
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.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar
|
||||
|
@ -433,6 +433,10 @@
|
||||
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jackson-core-2.9.7.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jackson-core-2.9.7.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/cxf-rt-frontend-jaxrs-3.0.16.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar</binary-origin>
|
||||
|
@ -52,27 +52,29 @@ final public class CorrelationAttributeNormalizer {
|
||||
throw new CorrelationAttributeNormalizationException("Data was null.");
|
||||
}
|
||||
|
||||
String trimmedData = data.trim();
|
||||
|
||||
switch(attributeType.getId()){
|
||||
case CorrelationAttributeInstance.FILES_TYPE_ID:
|
||||
return normalizeMd5(data);
|
||||
return normalizeMd5(trimmedData);
|
||||
case CorrelationAttributeInstance.DOMAIN_TYPE_ID:
|
||||
return normalizeDomain(data);
|
||||
return normalizeDomain(trimmedData);
|
||||
case CorrelationAttributeInstance.EMAIL_TYPE_ID:
|
||||
return normalizeEmail(data);
|
||||
return normalizeEmail(trimmedData);
|
||||
case CorrelationAttributeInstance.PHONE_TYPE_ID:
|
||||
return normalizePhone(data);
|
||||
return normalizePhone(trimmedData);
|
||||
case CorrelationAttributeInstance.USBID_TYPE_ID:
|
||||
return normalizeUsbId(data);
|
||||
return normalizeUsbId(trimmedData);
|
||||
case CorrelationAttributeInstance.SSID_TYPE_ID:
|
||||
return data;
|
||||
return trimmedData;
|
||||
case CorrelationAttributeInstance.MAC_TYPE_ID:
|
||||
return data;
|
||||
return trimmedData;
|
||||
case CorrelationAttributeInstance.IMEI_TYPE_ID:
|
||||
return data;
|
||||
return trimmedData;
|
||||
case CorrelationAttributeInstance.IMSI_TYPE_ID:
|
||||
return data;
|
||||
return trimmedData;
|
||||
case CorrelationAttributeInstance.ICCID_TYPE_ID:
|
||||
return data;
|
||||
return trimmedData;
|
||||
default:
|
||||
final String errorMessage = String.format(
|
||||
"Validator function not found for attribute type: %s",
|
||||
|
@ -62,6 +62,7 @@ public class IngestEventsListener {
|
||||
final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
|
||||
private static int correlationModuleInstanceCount;
|
||||
private static boolean flagNotableItems;
|
||||
private static boolean flagSeenDevices;
|
||||
private final ExecutorService jobProcessingExecutor;
|
||||
private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d";
|
||||
private final PropertyChangeListener pcl1 = new IngestModuleEventListener();
|
||||
@ -136,6 +137,15 @@ public class IngestEventsListener {
|
||||
return flagNotableItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are previously seen devices being flagged?
|
||||
*
|
||||
* @return True if flagging seen devices; otherwise false.
|
||||
*/
|
||||
public synchronized static boolean isFlagSeenDevices() {
|
||||
return flagSeenDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the listener to flag notable items or not.
|
||||
*
|
||||
@ -145,6 +155,15 @@ public class IngestEventsListener {
|
||||
flagNotableItems = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the listener to flag previously seen devices or not.
|
||||
*
|
||||
* @param value True to flag seen devices; otherwise false.
|
||||
*/
|
||||
public synchronized static void setFlagSeenDevices(boolean value) {
|
||||
flagSeenDevices = value;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
|
||||
"IngestEventsListener.prevCaseComment.text=Previous Case: ",
|
||||
"IngestEventsListener.ingestmodule.name=Correlation Engine"})
|
||||
@ -186,6 +205,46 @@ public class IngestEventsListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Interesting Aritfact hit for a device which was previously seen
|
||||
* in the central repository.
|
||||
*
|
||||
* @param bbArtifact the artifact to create the interesting item for
|
||||
*/
|
||||
@NbBundle.Messages({"IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)",
|
||||
"# {0} - typeName",
|
||||
"# {1} - count",
|
||||
"IngestEventsListener.prevCount.text=Number of previous {0}: {1}"})
|
||||
static private void postCorrelatedPreviousArtifactToBlackboard(BlackboardArtifact bbArtifact) {
|
||||
|
||||
try {
|
||||
AbstractFile af = bbArtifact.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID());
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name();
|
||||
BlackboardArtifact tifArtifact = af.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
|
||||
BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
|
||||
Bundle.IngestEventsListener_prevExists_text());
|
||||
attributes.add(att);
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID()));
|
||||
|
||||
tifArtifact.addAttributes(attributes);
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
Blackboard blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
|
||||
blackboard.indexArtifact(tifArtifact);
|
||||
} catch (Blackboard.BlackboardException | NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
// fire event to notify UI of this new artifact
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
|
||||
} catch (IllegalStateException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
private class IngestModuleEventListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
@ -205,7 +264,8 @@ public class IngestEventsListener {
|
||||
case DATA_ADDED: {
|
||||
//if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items
|
||||
boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems();
|
||||
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable));
|
||||
boolean flagPrevious = !IngestManager.getInstance().isIngestRunning() || isFlagSeenDevices();
|
||||
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -244,11 +304,13 @@ public class IngestEventsListener {
|
||||
private final EamDb dbManager;
|
||||
private final PropertyChangeEvent event;
|
||||
private final boolean flagNotableItemsEnabled;
|
||||
private final boolean flagPreviousItemsEnabled;
|
||||
|
||||
private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled) {
|
||||
private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled) {
|
||||
dbManager = db;
|
||||
event = evt;
|
||||
this.flagNotableItemsEnabled = flagNotableItemsEnabled;
|
||||
this.flagPreviousItemsEnabled = flagPreviousItemsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -286,6 +348,21 @@ public class IngestEventsListener {
|
||||
LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex);
|
||||
}
|
||||
}
|
||||
if (flagPreviousItemsEnabled
|
||||
&& (eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.USBID_TYPE_ID
|
||||
|| eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.ICCID_TYPE_ID
|
||||
|| eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMEI_TYPE_ID
|
||||
|| eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMSI_TYPE_ID
|
||||
|| eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.MAC_TYPE_ID)) {
|
||||
try {
|
||||
Long countPreviousOccurences = dbManager.getCountArtifactInstancesByTypeValue(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue());
|
||||
if (countPreviousOccurences > 0) {
|
||||
postCorrelatedPreviousArtifactToBlackboard(bbArtifact);
|
||||
}
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex);
|
||||
}
|
||||
}
|
||||
eamArtifacts.add(eamArtifact);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
|
@ -1,2 +1,3 @@
|
||||
IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings
|
||||
IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
|
||||
IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag previously seen devices
|
||||
|
@ -65,6 +65,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
final class IngestModule implements FileIngestModule {
|
||||
|
||||
static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true;
|
||||
static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = true;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(IngestModule.class.getName());
|
||||
private final IngestServices services = IngestServices.getInstance();
|
||||
@ -76,6 +77,7 @@ final class IngestModule implements FileIngestModule {
|
||||
private Blackboard blackboard;
|
||||
private CorrelationAttributeInstance.Type filesType;
|
||||
private final boolean flagTaggedNotableItems;
|
||||
private final boolean flagPreviouslySeenDevices;
|
||||
|
||||
/**
|
||||
* Instantiate the Correlation Engine ingest module.
|
||||
@ -84,6 +86,7 @@ final class IngestModule implements FileIngestModule {
|
||||
*/
|
||||
IngestModule(IngestSettings settings) {
|
||||
flagTaggedNotableItems = settings.isFlagTaggedNotableItems();
|
||||
flagPreviouslySeenDevices = settings.isFlagPreviousDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -232,6 +235,9 @@ final class IngestModule implements FileIngestModule {
|
||||
if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagNotableItems()) {
|
||||
IngestEventsListener.setFlagNotableItems(flagTaggedNotableItems);
|
||||
}
|
||||
if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagSeenDevices()) {
|
||||
IngestEventsListener.setFlagSeenDevices(flagPreviouslySeenDevices);
|
||||
}
|
||||
|
||||
if (EamDb.isEnabled() == false) {
|
||||
/*
|
||||
|
@ -28,21 +28,25 @@ final class IngestSettings implements IngestModuleIngestJobSettings {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean flagTaggedNotableItems;
|
||||
private boolean flagPreviousDevices;
|
||||
|
||||
/**
|
||||
* Instantiate the ingest job settings with default values.
|
||||
*/
|
||||
IngestSettings() {
|
||||
this.flagTaggedNotableItems = IngestModule.DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS;
|
||||
this.flagPreviousDevices = IngestModule.DEFAULT_FLAG_PREVIOUS_DEVICES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the ingest job settings.
|
||||
*
|
||||
* @param flagTaggedNotableItems Flag previously tagged notable items.
|
||||
* @param flagPreviousDevices Flag devices which exist already in the Central Repository
|
||||
*/
|
||||
IngestSettings(boolean flagTaggedNotableItems) {
|
||||
IngestSettings(boolean flagTaggedNotableItems, boolean flagPreviousDevices) {
|
||||
this.flagTaggedNotableItems = flagTaggedNotableItems;
|
||||
this.flagPreviousDevices = flagPreviousDevices;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,12 +64,11 @@ final class IngestSettings implements IngestModuleIngestJobSettings {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag or ignore previously identified notable items.
|
||||
* Are previously seen devices to be flagged?
|
||||
*
|
||||
* @param ignorePreviousNotableItems Are previously tagged notable items to
|
||||
* be flagged?
|
||||
* @return True if flagging; otherwise false.
|
||||
*/
|
||||
void setFlagTaggedNotableItems(boolean flagTaggedNotableItems) {
|
||||
this.flagTaggedNotableItems = flagTaggedNotableItems;
|
||||
boolean isFlagPreviousDevices() {
|
||||
return flagPreviousDevices;
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,14 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="flagTaggedNotableItemsCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="ingestSettingsLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="flagPreviouslySeenDevicesCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="flagTaggedNotableItemsCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace pref="65" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
@ -36,7 +39,9 @@
|
||||
<Component id="ingestSettingsLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="flagTaggedNotableItemsCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="245" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="flagPreviouslySeenDevicesCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="222" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -59,5 +64,12 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="flagPreviouslySeenDevicesCheckbox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties" key="IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -42,11 +42,12 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
*/
|
||||
private void customizeComponents(IngestSettings settings) {
|
||||
flagTaggedNotableItemsCheckbox.setSelected(settings.isFlagTaggedNotableItems());
|
||||
flagPreviouslySeenDevicesCheckbox.setSelected(settings.isFlagPreviousDevices());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestModuleIngestJobSettings getSettings() {
|
||||
return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected());
|
||||
return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected(), flagPreviouslySeenDevicesCheckbox.isSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,12 +61,15 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
|
||||
ingestSettingsLabel = new javax.swing.JLabel();
|
||||
flagTaggedNotableItemsCheckbox = new javax.swing.JCheckBox();
|
||||
flagPreviouslySeenDevicesCheckbox = new javax.swing.JCheckBox();
|
||||
|
||||
ingestSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ingestSettingsLabel, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.ingestSettingsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(flagTaggedNotableItemsCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(flagPreviouslySeenDevicesCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -73,10 +77,12 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(ingestSettingsLabel)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addComponent(flagTaggedNotableItemsCheckbox))
|
||||
.addComponent(ingestSettingsLabel))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(flagPreviouslySeenDevicesCheckbox)
|
||||
.addComponent(flagTaggedNotableItemsCheckbox))))
|
||||
.addContainerGap(65, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
@ -86,11 +92,14 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
.addComponent(ingestSettingsLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(flagTaggedNotableItemsCheckbox)
|
||||
.addContainerGap(245, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(flagPreviouslySeenDevicesCheckbox)
|
||||
.addContainerGap(222, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox flagPreviouslySeenDevicesCheckbox;
|
||||
private javax.swing.JCheckBox flagTaggedNotableItemsCheckbox;
|
||||
private javax.swing.JLabel ingestSettingsLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
@ -58,7 +58,7 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="errorText" pref="300" max="32767" attributes="0"/>
|
||||
<Component id="errorText" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="65" max="-2" attributes="0"/>
|
||||
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
@ -77,7 +77,7 @@
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" max="-2" attributes="0">
|
||||
<Component id="commonItemSearchDescription" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="commonItemSearchDescription" alignment="0" pref="403" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceComboBoxModel;
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceLoader;
|
||||
import java.awt.Dimension;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceComboBoxModel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceComboBoxModel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -337,6 +337,11 @@
|
||||
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.modules.stix.STIXReportModule.getDefault"/>
|
||||
<attr name="position" intvalue="910"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-modules-case_uco-ReportCaseUco.instance">
|
||||
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.report.GeneralReportModule"/>
|
||||
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.modules.case_uco.ReportCaseUco.getDefault"/>
|
||||
<attr name="position" intvalue="911"/>
|
||||
</file>
|
||||
<!--<folder name="JavaHelp">
|
||||
<file name="casemodule-helpset.xml" url="casemodule-helpset.xml">
|
||||
<attr name="position" intvalue="3075"/>
|
||||
|
@ -322,11 +322,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.displayName=Artifact Details",
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.name=Artifact Details",
|
||||
"BlackboardArtifactNode.artifact.displayName=Artifact",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
|
||||
"BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
|
||||
"BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
|
||||
"BlackboardArtifactNode.createSheet.fileSize.name=Size",
|
||||
@ -372,7 +371,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
|
||||
NO_DESCR,
|
||||
associatedArtifact.getDisplayName() + " " + NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.artifact.displayName")));
|
||||
associatedArtifact.getDisplayName()));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
|
||||
NO_DESCR,
|
||||
|
@ -411,11 +411,15 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
|
||||
artifactHits.put(id, art);
|
||||
}
|
||||
list.add(id);
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
||||
// Adding all keys at once is more efficient than adding one at a
|
||||
// time because Netbeans triggers internal processing each time an
|
||||
// element is added to the list.
|
||||
list.addAll(artifactHits.keySet());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
package org.sleuthkit.autopsy.guiutils;
|
||||
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.ComboBoxModel;
|
||||
@ -44,7 +44,7 @@ public class DataSourceComboBoxModel extends AbstractListModel<String> implement
|
||||
*
|
||||
* @param theDataSoureList names of data sources for user to pick from
|
||||
*/
|
||||
DataSourceComboBoxModel(String... theDataSoureList) {
|
||||
public DataSourceComboBoxModel(String... theDataSoureList) {
|
||||
dataSourceList = theDataSoureList.clone();
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
package org.sleuthkit.autopsy.guiutils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.ResultSet;
|
4
Core/src/org/sleuthkit/autopsy/modules/case_uco/Bundle.properties
Executable file
4
Core/src/org/sleuthkit/autopsy/modules/case_uco/Bundle.properties
Executable file
@ -0,0 +1,4 @@
|
||||
OpenIDE-Module-Name=CaseUcoModule
|
||||
ReportCaseUco.getName.text=CASE-UCO
|
||||
ReportCaseUco.getDesc.text=CASE-UCO format report with basic property fields for every file.
|
||||
ReportCaseUcoConfigPanel.jLabelSelectDataSource.text=Select a data source for the CASE-UCO report
|
458
Core/src/org/sleuthkit/autopsy/modules/case_uco/ReportCaseUco.java
Executable file
458
Core/src/org/sleuthkit/autopsy/modules/case_uco/ReportCaseUco.java
Executable file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2018 Basis Technology Corp.
|
||||
* Project Contact/Architect: 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.modules.case_uco;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.SimpleTimeZone;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
|
||||
import org.sleuthkit.datamodel.*;
|
||||
|
||||
/**
|
||||
* ReportCaseUco generates a report in the CASE-UCO format. It saves basic
|
||||
file info like full caseDirPath, name, MIME type, times, and hash.
|
||||
*/
|
||||
class ReportCaseUco implements GeneralReportModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
|
||||
private static ReportCaseUco instance = null;
|
||||
private ReportCaseUcoConfigPanel configPanel;
|
||||
|
||||
private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
|
||||
|
||||
// Hidden constructor for the report
|
||||
private ReportCaseUco() {
|
||||
}
|
||||
|
||||
// Get the default implementation of this report
|
||||
public static synchronized ReportCaseUco getDefault() {
|
||||
if (instance == null) {
|
||||
instance = new ReportCaseUco();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a CASE-UCO format report.
|
||||
*
|
||||
* @param baseReportDir caseDirPath to save the report
|
||||
* @param progressPanel panel to update the report's progress
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
|
||||
"ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report",
|
||||
"ReportCaseUco.noCaseOpen=Unable to open currect case",
|
||||
"ReportCaseUco.unableToCreateDirectories=Unable to create directory for CASE-UCO report",
|
||||
"ReportCaseUco.initializing=Creating directories...",
|
||||
"ReportCaseUco.querying=Querying files...",
|
||||
"ReportCaseUco.ingestWarning=Warning, this report will be created before ingest services completed",
|
||||
"ReportCaseUco.processing=Saving files in CASE-UCO format...",
|
||||
"ReportCaseUco.srcModuleName.text=CASE-UCO Report"
|
||||
})
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
|
||||
|
||||
if (configPanel == null) {
|
||||
logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
|
||||
if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
|
||||
logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the progress bar and setup the report
|
||||
progressPanel.setIndeterminate(false);
|
||||
progressPanel.start();
|
||||
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_initializing());
|
||||
|
||||
// Create the JSON generator
|
||||
JsonFactory jsonGeneratorFactory = new JsonFactory();
|
||||
String reportPath = baseReportDir + getRelativeFilePath();
|
||||
java.io.File reportFile = Paths.get(reportPath).toFile();
|
||||
try {
|
||||
Files.createDirectories(Paths.get(reportFile.getParent()));
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to create directory for CASE-UCO report", ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_unableToCreateDirectories());
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if ingest has finished
|
||||
if (IngestManager.getInstance().isIngestRunning()) {
|
||||
MessageNotifyUtil.Message.warn(Bundle.ReportCaseUco_ingestWarning());
|
||||
}
|
||||
|
||||
|
||||
JsonGenerator jsonGenerator = null;
|
||||
SimpleTimeZone timeZone = new SimpleTimeZone(0, "GMT");
|
||||
try {
|
||||
jsonGenerator = jsonGeneratorFactory.createGenerator(reportFile, JsonEncoding.UTF8);
|
||||
// instert \n after each field for more readable formatting
|
||||
jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter().withObjectIndenter(new DefaultIndenter(" ", "\n")));
|
||||
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
|
||||
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_querying());
|
||||
|
||||
// create the required CASE-UCO entries at the beginning of the output file
|
||||
initializeJsonOutputFile(jsonGenerator);
|
||||
|
||||
// create CASE-UCO entry for the Autopsy case
|
||||
String caseTraceId = saveCaseInfo(skCase, jsonGenerator);
|
||||
|
||||
// create CASE-UCO data source entry
|
||||
String dataSourceTraceId = saveDataSourceInfo(selectedDataSourceId, caseTraceId, skCase, jsonGenerator);
|
||||
|
||||
// Run getAllFilesQuery to get all files, exclude directories
|
||||
final String getAllFilesQuery = "select obj_id, name, size, crtime, atime, mtime, md5, parent_path, mime_type, extension from tsk_files where "
|
||||
+ "data_source_obj_id = " + Long.toString(selectedDataSourceId)
|
||||
+ " AND ((meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.getValue()
|
||||
+ ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
|
||||
+ ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue() + "))"; //NON-NLS
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getAllFilesQuery)) {
|
||||
ResultSet resultSet = queryResult.getResultSet();
|
||||
|
||||
progressPanel.updateStatusLabel(Bundle.ReportCaseUco_processing());
|
||||
|
||||
// Loop files and write info to CASE-UCO report
|
||||
while (resultSet.next()) {
|
||||
|
||||
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
|
||||
break;
|
||||
}
|
||||
|
||||
Long objectId = resultSet.getLong(1);
|
||||
String fileName = resultSet.getString(2);
|
||||
long size = resultSet.getLong("size");
|
||||
String crtime = ContentUtils.getStringTimeISO8601(resultSet.getLong("crtime"), timeZone);
|
||||
String atime = ContentUtils.getStringTimeISO8601(resultSet.getLong("atime"), timeZone);
|
||||
String mtime = ContentUtils.getStringTimeISO8601(resultSet.getLong("mtime"), timeZone);
|
||||
String md5Hash = resultSet.getString("md5");
|
||||
String parent_path = resultSet.getString("parent_path");
|
||||
String mime_type = resultSet.getString("mime_type");
|
||||
String extension = resultSet.getString("extension");
|
||||
|
||||
saveFileInCaseUcoFormat(objectId, fileName, parent_path, md5Hash, mime_type, size, crtime, atime, mtime, extension, jsonGenerator, dataSourceTraceId);
|
||||
}
|
||||
}
|
||||
|
||||
// create the required CASE-UCO entries at the end of the output file
|
||||
finilizeJsonOutputFile(jsonGenerator);
|
||||
|
||||
Case.getCurrentCaseThrows().addReport(reportPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
|
||||
|
||||
progressPanel.complete(ReportStatus.COMPLETE);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get list of files from case database", ex); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create JSON output for the CASE-UCO report", ex); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Unable to read result set", ex); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "No current case open", ex); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR);
|
||||
} finally {
|
||||
if (jsonGenerator != null) {
|
||||
try {
|
||||
jsonGenerator.close();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Failed to close JSON output file", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
|
||||
catalog.writeStartObject();
|
||||
catalog.writeFieldName("@graph");
|
||||
catalog.writeStartArray();
|
||||
}
|
||||
|
||||
private void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
|
||||
catalog.writeEndArray();
|
||||
catalog.writeEndObject();
|
||||
}
|
||||
|
||||
private String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
|
||||
|
||||
// create a "trace" entry for the Autopsy case iteself
|
||||
String uniqueCaseName;
|
||||
String dbFileName;
|
||||
TskData.DbType dbType = skCase.getDatabaseType();
|
||||
if (dbType == TskData.DbType.SQLITE) {
|
||||
uniqueCaseName = Case.getCurrentCaseThrows().getName();
|
||||
dbFileName = skCase.getDatabaseName();
|
||||
} else {
|
||||
uniqueCaseName = skCase.getDatabaseName();
|
||||
dbFileName = "";
|
||||
}
|
||||
|
||||
String caseDirPath = skCase.getDbDirPath();
|
||||
String caseTraceId = "case-" + uniqueCaseName;
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@id", caseTraceId);
|
||||
catalog.writeStringField("@type", "Trace");
|
||||
|
||||
catalog.writeFieldName("propertyBundle");
|
||||
catalog.writeStartArray();
|
||||
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "File");
|
||||
if (dbType == TskData.DbType.SQLITE) {
|
||||
catalog.writeStringField("filePath", caseDirPath + java.io.File.separator + dbFileName);
|
||||
catalog.writeBooleanField("isDirectory", false);
|
||||
} else {
|
||||
catalog.writeStringField("filePath", caseDirPath);
|
||||
catalog.writeBooleanField("isDirectory", true);
|
||||
}
|
||||
catalog.writeEndObject();
|
||||
|
||||
catalog.writeEndArray();
|
||||
catalog.writeEndObject();
|
||||
|
||||
return caseTraceId;
|
||||
}
|
||||
|
||||
private String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
|
||||
|
||||
Long imageSize = (long) 0;
|
||||
String imageName = "";
|
||||
boolean isImageDataSource = false;
|
||||
String getImageDataSourceQuery = "select size from tsk_image_info where obj_id = " + selectedDataSourceId;
|
||||
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getImageDataSourceQuery)) {
|
||||
ResultSet resultSet = queryResult.getResultSet();
|
||||
// check if we got a result
|
||||
while (resultSet.next()) {
|
||||
// we got a result so the data source was an image data source
|
||||
imageSize = resultSet.getLong(1);
|
||||
isImageDataSource = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isImageDataSource) {
|
||||
// get caseDirPath to image file
|
||||
String getPathToDataSourceQuery = "select name from tsk_image_names where obj_id = " + selectedDataSourceId;
|
||||
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getPathToDataSourceQuery)) {
|
||||
ResultSet resultSet = queryResult.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
imageName = resultSet.getString(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// logical file data source
|
||||
String getLogicalDataSourceQuery = "select name from tsk_files where obj_id = " + selectedDataSourceId;
|
||||
try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getLogicalDataSourceQuery)) {
|
||||
ResultSet resultSet = queryResult.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
imageName = resultSet.getString(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveDataSourceInCaseUcoFormat(jsonGenerator, imageName, imageSize, selectedDataSourceId, caseTraceId);
|
||||
}
|
||||
|
||||
private String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
|
||||
|
||||
// create a "trace" entry for the data source
|
||||
String dataSourceTraceId = "data-source-"+selectedDataSourceId;
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@id", dataSourceTraceId);
|
||||
catalog.writeStringField("@type", "Trace");
|
||||
|
||||
catalog.writeFieldName("propertyBundle");
|
||||
catalog.writeStartArray();
|
||||
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "File");
|
||||
catalog.writeStringField("filePath", imageName);
|
||||
catalog.writeEndObject();
|
||||
|
||||
if (imageSize > 0) {
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "ContentData");
|
||||
catalog.writeStringField("sizeInBytes", Long.toString(imageSize));
|
||||
catalog.writeEndObject();
|
||||
}
|
||||
|
||||
catalog.writeEndArray();
|
||||
catalog.writeEndObject();
|
||||
|
||||
// create a "relationship" entry between the case and the data source
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@id", "relationship-" + caseTraceId);
|
||||
catalog.writeStringField("@type", "Relationship");
|
||||
catalog.writeStringField("source", dataSourceTraceId);
|
||||
catalog.writeStringField("target", caseTraceId);
|
||||
catalog.writeStringField("kindOfRelationship", "contained-within");
|
||||
catalog.writeBooleanField("isDirectional", true);
|
||||
|
||||
catalog.writeFieldName("propertyBundle");
|
||||
catalog.writeStartArray();
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "PathRelation");
|
||||
catalog.writeStringField("path", imageName);
|
||||
catalog.writeEndObject();
|
||||
catalog.writeEndArray();
|
||||
|
||||
catalog.writeEndObject();
|
||||
|
||||
return dataSourceTraceId;
|
||||
}
|
||||
|
||||
private void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
|
||||
String atime, String mtime, String extension, JsonGenerator catalog, String dataSourceTraceId) throws IOException {
|
||||
|
||||
String fileTraceId = "file-" + objectId;
|
||||
|
||||
// create a "trace" entry for the file
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@id", fileTraceId);
|
||||
catalog.writeStringField("@type", "Trace");
|
||||
|
||||
catalog.writeFieldName("propertyBundle");
|
||||
catalog.writeStartArray();
|
||||
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "File");
|
||||
catalog.writeStringField("createdTime", ctime);
|
||||
catalog.writeStringField("accessedTime", atime);
|
||||
catalog.writeStringField("modifiedTime", mtime);
|
||||
if (extension != null) {
|
||||
catalog.writeStringField("extension", extension);
|
||||
}
|
||||
catalog.writeStringField("fileName", fileName);
|
||||
if (parent_path != null) {
|
||||
catalog.writeStringField("filePath", parent_path + fileName);
|
||||
}
|
||||
catalog.writeBooleanField("isDirectory", false);
|
||||
catalog.writeStringField("sizeInBytes", Long.toString(size));
|
||||
catalog.writeEndObject();
|
||||
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "ContentData");
|
||||
if (mime_type != null) {
|
||||
catalog.writeStringField("mimeType", mime_type);
|
||||
}
|
||||
if (md5Hash != null) {
|
||||
catalog.writeFieldName("hash");
|
||||
catalog.writeStartArray();
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "Hash");
|
||||
catalog.writeStringField("hashMethod", "SHA256");
|
||||
catalog.writeStringField("hashValue", md5Hash);
|
||||
catalog.writeEndObject();
|
||||
catalog.writeEndArray();
|
||||
}
|
||||
catalog.writeStringField("sizeInBytes", Long.toString(size));
|
||||
|
||||
catalog.writeEndObject();
|
||||
|
||||
catalog.writeEndArray();
|
||||
catalog.writeEndObject();
|
||||
|
||||
// create a "relationship" entry between the file and the data source
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@id", "relationship-" + objectId);
|
||||
catalog.writeStringField("@type", "Relationship");
|
||||
catalog.writeStringField("source", fileTraceId);
|
||||
catalog.writeStringField("target", dataSourceTraceId);
|
||||
catalog.writeStringField("kindOfRelationship", "contained-within");
|
||||
catalog.writeBooleanField("isDirectional", true);
|
||||
|
||||
catalog.writeFieldName("propertyBundle");
|
||||
catalog.writeStartArray();
|
||||
catalog.writeStartObject();
|
||||
catalog.writeStringField("@type", "PathRelation");
|
||||
if (parent_path != null) {
|
||||
catalog.writeStringField("path", parent_path + fileName);
|
||||
} else {
|
||||
catalog.writeStringField("path", fileName);
|
||||
}
|
||||
catalog.writeEndObject();
|
||||
catalog.writeEndArray();
|
||||
|
||||
catalog.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativeFilePath() {
|
||||
return REPORT_FILE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getConfigurationPanel() {
|
||||
try {
|
||||
configPanel = new ReportCaseUcoConfigPanel();
|
||||
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
|
||||
configPanel = null;
|
||||
}
|
||||
return configPanel;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="jLabelSelectDataSource" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="selectDataSourceComboBox" pref="348" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
|
||||
<Component id="jLabelSelectDataSource" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="selectDataSourceComboBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="130" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JComboBox" name="selectDataSourceComboBox">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dataSourcesList" type="code"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabelSelectDataSource">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/case_uco/Bundle.properties" key="ReportCaseUcoConfigPanel.jLabelSelectDataSource.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
133
Core/src/org/sleuthkit/autopsy/modules/case_uco/ReportCaseUcoConfigPanel.java
Executable file
133
Core/src/org/sleuthkit/autopsy/modules/case_uco/ReportCaseUcoConfigPanel.java
Executable file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 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.modules.case_uco;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceLoader;
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceComboBoxModel;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* UI controls for CASE-UCO report. It is a panel which provides the
|
||||
* ability to select a single datasource from a dropdown list
|
||||
* of sources in the current case.
|
||||
*/
|
||||
final class ReportCaseUcoConfigPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final long NO_DATA_SOURCE_SELECTED = -1;
|
||||
private ComboBoxModel<String> dataSourcesList = new DataSourceComboBoxModel();
|
||||
private final Map<Long, String> dataSourceMap;
|
||||
private final DataSourceLoader dataSourceLoader;
|
||||
|
||||
/**
|
||||
* Creates new form ReportCaseUcoConfigPanel
|
||||
*/
|
||||
ReportCaseUcoConfigPanel() throws NoCurrentCaseException, TskCoreException, SQLException {
|
||||
initComponents();
|
||||
this.dataSourceLoader = new DataSourceLoader();
|
||||
this.dataSourceMap = dataSourceLoader.getDataSourceMap();
|
||||
|
||||
String[] dataSourcesNames = new String[dataSourceMap.size()];
|
||||
if (dataSourcesNames.length > 0) {
|
||||
dataSourcesNames = dataSourceMap.values().toArray(dataSourcesNames);
|
||||
setDatasourceComboboxModel(new DataSourceComboBoxModel(dataSourcesNames));
|
||||
|
||||
selectDataSourceComboBox.setEnabled(true);
|
||||
selectDataSourceComboBox.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID for the datasource selected in the combo box.
|
||||
*
|
||||
* @return the selected datasource ID or
|
||||
ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED if none is selected
|
||||
*/
|
||||
Long getSelectedDataSourceId() {
|
||||
for (Entry<Long, String> entry : this.dataSourceMap.entrySet()) {
|
||||
if (entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
selectDataSourceComboBox = new javax.swing.JComboBox<>();
|
||||
jLabelSelectDataSource = new javax.swing.JLabel();
|
||||
|
||||
selectDataSourceComboBox.setModel(dataSourcesList);
|
||||
selectDataSourceComboBox.setEnabled(false);
|
||||
|
||||
jLabelSelectDataSource.setText(org.openide.util.NbBundle.getMessage(ReportCaseUcoConfigPanel.class, "ReportCaseUcoConfigPanel.jLabelSelectDataSource.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(21, 21, 21)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jLabelSelectDataSource)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addComponent(selectDataSourceComboBox, 0, 348, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(11, 11, 11)
|
||||
.addComponent(jLabelSelectDataSource)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(130, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel jLabelSelectDataSource;
|
||||
private javax.swing.JComboBox<String> selectDataSourceComboBox;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Set the datamodel for the combo box which displays the data sources in
|
||||
* the current case
|
||||
*
|
||||
* @param dataSourceComboBoxModel the DataSourceComboBoxModel to use
|
||||
*/
|
||||
void setDatasourceComboboxModel(DataSourceComboBoxModel dataSourceComboBoxModel) {
|
||||
this.dataSourcesList = dataSourceComboBoxModel;
|
||||
this.selectDataSourceComboBox.setModel(dataSourcesList);
|
||||
}
|
||||
}
|
@ -58,6 +58,7 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
|
||||
@ -82,6 +83,7 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
private FileTypeDetector fileTypeDetector;
|
||||
private final HashSet<String> supportedMimeTypes = new HashSet<>();
|
||||
private TimeZone timeZone = null;
|
||||
private Case currentCase;
|
||||
private Blackboard blackboard;
|
||||
|
||||
ExifParserFileIngestModule() {
|
||||
@ -104,7 +106,8 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
@Override
|
||||
public ProcessResult process(AbstractFile content) {
|
||||
try {
|
||||
blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
blackboard = currentCase.getServices().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
@ -142,12 +145,12 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
@Messages({"ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search."})
|
||||
ProcessResult processFile(AbstractFile f) {
|
||||
ProcessResult processFile(AbstractFile file) {
|
||||
InputStream in = null;
|
||||
BufferedInputStream bin = null;
|
||||
|
||||
try {
|
||||
in = new ReadContentInputStream(f);
|
||||
in = new ReadContentInputStream(file);
|
||||
bin = new BufferedInputStream(in);
|
||||
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
@ -160,7 +163,7 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
// set the timeZone for the current datasource.
|
||||
if (timeZone == null) {
|
||||
try {
|
||||
Content dataSource = f.getDataSource();
|
||||
Content dataSource = file.getDataSource();
|
||||
if ((dataSource != null) && (dataSource instanceof Image)) {
|
||||
Image image = (Image) dataSource;
|
||||
timeZone = TimeZone.getTimeZone(image.getTimeZone());
|
||||
@ -208,18 +211,23 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
|
||||
// Add the attributes, if there are any, to a new artifact
|
||||
if (!attributes.isEmpty()) {
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
|
||||
bba.addAttributes(attributes);
|
||||
SleuthkitCase tskCase = currentCase.getSleuthkitCase();
|
||||
org.sleuthkit.datamodel.Blackboard tskBlackboard = tskCase.getBlackboard();
|
||||
// Create artifact if it doesn't already exist.
|
||||
if (!tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF, attributes)) {
|
||||
BlackboardArtifact bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
|
||||
bba.addAttributes(attributes);
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(bba);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(
|
||||
Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(bba);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(
|
||||
Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
|
||||
}
|
||||
filesToFire = true;
|
||||
}
|
||||
filesToFire = true;
|
||||
}
|
||||
|
||||
return ProcessResult.OK;
|
||||
@ -227,13 +235,13 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
||||
logger.log(Level.WARNING, "Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage()); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
} catch (ImageProcessingException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to process the image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex);
|
||||
logger.log(Level.WARNING, String.format("Failed to process the image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex);
|
||||
return ProcessResult.ERROR;
|
||||
} catch (ReadContentInputStreamException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error while trying to read image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex); //NON-NLS
|
||||
logger.log(Level.WARNING, String.format("Error while trying to read image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, String.format("IOException when parsing image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex); //NON-NLS
|
||||
logger.log(Level.WARNING, String.format("IOException when parsing image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
} finally {
|
||||
try {
|
||||
|
@ -824,6 +824,18 @@ public final class FilesSet implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Construct a case-insensitive file name extension condition.
|
||||
*
|
||||
* @param extension The file name extension to be matched.
|
||||
*/
|
||||
public ExtensionCondition(String extension) {
|
||||
// If there is a leading ".", strip it since
|
||||
// AbstractFile.getFileNameExtension() returns just the
|
||||
// extension chars and not the dot.
|
||||
super(extension.startsWith(".") ? extension.substring(1) : extension, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a case-insensitive file name extension condition.
|
||||
*
|
||||
|
@ -4,7 +4,7 @@ OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleName=Search
|
||||
OtherCasesSearchDialog.searchButton.text=Search
|
||||
OtherCasesSearchDialog.correlationValueTextField.text=
|
||||
OtherCasesSearchDialog.correlationValueLabel.text=Correlation Property Value:
|
||||
OtherCasesSearchDialog.descriptionLabel.text=Search data in the Central Repository from other cases.
|
||||
OtherCasesSearchDialog.descriptionLabel.text=<html>Search the Central Repository for correlation properties with a specified value. The search is case insensitive.</html>
|
||||
OtherCasesSearchDialog.errorLabel.text=\
|
||||
OtherCasesSearchDialog.correlationTypeLabel.text=Correlation Property Type:
|
||||
OtherCasesSearchDialog.casesLabel.text=\
|
||||
|
@ -24,18 +24,10 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="casesLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="descriptionLabel" pref="430" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="correlationValueLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
@ -43,11 +35,16 @@
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="correlationTypeComboBox" pref="289" max="32767" attributes="0"/>
|
||||
<Component id="correlationTypeComboBox" max="32767" attributes="0"/>
|
||||
<Component id="correlationValueTextField" max="32767" attributes="0"/>
|
||||
<Component id="errorLabel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="casesLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -57,25 +54,25 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="descriptionLabel" min="-2" pref="39" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="correlationTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="correlationTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="15" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="correlationValueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="correlationValueTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="correlationValueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="20" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="casesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -149,7 +146,7 @@
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="casesLabel">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="horizontalAlignment" type="int" value="2"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.casesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
|
@ -26,19 +26,17 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizer;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||
@ -55,6 +53,10 @@ import org.sleuthkit.autopsy.datamodel.EmptyNode;
|
||||
"OtherCasesSearchDialog.resultsDescription.text=Other Cases Search",
|
||||
"OtherCasesSearchDialog.emptyNode.text=No results found.",
|
||||
"OtherCasesSearchDialog.validation.invalidHash=The supplied value is not a valid MD5 hash.",
|
||||
"OtherCasesSearchDialog.validation.invalidEmail=The supplied value is not a valid e-mail address.",
|
||||
"OtherCasesSearchDialog.validation.invalidDomain=The supplied value is not a valid domain.",
|
||||
"OtherCasesSearchDialog.validation.invalidPhone=The supplied value is not a valid phone number.",
|
||||
"OtherCasesSearchDialog.validation.genericMessage=The supplied value is not valid.",
|
||||
"# {0} - number of cases",
|
||||
"OtherCasesSearchDialog.caseLabel.text=The current Central Repository contains {0} case(s)."
|
||||
})
|
||||
@ -67,9 +69,8 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
private static final Logger logger = Logger.getLogger(OtherCasesSearchDialog.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String FILES_CORRELATION_TYPE = "Files";
|
||||
|
||||
private final List<CorrelationAttributeInstance.Type> correlationTypes;
|
||||
private CorrelationAttributeInstance.Type selectedCorrelationType;
|
||||
private TextPrompt correlationValueTextFieldPrompt;
|
||||
|
||||
/**
|
||||
@ -84,23 +85,19 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
* Perform the other cases search.
|
||||
*
|
||||
* @param type The correlation type.
|
||||
* @param value The value to be matched.
|
||||
*/
|
||||
private void search() {
|
||||
private void search(CorrelationAttributeInstance.Type type, String value) {
|
||||
new SwingWorker<List<CorrelationAttributeInstance>, Void>() {
|
||||
|
||||
@Override
|
||||
protected List<CorrelationAttributeInstance> doInBackground() {
|
||||
List<CorrelationAttributeInstance.Type> correlationTypes;
|
||||
List<CorrelationAttributeInstance> correlationInstances = new ArrayList<>();
|
||||
|
||||
try {
|
||||
correlationTypes = EamDb.getInstance().getDefinedCorrelationTypes();
|
||||
for (CorrelationAttributeInstance.Type type : correlationTypes) {
|
||||
if (type.getDisplayName().equals((String) correlationTypeComboBox.getSelectedItem())) {
|
||||
correlationInstances = EamDb.getInstance().getArtifactInstancesByTypeValue(type, correlationValueTextField.getText());
|
||||
break;
|
||||
}
|
||||
}
|
||||
correlationInstances = EamDb.getInstance().getArtifactInstancesByTypeValue(type, value);
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to connect to the Central Repository database.", ex);
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
@ -123,9 +120,7 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
TableFilterNode tableFilterNode = new TableFilterNode(searchNode, true, searchNode.getName());
|
||||
|
||||
String resultsText = String.format("%s (%s; \"%s\")",
|
||||
Bundle.OtherCasesSearchDialog_resultsTitle_text(),
|
||||
(String) correlationTypeComboBox.getSelectedItem(),
|
||||
correlationValueTextField.getText());
|
||||
Bundle.OtherCasesSearchDialog_resultsTitle_text(), type.getDisplayName(), value);
|
||||
final TopComponent searchResultWin;
|
||||
if (correlationInstances.isEmpty()) {
|
||||
Node emptyNode = new TableFilterNode(
|
||||
@ -183,7 +178,7 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.descriptionLabel.text")); // NOI18N
|
||||
|
||||
casesLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
casesLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(casesLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.casesLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
@ -193,44 +188,42 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(casesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(searchButton))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(descriptionLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addComponent(descriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(correlationValueLabel)
|
||||
.addComponent(correlationTypeLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(correlationTypeComboBox, 0, 289, Short.MAX_VALUE)
|
||||
.addComponent(correlationTypeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(correlationValueTextField)
|
||||
.addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
.addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(casesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(searchButton)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(descriptionLabel)
|
||||
.addComponent(descriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(18, 18, 18)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(correlationTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(correlationTypeLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGap(15, 15, 15)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(correlationValueLabel)
|
||||
.addComponent(correlationValueTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(correlationValueTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(correlationValueLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(errorLabel)
|
||||
.addGap(11, 11, 11)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(searchButton)
|
||||
.addComponent(casesLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
searchButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleName")); // NOI18N
|
||||
@ -240,23 +233,57 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
|
||||
if (validateInputs()) {
|
||||
/*
|
||||
* Just in case, we'll lock down the type and value components to
|
||||
* avoid the possibly of a race condition.
|
||||
*/
|
||||
correlationTypeComboBox.setEnabled(false);
|
||||
correlationValueTextField.setEnabled(false);
|
||||
|
||||
search();
|
||||
CorrelationAttributeInstance.Type correlationType = selectedCorrelationType;
|
||||
String correlationValue = correlationValueTextField.getText().trim();
|
||||
|
||||
if (validateInputs(correlationType, correlationValue)) {
|
||||
search(correlationType, correlationValue);
|
||||
dispose();
|
||||
} else {
|
||||
String validationMessage;
|
||||
switch (correlationType.getId()) {
|
||||
case CorrelationAttributeInstance.FILES_TYPE_ID:
|
||||
validationMessage = Bundle.OtherCasesSearchDialog_validation_invalidHash();
|
||||
break;
|
||||
case CorrelationAttributeInstance.DOMAIN_TYPE_ID:
|
||||
validationMessage = Bundle.OtherCasesSearchDialog_validation_invalidDomain();
|
||||
break;
|
||||
case CorrelationAttributeInstance.EMAIL_TYPE_ID:
|
||||
validationMessage = Bundle.OtherCasesSearchDialog_validation_invalidEmail();
|
||||
break;
|
||||
case CorrelationAttributeInstance.PHONE_TYPE_ID:
|
||||
validationMessage = Bundle.OtherCasesSearchDialog_validation_invalidPhone();
|
||||
break;
|
||||
default:
|
||||
validationMessage = Bundle.OtherCasesSearchDialog_validation_genericMessage();
|
||||
break;
|
||||
|
||||
}
|
||||
errorLabel.setText(validationMessage);
|
||||
searchButton.setEnabled(false);
|
||||
errorLabel.setText(Bundle.OtherCasesSearchDialog_validation_invalidHash());
|
||||
correlationValueTextField.grabFocus();
|
||||
}
|
||||
}//GEN-LAST:event_searchButtonActionPerformed
|
||||
|
||||
|
||||
/**
|
||||
* Validate the supplied input.
|
||||
*
|
||||
* @param type The correlation type.
|
||||
* @param value The value to be validated.
|
||||
*
|
||||
* @return True if the input is valid for the given type; otherwise false.
|
||||
*/
|
||||
private boolean validateInputs(CorrelationAttributeInstance.Type type, String value) {
|
||||
try {
|
||||
CorrelationAttributeNormalizer.normalize(type, value);
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
// No need to log this.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Further customize the components beyond the standard initialization.
|
||||
*/
|
||||
@ -277,20 +304,21 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
}
|
||||
|
||||
for (CorrelationAttributeInstance.Type type : correlationTypes) {
|
||||
// We only support the "Files" type for now.
|
||||
if (type.getDisplayName().equals(FILES_CORRELATION_TYPE)) {
|
||||
correlationTypeComboBox.addItem(type.getDisplayName());
|
||||
}
|
||||
correlationTypeComboBox.addItem(type.getDisplayName());
|
||||
}
|
||||
correlationTypeComboBox.setSelectedIndex(0);
|
||||
|
||||
correlationTypeComboBox.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
updateSelectedType();
|
||||
updateCorrelationValueTextFieldPrompt();
|
||||
updateSearchButton();
|
||||
}
|
||||
});
|
||||
|
||||
updateSelectedType();
|
||||
|
||||
/*
|
||||
* Create listener for text input.
|
||||
*/
|
||||
@ -315,7 +343,12 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OtherCasesSearchDialog.correlationValueTextField.filesExample=Example: \"f0e1d2c3b4a5968778695a4b3c2d1e0f\""
|
||||
"OtherCasesSearchDialog.correlationValueTextField.filesExample=Example: \"f0e1d2c3b4a5968778695a4b3c2d1e0f\"",
|
||||
"OtherCasesSearchDialog.correlationValueTextField.domainExample=Example: \"domain.com\"",
|
||||
"OtherCasesSearchDialog.correlationValueTextField.emailExample=Example: \"user@host.com\"",
|
||||
"OtherCasesSearchDialog.correlationValueTextField.phoneExample=Example: \"(800)123-4567\"",
|
||||
"OtherCasesSearchDialog.correlationValueTextField.usbExample=Example: \"4&1234567&0\"",
|
||||
"OtherCasesSearchDialog.correlationValueTextField.ssidExample=Example: \"WirelessNetwork-5G\""
|
||||
})
|
||||
/**
|
||||
* Update the text prompt of the name text field based on the input type
|
||||
@ -325,7 +358,30 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
/**
|
||||
* Add text prompt to the text field.
|
||||
*/
|
||||
String text = Bundle.OtherCasesSearchDialog_correlationValueTextField_filesExample();
|
||||
String text;
|
||||
switch(selectedCorrelationType.getId()) {
|
||||
case CorrelationAttributeInstance.FILES_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_filesExample();
|
||||
break;
|
||||
case CorrelationAttributeInstance.DOMAIN_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_domainExample();
|
||||
break;
|
||||
case CorrelationAttributeInstance.EMAIL_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_emailExample();
|
||||
break;
|
||||
case CorrelationAttributeInstance.PHONE_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_phoneExample();
|
||||
break;
|
||||
case CorrelationAttributeInstance.USBID_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_usbExample();
|
||||
break;
|
||||
case CorrelationAttributeInstance.SSID_TYPE_ID:
|
||||
text = Bundle.OtherCasesSearchDialog_correlationValueTextField_ssidExample();
|
||||
break;
|
||||
default:
|
||||
text = "";
|
||||
break;
|
||||
}
|
||||
correlationValueTextFieldPrompt = new TextPrompt(text, correlationValueTextField);
|
||||
|
||||
/**
|
||||
@ -338,6 +394,19 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the 'selectedCorrelationType' value to match the selected type
|
||||
* from the combo-box.
|
||||
*/
|
||||
private void updateSelectedType() {
|
||||
for (CorrelationAttributeInstance.Type type : correlationTypes) {
|
||||
if (type.getDisplayName().equals((String) correlationTypeComboBox.getSelectedItem())) {
|
||||
selectedCorrelationType = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the Search button depending on whether or not text has
|
||||
* been provided for the correlation property value.
|
||||
@ -345,20 +414,6 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
private void updateSearchButton() {
|
||||
searchButton.setEnabled(correlationValueTextField.getText().isEmpty() == false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the value input.
|
||||
*
|
||||
* @return True if the input is valid for the selected type; otherwise false.
|
||||
*/
|
||||
private boolean validateInputs() {
|
||||
Pattern md5Pattern = Pattern.compile("^[a-fA-F0-9]{32}$"); // NON-NLS
|
||||
Matcher matcher = md5Pattern.matcher(correlationValueTextField.getText().trim());
|
||||
if (matcher.find()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the Search Other Cases dialog.
|
||||
@ -378,4 +433,4 @@ final class OtherCasesSearchDialog extends javax.swing.JDialog {
|
||||
private javax.swing.JLabel errorLabel;
|
||||
private javax.swing.JButton searchButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
}
|
@ -253,4 +253,8 @@ ReportGenerator.errList.coreExceptionWhileGenRptRow=Core exception while generat
|
||||
ReportKML.latLongStartPoint={0};{1};;{2} (Start)\n
|
||||
ReportKML.latLongEndPoint={0};{1};;{2} (End)\n
|
||||
ReportGenerationPanel.cancelButton.actionCommand=Cancel
|
||||
ReportGenerationPanel.cancelButton.text=Cancel
|
||||
ReportGenerationPanel.cancelButton.text=Cancel
|
||||
ReportHTMLConfigurationPanel.headerTextField.text=
|
||||
ReportHTMLConfigurationPanel.footerTextField.text=
|
||||
ReportHTMLConfigurationPanel.headerLabel.text=Header:
|
||||
ReportHTMLConfigurationPanel.footerLabel.text=Footer:
|
||||
|
@ -243,4 +243,4 @@ ReportVisualPanel1.invalidModuleWarning=\u7121\u52b9\u306a\u30ec\u30dd\u30fc\u30
|
||||
ReportGenerationPanel.confDlg.cancelReport.msg=\u672c\u5f53\u306b\u30ec\u30dd\u30fc\u30c8\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u307e\u3059\u304b\uff1f
|
||||
ReportProgressPanel.complete.processLb2.text=\u5b8c\u4e86\u3057\u307e\u3057\u305f\u304c\u3001\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||
ReportGenerationPanel.cancelButton.actionCommand=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||
ReportGenerationPanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||
ReportGenerationPanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012 Basis Technology Corp.
|
||||
* Copyright 2012-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public interface GeneralReportModule extends ReportModule {
|
||||
|
||||
/**
|
||||
@ -33,13 +31,4 @@ public interface GeneralReportModule extends ReportModule {
|
||||
*/
|
||||
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel);
|
||||
|
||||
/**
|
||||
* Returns the configuration panel for the report, which is displayed in the
|
||||
* report configuration step of the report wizard.
|
||||
*
|
||||
* @return Configuration panel or null if the module does not need
|
||||
* configuration.
|
||||
*/
|
||||
public JPanel getConfigurationPanel();
|
||||
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JPanel;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.openide.filesystems.FileUtil;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -56,6 +58,7 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
@ -89,6 +92,8 @@ class ReportHTML implements TableReportModule {
|
||||
private String currentDataType; // name of current data type
|
||||
private Integer rowCount; // number of rows (aka artifacts or tags) for the current data type
|
||||
private Writer out;
|
||||
|
||||
private ReportHTMLConfigurationPanel configPanel;
|
||||
|
||||
private final ReportBranding reportBranding;
|
||||
|
||||
@ -104,6 +109,14 @@ class ReportHTML implements TableReportModule {
|
||||
private ReportHTML() {
|
||||
reportBranding = new ReportBranding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getConfigurationPanel() {
|
||||
if (configPanel == null) {
|
||||
configPanel = new ReportHTMLConfigurationPanel();
|
||||
}
|
||||
return configPanel;
|
||||
}
|
||||
|
||||
// Refesh the member variables
|
||||
private void refresh() throws NoCurrentCaseException {
|
||||
@ -332,6 +345,10 @@ class ReportHTML implements TableReportModule {
|
||||
*/
|
||||
@Override
|
||||
public void startReport(String baseReportDir) {
|
||||
// Save settings
|
||||
ModuleSettings.setConfigSetting("HTMLReport", "header", configPanel.getHeader()); //NON-NLS
|
||||
ModuleSettings.setConfigSetting("HTMLReport", "footer", configPanel.getFooter()); //NON-NLS
|
||||
|
||||
// Refresh the HTML report
|
||||
try {
|
||||
refresh();
|
||||
@ -392,12 +409,14 @@ class ReportHTML implements TableReportModule {
|
||||
|
||||
try {
|
||||
StringBuilder page = new StringBuilder();
|
||||
page.append("<html>\n<head>\n\t<title>").append(name).append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n"); //NON-NLS
|
||||
page.append("<div id=\"header\">").append(name).append("</div>\n<div id=\"content\">\n"); //NON-NLS
|
||||
page.append("<html>\n<head>\n\t<title>").append(name).append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n") //NON-NLS
|
||||
.append(writePageHeader())
|
||||
.append("<div id=\"header\">").append(name).append("</div>\n")
|
||||
.append("<div id=\"content\">\n"); //NON-NLS
|
||||
if (!description.isEmpty()) {
|
||||
page.append("<p><strong>"); //NON-NLS
|
||||
page.append(description);
|
||||
page.append("</string></p>\n"); //NON-NLS
|
||||
page.append("</strong></p>\n"); //NON-NLS
|
||||
}
|
||||
out.write(page.toString());
|
||||
currentDataType = name;
|
||||
@ -415,7 +434,10 @@ class ReportHTML implements TableReportModule {
|
||||
public void endDataType() {
|
||||
dataTypes.put(currentDataType, rowCount);
|
||||
try {
|
||||
out.write("</div>\n</body>\n</html>\n"); //NON-NLS
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(writePageFooter());
|
||||
builder.append("</div>\n</body>\n</html>\n"); //NON-NLS
|
||||
out.write(builder.toString());
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to write end of HTML report.", ex); //NON-NLS
|
||||
} finally {
|
||||
@ -430,6 +452,40 @@ class ReportHTML implements TableReportModule {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write HTML-formatted page header text based on the text provided in the
|
||||
* configuration panel.
|
||||
*
|
||||
* @return The HTML-formatted text.
|
||||
*/
|
||||
private String writePageHeader() {
|
||||
StringBuilder output = new StringBuilder();
|
||||
String pageHeader = configPanel.getHeader();
|
||||
if (pageHeader.isEmpty() == false) {
|
||||
output.append("<div id=\"pageHeaderFooter\">")
|
||||
.append(StringEscapeUtils.escapeHtml4(pageHeader))
|
||||
.append("</div>\n"); //NON-NLS
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write HTML-formatted page footer text based on the text provided in the
|
||||
* configuration panel.
|
||||
*
|
||||
* @return The HTML-formatted text.
|
||||
*/
|
||||
private String writePageFooter() {
|
||||
StringBuilder output = new StringBuilder();
|
||||
String pageFooter = configPanel.getFooter();
|
||||
if (pageFooter.isEmpty() == false) {
|
||||
output.append("<br/><div id=\"pageHeaderFooter\">")
|
||||
.append(StringEscapeUtils.escapeHtml4(pageFooter))
|
||||
.append("</div>"); //NON-NLS
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new set under the current data type.
|
||||
@ -860,6 +916,8 @@ class ReportHTML implements TableReportModule {
|
||||
+ //NON-NLS
|
||||
"#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n"
|
||||
+ //NON-NLS
|
||||
"#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n"
|
||||
+ //NON-NLS
|
||||
"h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n"
|
||||
+ //NON-NLS
|
||||
"h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n"
|
||||
@ -1071,6 +1129,7 @@ class ReportHTML implements TableReportModule {
|
||||
NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
|
||||
head.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"); //NON-NLS
|
||||
head.append("<style type=\"text/css\">\n"); //NON-NLS
|
||||
head.append("#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n"); //NON-NLS
|
||||
head.append("body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n"); //NON-NLS
|
||||
head.append("#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n"); //NON-NLS
|
||||
head.append("h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n"); //NON-NLS
|
||||
@ -1109,6 +1168,7 @@ class ReportHTML implements TableReportModule {
|
||||
final boolean generatorLogoSet = reportBranding.getGeneratorLogoPath() != null && !reportBranding.getGeneratorLogoPath().isEmpty();
|
||||
|
||||
summary.append("<div id=\"wrapper\">\n"); //NON-NLS
|
||||
summary.append(writePageHeader());
|
||||
summary.append("<h1>").append(reportTitle) //NON-NLS
|
||||
.append(running ? NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.warningMsg") : "")
|
||||
.append("</h1>\n"); //NON-NLS
|
||||
@ -1129,6 +1189,7 @@ class ReportHTML implements TableReportModule {
|
||||
summary.append("<p class=\"subheadding\">").append(reportFooter).append("</p>\n"); //NON-NLS
|
||||
}
|
||||
summary.append("</div>\n"); //NON-NLS
|
||||
summary.append(writePageFooter());
|
||||
summary.append("</body></html>"); //NON-NLS
|
||||
output.write(summary.toString());
|
||||
} catch (FileNotFoundException ex) {
|
||||
|
89
Core/src/org/sleuthkit/autopsy/report/ReportHTMLConfigurationPanel.form
Executable file
89
Core/src/org/sleuthkit/autopsy/report/ReportHTMLConfigurationPanel.form
Executable file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="Form" property="font" relativeSize="false" size="11"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="headerLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="footerLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="headerTextField" max="32767" attributes="0"/>
|
||||
<Component id="footerTextField" pref="371" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="headerLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="headerTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="footerTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="footerLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="188" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="headerLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportHTMLConfigurationPanel.headerLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="footerLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportHTMLConfigurationPanel.footerLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="headerTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportHTMLConfigurationPanel.headerTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="footerTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportHTMLConfigurationPanel.footerTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
120
Core/src/org/sleuthkit/autopsy/report/ReportHTMLConfigurationPanel.java
Executable file
120
Core/src/org/sleuthkit/autopsy/report/ReportHTMLConfigurationPanel.java
Executable file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 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.report;
|
||||
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
|
||||
/**
|
||||
* The panel shown for all TableReportModules when configuring report modules.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class ReportHTMLConfigurationPanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Creates new form DefaultReportConfigurationPanel
|
||||
*/
|
||||
ReportHTMLConfigurationPanel() {
|
||||
initComponents();
|
||||
|
||||
// Load settings
|
||||
String header = ModuleSettings.getConfigSetting("HTMLReport", "header"); //NON-NLS
|
||||
String footer = ModuleSettings.getConfigSetting("HTMLReport", "footer"); //NON-NLS
|
||||
|
||||
headerTextField.setText(header != null ? header : "");
|
||||
footerTextField.setText(footer != null ? footer : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header text.
|
||||
*
|
||||
* @return The header text.
|
||||
*/
|
||||
String getHeader() {
|
||||
return headerTextField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the footer text.
|
||||
*
|
||||
* @return The footer text.
|
||||
*/
|
||||
String getFooter() {
|
||||
return footerTextField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
headerLabel = new javax.swing.JLabel();
|
||||
footerLabel = new javax.swing.JLabel();
|
||||
headerTextField = new javax.swing.JTextField();
|
||||
footerTextField = new javax.swing.JTextField();
|
||||
|
||||
setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(headerLabel, org.openide.util.NbBundle.getMessage(ReportHTMLConfigurationPanel.class, "ReportHTMLConfigurationPanel.headerLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(footerLabel, org.openide.util.NbBundle.getMessage(ReportHTMLConfigurationPanel.class, "ReportHTMLConfigurationPanel.footerLabel.text")); // NOI18N
|
||||
|
||||
headerTextField.setText(org.openide.util.NbBundle.getMessage(ReportHTMLConfigurationPanel.class, "ReportHTMLConfigurationPanel.headerTextField.text")); // NOI18N
|
||||
|
||||
footerTextField.setText(org.openide.util.NbBundle.getMessage(ReportHTMLConfigurationPanel.class, "ReportHTMLConfigurationPanel.footerTextField.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(headerLabel)
|
||||
.addComponent(footerLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(headerTextField)
|
||||
.addComponent(footerTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 371, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(headerLabel)
|
||||
.addComponent(headerTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(footerTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(footerLabel))
|
||||
.addContainerGap(188, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel footerLabel;
|
||||
private javax.swing.JTextField footerTextField;
|
||||
private javax.swing.JLabel headerLabel;
|
||||
private javax.swing.JTextField headerTextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012 Basis Technology Corp.
|
||||
* Copyright 2012-2018 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* Contact: aebadirad <at> 42six <dot> com
|
||||
@ -22,6 +22,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* Interface for report modules that plug in to the reporting infrastructure.
|
||||
*/
|
||||
@ -47,4 +49,15 @@ interface ReportModule {
|
||||
* module does not produce a report file.
|
||||
*/
|
||||
public String getRelativeFilePath();
|
||||
|
||||
/**
|
||||
* Returns the configuration panel for the report, which is displayed in the
|
||||
* report configuration step of the report wizard.
|
||||
*
|
||||
* @return Configuration panel or null if the module does not need
|
||||
* configuration.
|
||||
*/
|
||||
public default JPanel getConfigurationPanel() {
|
||||
return new DefaultReportConfigurationPanel();
|
||||
}
|
||||
}
|
||||
|
@ -294,18 +294,18 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
// single selection, so max selection index is the only one selected.
|
||||
selectedIndex = m.getMaxSelectionIndex();
|
||||
|
||||
JPanel panel = new DefaultReportConfigurationPanel();
|
||||
ReportModule module = modules.get(selectedIndex);
|
||||
boolean generalModuleSelected = false;
|
||||
if (module instanceof GeneralReportModule) {
|
||||
JPanel generalPanel = ((GeneralReportModule) module).getConfigurationPanel();
|
||||
panel = (generalPanel == null) ? new JPanel() : generalPanel;
|
||||
generalModuleSelected = true;
|
||||
JPanel panel = module.getConfigurationPanel();
|
||||
if (panel == null) {
|
||||
panel = new JPanel();
|
||||
}
|
||||
|
||||
descriptionTextPane.setText(module.getDescription());
|
||||
configurationPanel.add(panel, BorderLayout.CENTER);
|
||||
configurationPanel.revalidate();
|
||||
configurationPanel.repaint();
|
||||
|
||||
boolean generalModuleSelected = (module instanceof GeneralReportModule);
|
||||
|
||||
wizPanel.setNext(!generalModuleSelected);
|
||||
wizPanel.setFinish(generalModuleSelected);
|
||||
|
@ -756,9 +756,12 @@ class TableReportGenerator {
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getDisplayName(), currentList));
|
||||
}
|
||||
if (!keyword.equals(currentKeyword)) {
|
||||
// End the previous table if one exists.
|
||||
if (!currentKeyword.equals("")) {
|
||||
tableModule.endTable();
|
||||
}
|
||||
|
||||
// Prepare for a new table.
|
||||
currentKeyword = keyword;
|
||||
tableModule.addSetElement(currentKeyword);
|
||||
List<String> columnHeaderNames = new ArrayList<>();
|
||||
@ -770,6 +773,11 @@ class TableReportGenerator {
|
||||
|
||||
tableModule.addRow(Arrays.asList(new String[]{preview, uniquePath, tagsList}));
|
||||
}
|
||||
|
||||
// End the previous table if one exists.
|
||||
if (!currentKeyword.isEmpty()) {
|
||||
tableModule.endTable();
|
||||
}
|
||||
|
||||
// Finish the current data type
|
||||
progressPanel.increment();
|
||||
|
@ -178,7 +178,7 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
//
|
||||
//
|
||||
// // Restore the original properties
|
||||
// ModuleSettings.setConfigSettings(PROPERTIES_FILE, propertiesMap);
|
||||
//
|
||||
@ -1172,8 +1172,10 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
// DbManager.processInstanceTable(fileType, instancetableCallback);
|
||||
// int count1 = instancetableCallback.getCounter();
|
||||
// int count2 = instancetableCallback.getCounterNamingConvention();
|
||||
// assertEquals("Unexpected value for Process Instance count with filepath naming convention", 2, count2);
|
||||
// assertTrue("Unexpected value for Process Instance count with filepath without naming convention: " + count1 + " - expected greater than 0", count1 > 0);
|
||||
// //expects 2 rows to match the naming convention to of been processed, expects at least one row not matching the naming convention to be processed
|
||||
// //if the test code is changed to add additional Correlation Attributes which also have "processinstancecallback" in their path the first of these comparisons will need to change
|
||||
// assertEquals("Counter for items matching naming convention from AttributeInstaceTableCallback indicates an unexepected number of results when processed with DbManager.processInstanceTable", 2, count2);
|
||||
// assertTrue("Counter for items which do not match naming convention from AttributeInstaceTableCallback indicates an unexepected number of results when processed with DbManager.processInstanceTable. Count indicated: " + count1 + " - expected a number greater than 0", count1 > 0);
|
||||
// } catch (EamDbException | CorrelationAttributeNormalizationException ex) {
|
||||
// Exceptions.printStackTrace(ex);
|
||||
// Assert.fail("Error Exception thrown when calling processInstanceTable " + ex.getMessage());
|
||||
@ -1193,14 +1195,18 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
// CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath1);
|
||||
// CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath2);
|
||||
// EamDb DbManager = EamDb.getInstance();
|
||||
// //these redundant addArtifactInstance calls allow code to be rearranged if necessary
|
||||
// DbManager.addArtifactInstance(attr1);
|
||||
// DbManager.addArtifactInstance(attr2);
|
||||
// AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback();
|
||||
// DbManager.processInstanceTableWhere(fileType, "value='" + callbackTestFileHash + "'", instancetableCallback);
|
||||
// int count1 = instancetableCallback.getCounter();
|
||||
// //naming convention counts
|
||||
// int count2 = instancetableCallback.getCounterNamingConvention();
|
||||
// assertEquals("Unexpected value for Process Instance Where count with filepath naming convention", 2, count2);
|
||||
// assertTrue("Unexpected value for Process Instance Where count with filepath without naming convention: " + count1 + " - expected greater than 0", count1 > 0);
|
||||
// //this has only processed the rows where the value is equal to the specified value, which should only be the two rows with the naming convention checked for
|
||||
// //if the test code is changed to add additional Correlation Attributes with the same callbackTestFileHash value that is used here these comparisons will need to change
|
||||
// assertEquals("Counter for items matching naming convention from AttributeInstaceTableCallback indicates an unexepected number of results when processed with DbManager.processInstanceTableWhere", 2, count2);
|
||||
// assertEquals("Counter for items which do not match naming convention from AttributeInstaceTableCallback indicates an unexepected number of results when processed with DbManager.processInstanceTableWhere", 0, count1);
|
||||
// } catch (EamDbException | CorrelationAttributeNormalizationException ex) {
|
||||
// Exceptions.printStackTrace(ex);
|
||||
// Assert.fail("Error Exception thrown when calling processInstanceTableWhere " + ex.getMessage());
|
||||
|
@ -56,7 +56,7 @@ import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstance;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader;
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceLoader;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
|
@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader;
|
||||
import org.sleuthkit.autopsy.guiutils.DataSourceLoader;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
||||
|
@ -3106,27 +3106,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
||||
updateCoordinationServiceManifestNode(currentJob);
|
||||
eventPublisher.publishRemotely(new AutoIngestJobStatusEvent(currentJob));
|
||||
}
|
||||
|
||||
if (AutoIngestUserPreferences.getStatusDatabaseLoggingEnabled()) {
|
||||
String message;
|
||||
boolean isError = false;
|
||||
if (getErrorState().equals(ErrorState.NONE)) {
|
||||
if (currentJob != null) {
|
||||
message = "Processing " + currentJob.getManifest().getDataSourceFileName()
|
||||
+ " for case " + currentJob.getManifest().getCaseName();
|
||||
} else {
|
||||
message = "Paused or waiting for next case";
|
||||
}
|
||||
} else {
|
||||
message = getErrorState().toString();
|
||||
isError = true;
|
||||
}
|
||||
try {
|
||||
StatusDatabaseLogger.logToStatusDatabase(message, isError);
|
||||
} catch (SQLException | UserPreferencesException ex) {
|
||||
sysLogger.log(Level.WARNING, "Failed to update status database", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check whether any remote nodes have timed out
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-2018 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.experimental.autoingest;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
|
||||
|
||||
/**
|
||||
* Write auto-ingest status updates to a database.
|
||||
*/
|
||||
public class StatusDatabaseLogger {
|
||||
|
||||
/**
|
||||
* Log the current status to the database using the database parameters
|
||||
* saved in AutoIngestUserPreferences.
|
||||
*
|
||||
* @param message Current status message
|
||||
* @param isError true if we're in an error state, false otherwise
|
||||
*
|
||||
* @throws SQLException If a SQL data access error occurs.
|
||||
* @throws UserPreferencesException If there's an issue reading the user
|
||||
* preferences.
|
||||
*/
|
||||
public static void logToStatusDatabase(String message, boolean isError) throws SQLException, UserPreferencesException {
|
||||
|
||||
try {
|
||||
Class.forName("org.postgresql.Driver");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger sysLogger = AutoIngestSystemLogger.getLogger();
|
||||
sysLogger.log(Level.WARNING, "Error loading postgresql driver", ex);
|
||||
}
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://"
|
||||
+ AutoIngestUserPreferences.getLoggingDatabaseHostnameOrIP()
|
||||
+ ":" + AutoIngestUserPreferences.getLoggingPort()
|
||||
+ "/" + AutoIngestUserPreferences.getLoggingDatabaseName(),
|
||||
AutoIngestUserPreferences.getLoggingUsername(),
|
||||
AutoIngestUserPreferences.getLoggingPassword());
|
||||
Statement statement = connection.createStatement();) {
|
||||
|
||||
logToStatusDatabase(statement, message, isError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the current status to the database using an already configured
|
||||
* Statement.
|
||||
*
|
||||
* @param statement SQL statement (must have already been created)
|
||||
* @param message Current status message
|
||||
* @param isError true if we're in an error state, false otherwise
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static void logToStatusDatabase(Statement statement, String message, boolean isError) throws SQLException {
|
||||
if ((statement == null) || statement.isClosed()) {
|
||||
throw new SQLException("SQL Statement is null/closed");
|
||||
}
|
||||
|
||||
int status;
|
||||
if (isError) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format( new java.util.Date());
|
||||
|
||||
String checkForPreviousEntry = "SELECT * FROM statusUpdates WHERE tool='" + UserPreferences.getAppName() + "' AND "
|
||||
+ "node='" + NetworkUtils.getLocalHostName() + "'";
|
||||
|
||||
ResultSet resultSet = statement.executeQuery(checkForPreviousEntry);
|
||||
String logMessage;
|
||||
if (resultSet.next()) {
|
||||
logMessage = "UPDATE statusUpdates SET reportTime='" + timestamp
|
||||
+ "', message='" + message + "', status=" + status
|
||||
+ " WHERE tool='" + UserPreferences.getAppName() + "' AND node='" + NetworkUtils.getLocalHostName() + "'";
|
||||
} else {
|
||||
logMessage = "INSERT INTO statusUpdates (tool, node, reportTime, message, status) "
|
||||
+ "VALUES ('" + UserPreferences.getAppName()
|
||||
+ "', '" + NetworkUtils.getLocalHostName()
|
||||
+ "', '"
|
||||
+ timestamp + "', '" + message + "', '" + status + "')";
|
||||
|
||||
}
|
||||
statement.execute(logMessage);
|
||||
}
|
||||
|
||||
}
|
@ -85,8 +85,6 @@
|
||||
<Component id="bnAdvancedSettings" min="-2" pref="155" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="bnFileExport" min="-2" pref="155" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="bnLogging" min="-2" pref="155" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jLabelSelectOutputFolder" min="-2" max="-2" attributes="0"/>
|
||||
@ -162,7 +160,6 @@
|
||||
<Component id="bnEditIngestSettings" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnFileExport" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnAdvancedSettings" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnLogging" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
@ -248,16 +245,6 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnFileExportActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnLogging">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnLogging.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnLoggingActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="browseOutputFolderButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
|
@ -565,7 +565,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
bnEditIngestSettings.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
|
||||
bnAdvancedSettings.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
|
||||
bnLogging.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
|
||||
sharedConfigCheckbox.setEnabled(mode == OptionsUiMode.AIM);
|
||||
masterNodeCheckBox.setEnabled(mode == OptionsUiMode.AIM && sharedConfigCheckbox.isSelected());
|
||||
bnFileExport.setEnabled(mode == OptionsUiMode.AIM);
|
||||
@ -603,7 +602,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
bnEditIngestSettings = new javax.swing.JButton();
|
||||
bnAdvancedSettings = new javax.swing.JButton();
|
||||
bnFileExport = new javax.swing.JButton();
|
||||
bnLogging = new javax.swing.JButton();
|
||||
browseOutputFolderButton = new javax.swing.JButton();
|
||||
browseInputFolderButton = new javax.swing.JButton();
|
||||
inputPathTextField = new javax.swing.JTextField();
|
||||
@ -658,13 +656,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnLogging, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.bnLogging.text")); // NOI18N
|
||||
bnLogging.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnLoggingActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(browseOutputFolderButton, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.browseOutputFolderButton.text")); // NOI18N
|
||||
browseOutputFolderButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
@ -805,9 +796,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(bnAdvancedSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(bnFileExport, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(bnLogging, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(bnFileExport, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(nodePanelLayout.createSequentialGroup()
|
||||
.addComponent(jLabelSelectOutputFolder)
|
||||
.addGap(18, 18, 18)
|
||||
@ -863,8 +852,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(bnEditIngestSettings)
|
||||
.addComponent(bnFileExport)
|
||||
.addComponent(bnAdvancedSettings)
|
||||
.addComponent(bnLogging))
|
||||
.addComponent(bnAdvancedSettings))
|
||||
.addGap(18, 18, 18)
|
||||
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(sharedConfigCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
@ -1042,23 +1030,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}//GEN-LAST:event_browseInputFolderButtonActionPerformed
|
||||
|
||||
private void bnLoggingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnLoggingActionPerformed
|
||||
JDialog jDialog = new JDialog();
|
||||
NodeStatusLogPanel loggingPanel = new NodeStatusLogPanel(jDialog);
|
||||
|
||||
JScrollPane jScrollPane = new JScrollPane(loggingPanel);
|
||||
jScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
jScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
jScrollPane.setMinimumSize(new Dimension(100, 100));
|
||||
jDialog.add(jScrollPane);
|
||||
jDialog.setTitle(NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.NodeStatusLogging.text"));
|
||||
jDialog.setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame32.gif"));
|
||||
jDialog.setModalityType(java.awt.Dialog.ModalityType.APPLICATION_MODAL);
|
||||
jDialog.pack();
|
||||
jDialog.setLocationRelativeTo(this);
|
||||
jDialog.setVisible(true);
|
||||
}//GEN-LAST:event_bnLoggingActionPerformed
|
||||
|
||||
private void bnFileExportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnFileExportActionPerformed
|
||||
JDialog jDialog = new JDialog();
|
||||
FileExporterSettingsPanel fileExporterSettingsPanel = new FileExporterSettingsPanel(jDialog);
|
||||
@ -1210,7 +1181,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
bnAdvancedSettings.setEnabled(enabled);
|
||||
bnEditIngestSettings.setEnabled(enabled);
|
||||
bnFileExport.setEnabled(enabled);
|
||||
bnLogging.setEnabled(enabled);
|
||||
browseInputFolderButton.setEnabled(enabled);
|
||||
browseOutputFolderButton.setEnabled(enabled);
|
||||
browseSharedSettingsButton.setEnabled(sharedConfigCheckbox.isSelected() && autoIngestModeRadioButton.isSelected());
|
||||
@ -1228,7 +1198,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JButton bnAdvancedSettings;
|
||||
private javax.swing.JButton bnEditIngestSettings;
|
||||
private javax.swing.JButton bnFileExport;
|
||||
private javax.swing.JButton bnLogging;
|
||||
private javax.swing.JButton browseInputFolderButton;
|
||||
private javax.swing.JButton browseOutputFolderButton;
|
||||
private javax.swing.JButton browseSharedSettingsButton;
|
||||
|
@ -281,125 +281,6 @@ public final class AutoIngestUserPreferences {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE, Integer.toString(numberOfNodes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status database logging checkbox state for automated ingest mode from
|
||||
* persistent storage.
|
||||
*
|
||||
* @return Boolean true if database logging is enabled.
|
||||
*/
|
||||
public static Boolean getStatusDatabaseLoggingEnabled() {
|
||||
return Boolean.parseBoolean(getPreferenceValue(STATUS_DATABASE_LOGGING_ENABLED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save status database logging checkbox state for automated ingest mode to
|
||||
* persistent storage.
|
||||
*
|
||||
* @param databaseLoggingEnabled true = use database logging in auto-ingest
|
||||
* mode
|
||||
*/
|
||||
public static void setStatusDatabaseLoggingEnabled(boolean databaseLoggingEnabled) {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, STATUS_DATABASE_LOGGING_ENABLED, Boolean.toString(databaseLoggingEnabled));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging database hostname from persistent storage.
|
||||
*
|
||||
* @return Logging database hostname or IP
|
||||
*/
|
||||
public static String getLoggingDatabaseHostnameOrIP() {
|
||||
return getPreferenceValue(LOGGING_DB_HOSTNAME_OR_IP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the logging database hostname to persistent storage.
|
||||
*
|
||||
* @param hostname Logging database hostname or IP
|
||||
*/
|
||||
public static void setLoggingDatabaseHostnameOrIP(String hostname) {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, LOGGING_DB_HOSTNAME_OR_IP, hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging database port from persistent storage.
|
||||
*
|
||||
* @return logging database port
|
||||
*/
|
||||
public static String getLoggingPort() {
|
||||
return getPreferenceValue(LOGGING_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the logging database port to persistent storage.
|
||||
*
|
||||
* @param port Logging database port
|
||||
*/
|
||||
public static void setLoggingPort(String port) {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, LOGGING_PORT, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging database username from persistent storage.
|
||||
*
|
||||
* @return logging database username
|
||||
*/
|
||||
public static String getLoggingUsername() {
|
||||
return getPreferenceValue(LOGGING_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the logging database username to persistent storage.
|
||||
*
|
||||
* @param username Logging database username
|
||||
*/
|
||||
public static void setLoggingUsername(String username) {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, LOGGING_USERNAME, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging database password from persistent storage.
|
||||
*
|
||||
* @return logging database password
|
||||
*
|
||||
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
|
||||
*/
|
||||
public static String getLoggingPassword() throws UserPreferencesException {
|
||||
return getPreferenceValue(LOGGING_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the logging database password to persistent storage.
|
||||
*
|
||||
* @param password Logging database password
|
||||
*
|
||||
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
|
||||
*/
|
||||
public static void setLoggingPassword(String password) throws UserPreferencesException {
|
||||
try {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, LOGGING_PASSWORD, TextConverter.convertTextToHexText(password));
|
||||
} catch (TextConverterException ex) {
|
||||
throw new UserPreferencesException("Error encrypting password", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging database name from persistent storage.
|
||||
*
|
||||
* @return logging database name
|
||||
*/
|
||||
public static String getLoggingDatabaseName() {
|
||||
return getPreferenceValue(LOGGING_DATABASE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the logging database name to persistent storage.
|
||||
*
|
||||
* @param name Logging database name
|
||||
*/
|
||||
public static void setLoggingDatabaseName(String name) {
|
||||
ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, LOGGING_DATABASE_NAME, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured time for input scan interval
|
||||
*
|
||||
|
@ -127,7 +127,6 @@ AutoIngestSettingsPanel.browseInputFolderButton.text=Browse
|
||||
AutoIngestSettingsPanel.inputPathTextField.toolTipText=Input folder for automated processing, i.e., the location where input case folders will be created for ingest by automated processing mode
|
||||
AutoIngestSettingsPanel.inputPathTextField.text=
|
||||
AutoIngestSettingsPanel.jLabelSelectInputFolder.text=Select shared images folder:
|
||||
AutoIngestSettingsPanel.bnLogging.text=Node Status Logging
|
||||
AutoIngestSettingsPanel.bnFileExport.text=File Export Settings
|
||||
AutoIngestSettingsPanel.bnAdvancedSettings.text=Advanced Settings
|
||||
AutoIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the automated processing mode context.
|
||||
|
@ -1,277 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="232" max="-2" attributes="0"/>
|
||||
<Component id="bnOk" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="bnCancel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="cbEnableLogging" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="421" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="79" max="-2" attributes="0"/>
|
||||
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="bnOk" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="69" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="48" max="-2" attributes="0"/>
|
||||
<Component id="cbEnableLogging" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="324" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="pnDatabaseSettings">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
|
||||
<EtchetBorder/>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="lbDatabaseSettings" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="344" max="32767" attributes="0"/>
|
||||
<Component id="bnTestDatabase" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="lbTestDatabase" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="tbDbHostname" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbDbPort" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbDbUsername" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="tbDbPassword" max="32767" attributes="0"/>
|
||||
<Component id="tbDbName" max="32767" attributes="0"/>
|
||||
<Component id="lbTestDbWarning" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="bnTestDatabase" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbTestDatabase" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="lbDatabaseSettings" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbDbHostname" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbDbPort" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbDbUsername" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbDbPassword" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbDbName" min="-2" pref="20" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="lbTestDbWarning" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextField" name="tbDbHostname">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="tbDbHostname" property="font" relativeSize="false" size="12"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbHostname.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbHostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbDbPort">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="tbDbPort" property="font" relativeSize="false" size="12"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPort.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPort.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbDbUsername">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="tbDbUsername" property="font" relativeSize="false" size="12"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbUsername.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbUsername.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JPasswordField" name="tbDbPassword">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="tbDbPassword" property="font" relativeSize="false" size="12"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPassword.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPassword.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbDatabaseSettings">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="lbDatabaseSettings" property="font" relativeSize="false" size="12"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbDatabaseSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="verticalAlignment" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnTestDatabase">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="bnTestDatabase" property="font" relativeSize="false" size="11"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnTestDatabase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestDatabaseActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbTestDatabase">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbTestDatabase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="autoscrolls" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbTestDbWarning">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbTestDbWarning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbDbName">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbName.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JCheckBox" name="cbEnableLogging">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="false" component="cbEnableLogging" property="font" relativeSize="false" size="11"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.cbEnableLogging.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbEnableLoggingItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnOk">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnOk.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOkActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnCancel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnCancel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCancelActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -1,424 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-2018 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.experimental.configuration;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
|
||||
import java.awt.Cursor;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.openide.util.ImageUtilities;
|
||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.experimental.autoingest.StatusDatabaseLogger;
|
||||
|
||||
/**
|
||||
* Node status log panel.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class NodeStatusLogPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final String HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.toolTipText");
|
||||
private static final String PORT_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.toolTipText");
|
||||
private static final String USER_NAME_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.toolTipText");
|
||||
private static final String PASSWORD_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.toolTipText");
|
||||
private static final String DATABASE_NAME_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.toolTipText");
|
||||
|
||||
private final ImageIcon goodIcon;
|
||||
private final ImageIcon badIcon;
|
||||
|
||||
JDialog jDialog;
|
||||
private static final Logger logger = Logger.getLogger(NodeStatusLogPanel.class.getName());
|
||||
|
||||
/**
|
||||
* Creates new form DatabaseLogPanell
|
||||
* @param jDialog
|
||||
*/
|
||||
public NodeStatusLogPanel(JDialog jDialog) {
|
||||
initComponents();
|
||||
load();
|
||||
validateSettings();
|
||||
this.jDialog = jDialog;
|
||||
|
||||
tbDbHostname.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
tbDbPort.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
tbDbPassword.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
tbDbUsername.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
tbDbName.getDocument().addDocumentListener(new MyDocumentListener());
|
||||
|
||||
/**
|
||||
* Add text prompts to all of the text fields.
|
||||
*/
|
||||
Collection<TextPrompt> textPrompts = new ArrayList<>();
|
||||
textPrompts.add(new TextPrompt(HOST_NAME_OR_IP_PROMPT, tbDbHostname));
|
||||
textPrompts.add(new TextPrompt(PORT_PROMPT, tbDbPort));
|
||||
textPrompts.add(new TextPrompt(USER_NAME_PROMPT, tbDbUsername));
|
||||
textPrompts.add(new TextPrompt(PASSWORD_PROMPT, tbDbPassword));
|
||||
textPrompts.add(new TextPrompt(DATABASE_NAME_PROMPT, tbDbName));
|
||||
configureTextPrompts(textPrompts);
|
||||
|
||||
goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false));
|
||||
badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false));
|
||||
}
|
||||
|
||||
final void validateSettings(){
|
||||
if(valid()){
|
||||
bnOk.setEnabled(true);
|
||||
if(cbEnableLogging.isSelected()){
|
||||
bnTestDatabase.setEnabled(true);
|
||||
} else {
|
||||
bnTestDatabase.setEnabled(false);
|
||||
}
|
||||
} else {
|
||||
bnOk.setEnabled(false);
|
||||
bnTestDatabase.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean valid(){
|
||||
if(cbEnableLogging.isSelected()){
|
||||
if(tbDbHostname.getText().isEmpty()
|
||||
|| tbDbPort.getText().isEmpty()
|
||||
|| tbDbUsername.getText().isEmpty()
|
||||
|| (tbDbPassword.getPassword().length == 0)
|
||||
|| tbDbName.getText().isEmpty()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void enableFields(boolean enable){
|
||||
tbDbHostname.setEnabled(enable);
|
||||
tbDbPort.setEnabled(enable);
|
||||
tbDbUsername.setEnabled(enable);
|
||||
tbDbPassword.setEnabled(enable);
|
||||
tbDbName.setEnabled(enable);
|
||||
}
|
||||
|
||||
final void load(){
|
||||
|
||||
try{
|
||||
cbEnableLogging.setSelected(AutoIngestUserPreferences.getStatusDatabaseLoggingEnabled());
|
||||
tbDbHostname.setText(AutoIngestUserPreferences.getLoggingDatabaseHostnameOrIP());
|
||||
tbDbPort.setText(AutoIngestUserPreferences.getLoggingPort());
|
||||
tbDbUsername.setText(AutoIngestUserPreferences.getLoggingUsername());
|
||||
tbDbPassword.setText(AutoIngestUserPreferences.getLoggingPassword());
|
||||
} catch (UserPreferencesException ex) {
|
||||
logger.log(Level.SEVERE, "Error accessing status database connection info", ex); //NON-NLS
|
||||
}
|
||||
tbDbName.setText(AutoIngestUserPreferences.getLoggingDatabaseName());
|
||||
}
|
||||
|
||||
void store(){
|
||||
AutoIngestUserPreferences.setStatusDatabaseLoggingEnabled(cbEnableLogging.isSelected());
|
||||
if(cbEnableLogging.isSelected()){
|
||||
try{
|
||||
AutoIngestUserPreferences.setLoggingDatabaseHostnameOrIP(tbDbHostname.getText().trim());
|
||||
AutoIngestUserPreferences.setLoggingPort(tbDbPort.getText().trim());
|
||||
AutoIngestUserPreferences.setLoggingUsername(tbDbUsername.getText().trim());
|
||||
AutoIngestUserPreferences.setLoggingPassword(new String(tbDbPassword.getPassword()));
|
||||
AutoIngestUserPreferences.setLoggingDatabaseName(tbDbName.getText().trim());
|
||||
} catch (UserPreferencesException ex) {
|
||||
logger.log(Level.SEVERE, "Error saving database connection info", ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the foreground color and transparency of a collection of text
|
||||
* prompts.
|
||||
*
|
||||
* @param textPrompts The text prompts to configure.
|
||||
*/
|
||||
private static void configureTextPrompts(Collection<TextPrompt> textPrompts) {
|
||||
float alpha = 0.9f; // Mostly opaque
|
||||
for (TextPrompt textPrompt : textPrompts) {
|
||||
textPrompt.setForeground(Color.LIGHT_GRAY);
|
||||
textPrompt.changeAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
private void testDatabase(){
|
||||
String host = tbDbHostname.getText();
|
||||
String port = tbDbPort.getText();
|
||||
String username = tbDbUsername.getText();
|
||||
String password = new String(tbDbPassword.getPassword());
|
||||
String dbName = tbDbName.getText();
|
||||
|
||||
lbTestDatabase.setIcon(null);
|
||||
lbTestDbWarning.setText("");
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
// First test whether we can connect to the database
|
||||
try{
|
||||
Class.forName("org.postgresql.Driver");
|
||||
} catch (ClassNotFoundException ex){
|
||||
// Continue on even if this fails
|
||||
}
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + host + ":" + port + "/" + dbName,
|
||||
username, password);
|
||||
Statement statement = connection.createStatement();) {
|
||||
// Now make sure the database is set up for logging
|
||||
try{
|
||||
StatusDatabaseLogger.logToStatusDatabase(statement, "Testing configuration", false);
|
||||
lbTestDatabase.setIcon(goodIcon);
|
||||
lbTestDbWarning.setText("");
|
||||
} catch (SQLException ex){
|
||||
lbTestDatabase.setIcon(badIcon);
|
||||
lbTestDbWarning.setText("Database is not correctly initialized - " + ex.getMessage());
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
lbTestDatabase.setIcon(badIcon);
|
||||
lbTestDbWarning.setText(ex.getMessage());
|
||||
} finally {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
pnDatabaseSettings = new javax.swing.JPanel();
|
||||
tbDbHostname = new javax.swing.JTextField();
|
||||
tbDbPort = new javax.swing.JTextField();
|
||||
tbDbUsername = new javax.swing.JTextField();
|
||||
tbDbPassword = new javax.swing.JPasswordField();
|
||||
lbDatabaseSettings = new javax.swing.JLabel();
|
||||
bnTestDatabase = new javax.swing.JButton();
|
||||
lbTestDatabase = new javax.swing.JLabel();
|
||||
lbTestDbWarning = new javax.swing.JLabel();
|
||||
tbDbName = new javax.swing.JTextField();
|
||||
cbEnableLogging = new javax.swing.JCheckBox();
|
||||
bnOk = new javax.swing.JButton();
|
||||
bnCancel = new javax.swing.JButton();
|
||||
|
||||
pnDatabaseSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
|
||||
tbDbHostname.setFont(tbDbHostname.getFont().deriveFont(tbDbHostname.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
|
||||
tbDbHostname.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.text")); // NOI18N
|
||||
tbDbHostname.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.toolTipText")); // NOI18N
|
||||
|
||||
tbDbPort.setFont(tbDbPort.getFont().deriveFont(tbDbPort.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
|
||||
tbDbPort.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.text")); // NOI18N
|
||||
tbDbPort.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.toolTipText")); // NOI18N
|
||||
|
||||
tbDbUsername.setFont(tbDbUsername.getFont().deriveFont(tbDbUsername.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
|
||||
tbDbUsername.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.text")); // NOI18N
|
||||
tbDbUsername.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.toolTipText")); // NOI18N
|
||||
|
||||
tbDbPassword.setFont(tbDbPassword.getFont().deriveFont(tbDbPassword.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
|
||||
tbDbPassword.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.text")); // NOI18N
|
||||
tbDbPassword.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.toolTipText")); // NOI18N
|
||||
|
||||
lbDatabaseSettings.setFont(lbDatabaseSettings.getFont().deriveFont(lbDatabaseSettings.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseSettings, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbDatabaseSettings.text")); // NOI18N
|
||||
lbDatabaseSettings.setVerticalAlignment(javax.swing.SwingConstants.TOP);
|
||||
|
||||
bnTestDatabase.setFont(bnTestDatabase.getFont().deriveFont(bnTestDatabase.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnTestDatabase, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnTestDatabase.text")); // NOI18N
|
||||
bnTestDatabase.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnTestDatabaseActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbTestDatabase, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbTestDatabase.text")); // NOI18N
|
||||
lbTestDatabase.setAutoscrolls(true);
|
||||
|
||||
lbTestDbWarning.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbTestDbWarning, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbTestDbWarning.text")); // NOI18N
|
||||
|
||||
tbDbName.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.text")); // NOI18N
|
||||
tbDbName.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.toolTipText_1")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout pnDatabaseSettingsLayout = new javax.swing.GroupLayout(pnDatabaseSettings);
|
||||
pnDatabaseSettings.setLayout(pnDatabaseSettingsLayout);
|
||||
pnDatabaseSettingsLayout.setHorizontalGroup(
|
||||
pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(pnDatabaseSettingsLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(pnDatabaseSettingsLayout.createSequentialGroup()
|
||||
.addComponent(lbDatabaseSettings)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 344, Short.MAX_VALUE)
|
||||
.addComponent(bnTestDatabase)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(tbDbHostname)
|
||||
.addComponent(tbDbPort)
|
||||
.addComponent(tbDbUsername)
|
||||
.addComponent(tbDbPassword)
|
||||
.addComponent(tbDbName)
|
||||
.addComponent(lbTestDbWarning, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
pnDatabaseSettingsLayout.setVerticalGroup(
|
||||
pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnDatabaseSettingsLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(bnTestDatabase)
|
||||
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lbDatabaseSettings))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbDbName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(lbTestDbWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(24, 24, 24))
|
||||
);
|
||||
|
||||
cbEnableLogging.setFont(cbEnableLogging.getFont().deriveFont(cbEnableLogging.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(cbEnableLogging, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.cbEnableLogging.text")); // NOI18N
|
||||
cbEnableLogging.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
cbEnableLoggingItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnOk, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnOk.text")); // NOI18N
|
||||
bnOk.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnOkActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnCancel.text")); // NOI18N
|
||||
bnCancel.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnCancelActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(232, 232, 232)
|
||||
.addComponent(bnOk)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(bnCancel)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(cbEnableLogging)
|
||||
.addContainerGap(421, Short.MAX_VALUE)))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(79, 79, 79)
|
||||
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(bnOk)
|
||||
.addComponent(bnCancel))
|
||||
.addContainerGap(69, Short.MAX_VALUE))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(48, 48, 48)
|
||||
.addComponent(cbEnableLogging)
|
||||
.addContainerGap(324, Short.MAX_VALUE)))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cbEnableLoggingItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cbEnableLoggingItemStateChanged
|
||||
enableFields(cbEnableLogging.isSelected());
|
||||
validateSettings();
|
||||
}//GEN-LAST:event_cbEnableLoggingItemStateChanged
|
||||
|
||||
private void bnTestDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestDatabaseActionPerformed
|
||||
testDatabase();
|
||||
}//GEN-LAST:event_bnTestDatabaseActionPerformed
|
||||
|
||||
private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed
|
||||
store();
|
||||
jDialog.dispose();
|
||||
}//GEN-LAST:event_bnOkActionPerformed
|
||||
|
||||
private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed
|
||||
jDialog.dispose();
|
||||
}//GEN-LAST:event_bnCancelActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton bnCancel;
|
||||
private javax.swing.JButton bnOk;
|
||||
private javax.swing.JButton bnTestDatabase;
|
||||
private javax.swing.JCheckBox cbEnableLogging;
|
||||
private javax.swing.JLabel lbDatabaseSettings;
|
||||
private javax.swing.JLabel lbTestDatabase;
|
||||
private javax.swing.JLabel lbTestDbWarning;
|
||||
private javax.swing.JPanel pnDatabaseSettings;
|
||||
private javax.swing.JTextField tbDbHostname;
|
||||
private javax.swing.JTextField tbDbName;
|
||||
private javax.swing.JPasswordField tbDbPassword;
|
||||
private javax.swing.JTextField tbDbPort;
|
||||
private javax.swing.JTextField tbDbUsername;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private class MyDocumentListener implements DocumentListener {
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
validateSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
validateSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
validateSettings();
|
||||
}
|
||||
};
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -37,7 +37,6 @@ import javafx.beans.Observable;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@ -51,7 +50,6 @@ import javax.annotation.Nonnull;
|
||||
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.Cancellable;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
||||
@ -71,7 +69,6 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
|
||||
@ -117,7 +114,7 @@ public final class ImageGalleryController {
|
||||
private final CategoryManager categoryManager;
|
||||
private final DrawableTagsManager tagsManager;
|
||||
|
||||
private ListeningExecutorService dbExecutor;
|
||||
private final ListeningExecutorService dbExecutor;
|
||||
|
||||
private final Case autopsyCase;
|
||||
private final SleuthkitCase sleuthKitCase;
|
||||
@ -168,7 +165,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param b True if any data source in the case is stale
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||
@ -183,7 +180,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return true if any data source in the case is stale
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@ -192,7 +189,6 @@ public final class ImageGalleryController {
|
||||
}
|
||||
|
||||
ImageGalleryController(@Nonnull Case newCase) throws TskCoreException {
|
||||
|
||||
this.autopsyCase = Objects.requireNonNull(newCase);
|
||||
this.sleuthKitCase = newCase.getSleuthkitCase();
|
||||
|
||||
@ -325,19 +321,17 @@ public final class ImageGalleryController {
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the state of the controller (eg if the case is closed)
|
||||
* Shuts down this per case singleton image gallery controller.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
logger.info("Closing ImageGalleryControler for case."); //NON-NLS
|
||||
|
||||
public synchronized void shutDown() {
|
||||
logger.log(Level.INFO, String.format("Shutting down image gallery controller for case %s (%s)", autopsyCase.getDisplayName(), autopsyCase.getName()));
|
||||
selectionModel.clearSelection();
|
||||
thumbnailCache.clearCache();
|
||||
historyManager.clear();
|
||||
groupManager.reset();
|
||||
|
||||
shutDownDBExecutor();
|
||||
drawableDB.close();
|
||||
dbExecutor = getNewDBExecutor();
|
||||
logger.log(Level.INFO, String.format("Completed shut down of image gallery controller for case %s (%s)", autopsyCase.getDisplayName(), autopsyCase.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,8 +347,8 @@ public final class ImageGalleryController {
|
||||
* Returns a set of data source object ids that are stale.
|
||||
*
|
||||
* This includes any data sources already in the table, that are not in
|
||||
* COMPLETE or IN_PROGRESS status, or any data sources that might have been added to the
|
||||
* case, but are not in the datasources table.
|
||||
* COMPLETE or IN_PROGRESS status, or any data sources that might have been
|
||||
* added to the case, but are not in the datasources table.
|
||||
*
|
||||
* @return list of data source object ids that are stale.
|
||||
*/
|
||||
@ -491,7 +485,7 @@ public final class ImageGalleryController {
|
||||
|
||||
return sleuthKitCase.countFilesWhere(whereClause) > 0;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasFilesWithMimeType(long dataSourceId) throws TskCoreException {
|
||||
|
||||
String whereClause = "data_source_obj_id = " + dataSourceId
|
||||
@ -502,13 +496,11 @@ public final class ImageGalleryController {
|
||||
}
|
||||
|
||||
synchronized private void shutDownDBExecutor() {
|
||||
if (dbExecutor != null) {
|
||||
dbExecutor.shutdownNow();
|
||||
try {
|
||||
dbExecutor.awaitTermination(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.WARNING, "Image Gallery failed to shutdown DB Task Executor in a timely fashion.", ex);
|
||||
}
|
||||
dbExecutor.shutdownNow();
|
||||
try {
|
||||
dbExecutor.awaitTermination(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.WARNING, "Image Gallery failed to shutdown DB Task Executor in a timely fashion.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,12 +515,10 @@ public final class ImageGalleryController {
|
||||
* @param bgTask
|
||||
*/
|
||||
public synchronized void queueDBTask(BackgroundTask bgTask) {
|
||||
if (dbExecutor == null || dbExecutor.isShutdown()) {
|
||||
dbExecutor = getNewDBExecutor();
|
||||
if (!dbExecutor.isShutdown()) {
|
||||
incrementQueueSize();
|
||||
dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor());
|
||||
}
|
||||
incrementQueueSize();
|
||||
dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor());
|
||||
|
||||
}
|
||||
|
||||
private void incrementQueueSize() {
|
||||
@ -634,7 +624,6 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* task that updates one file in database with results from ingest
|
||||
*/
|
||||
@ -650,7 +639,7 @@ public final class ImageGalleryController {
|
||||
public AbstractFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
UpdateFileTask(AbstractFile f, DrawableDB taskDB) {
|
||||
super();
|
||||
this.file = f;
|
||||
@ -671,7 +660,6 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base abstract class for various methods of copying image files data, for
|
||||
* a given data source, into the Image gallery DB.
|
||||
@ -680,6 +668,7 @@ public final class ImageGalleryController {
|
||||
"BulkTask.stopCopy.status=Stopping copy to drawable db task.",
|
||||
"BulkTask.errPopulating.errMsg=There was an error populating Image Gallery database."})
|
||||
abstract static class BulkTransferTask extends BackgroundTask {
|
||||
|
||||
static private final String MIMETYPE_CLAUSE
|
||||
= "(mime_type LIKE '" //NON-NLS
|
||||
+ String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS
|
||||
@ -742,11 +731,11 @@ public final class ImageGalleryController {
|
||||
CaseDbTransaction caseDbTransaction = null;
|
||||
boolean hasFilesWithNoMime = true;
|
||||
boolean endedEarly = false;
|
||||
|
||||
|
||||
try {
|
||||
// See if there are any files in the DS w/out a MIME TYPE
|
||||
hasFilesWithNoMime = controller.hasFilesWithNoMimeType(dataSourceObjId);
|
||||
|
||||
|
||||
//grab all files with detected mime types
|
||||
final List<AbstractFile> files = getFiles();
|
||||
progressHandle.switchToDeterminate(files.size());
|
||||
@ -756,7 +745,6 @@ public final class ImageGalleryController {
|
||||
updateProgress(0.0);
|
||||
int workDone = 0;
|
||||
|
||||
|
||||
// Cycle through all of the files returned and call processFile on each
|
||||
//do in transaction
|
||||
drawableDbTransaction = taskDB.beginTransaction();
|
||||
@ -776,7 +764,6 @@ public final class ImageGalleryController {
|
||||
logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS
|
||||
endedEarly = true;
|
||||
progressHandle.finish();
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-18 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,12 +22,16 @@ import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
@ -35,16 +39,13 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.FILE_DONE;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisStartedEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisEvent;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -53,262 +54,266 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/** static definitions, utilities, and listeners for the ImageGallery module */
|
||||
/**
|
||||
* This class is reponsible for handling selected application events for the
|
||||
* image gallery module, managing the image gallery module's per case MVC
|
||||
* controller and keeping track of the following state: the module name, the
|
||||
* module output directory and whether or not the ingest gallery module is
|
||||
* enabled for the current case.
|
||||
*/
|
||||
@NbBundle.Messages({"ImageGalleryModule.moduleName=Image Gallery"})
|
||||
public class ImageGalleryModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ImageGalleryModule.class.getName());
|
||||
|
||||
private static final String MODULE_NAME = Bundle.ImageGalleryModule_moduleName();
|
||||
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
|
||||
Case.Events.CURRENT_CASE,
|
||||
Case.Events.DATA_SOURCE_ADDED,
|
||||
Case.Events.CONTENT_TAG_ADDED,
|
||||
Case.Events.CONTENT_TAG_DELETED
|
||||
);
|
||||
private static final Object controllerLock = new Object();
|
||||
@GuardedBy("controllerLock")
|
||||
private static ImageGalleryController controller;
|
||||
|
||||
public static ImageGalleryController getController() throws TskCoreException, NoCurrentCaseException {
|
||||
/**
|
||||
* Gets the per case image gallery controller for the current case. The
|
||||
* controller is changed in the case event listener.
|
||||
*
|
||||
* @return The image gallery controller for the current case.
|
||||
*
|
||||
* @throws TskCoreException If there is a problem creating the controller.
|
||||
*/
|
||||
public static ImageGalleryController getController() throws TskCoreException {
|
||||
synchronized (controllerLock) {
|
||||
if (controller == null) {
|
||||
controller = new ImageGalleryController(Case.getCurrentCaseThrows());
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
controller = new ImageGalleryController(currentCase);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("Failed to get ", ex);
|
||||
}
|
||||
}
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This method is invoked by virtue of the OnStart annotation on the OnStart
|
||||
* class class
|
||||
* Sets the implicit exit property attribute of the JavaFX runtime to false
|
||||
* and sets up listeners for application events. It is invoked at
|
||||
* application start up by virtue of the OnStart annotation on the OnStart
|
||||
* class in this package.
|
||||
*/
|
||||
static void onStart() {
|
||||
Platform.setImplicitExit(false);
|
||||
logger.info("Setting up ImageGallery listeners"); //NON-NLS
|
||||
|
||||
IngestManager.getInstance().addIngestJobEventListener(new IngestJobEventListener());
|
||||
IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener());
|
||||
Case.addPropertyChangeListener(new CaseEventListener());
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, new CaseEventListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image gallery module name.
|
||||
*
|
||||
* @return The module name,
|
||||
*/
|
||||
static String getModuleName() {
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the Path to the Case's ImageGallery ModuleOutput subfolder; ie
|
||||
* ".../[CaseName]/ModuleOutput/Image Gallery/"
|
||||
* Gets the path to the image gallery module output folder for a given case.
|
||||
*
|
||||
* @param theCase the case to get the ImageGallery ModuleOutput subfolder
|
||||
* for
|
||||
* @param theCase The case.
|
||||
*
|
||||
* @return the Path to the ModuleOuput subfolder for Image Gallery
|
||||
* @return The path to the image gallery module output folder for the case.
|
||||
*/
|
||||
public static Path getModuleOutputDir(Case theCase) {
|
||||
return Paths.get(theCase.getModuleDirectory(), getModuleName());
|
||||
}
|
||||
|
||||
/** provides static utilities, can not be instantiated */
|
||||
/**
|
||||
* Prevents instantiation.
|
||||
*/
|
||||
private ImageGalleryModule() {
|
||||
}
|
||||
|
||||
/** is listening enabled for the given case
|
||||
/**
|
||||
* Indicates whether or not the image gallery module is enabled for a given
|
||||
* case.
|
||||
*
|
||||
* @param c
|
||||
* @param theCase The case.
|
||||
*
|
||||
* @return true if listening is enabled for the given case, false otherwise
|
||||
* @return True or false.
|
||||
*/
|
||||
static boolean isEnabledforCase(Case c) {
|
||||
if (c != null) {
|
||||
String enabledforCaseProp = new PerCaseProperties(c).getConfigSetting(ImageGalleryModule.MODULE_NAME, PerCaseProperties.ENABLED);
|
||||
return isNotBlank(enabledforCaseProp) ? Boolean.valueOf(enabledforCaseProp) : ImageGalleryPreferences.isEnabledByDefault();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
static boolean isEnabledforCase(@Nonnull Case theCase) {
|
||||
String enabledforCaseProp = new PerCaseProperties(theCase).getConfigSetting(ImageGalleryModule.MODULE_NAME, PerCaseProperties.ENABLED);
|
||||
return isNotBlank(enabledforCaseProp) ? Boolean.valueOf(enabledforCaseProp) : ImageGalleryPreferences.isEnabledByDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given file 'supported' and not 'known'(nsrl hash hit). If so we
|
||||
* should include it in {@link DrawableDB} and UI
|
||||
* Indicates whether or not a given file is of interest to the image gallery
|
||||
* module (is "drawable") and is not marked as a "known" file (e.g., is not
|
||||
* a file in the NSRL hash set).
|
||||
*
|
||||
* @param abstractFile
|
||||
* @param file The file.
|
||||
*
|
||||
* @return true if the given {@link AbstractFile} is "drawable" and not
|
||||
* 'known', else false
|
||||
* @return True if the file is "drawable" and not "known", false otherwise.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException
|
||||
* @throws FileTypeDetectorInitException If there is an error determining
|
||||
* the type of the file.
|
||||
*/
|
||||
public static boolean isDrawableAndNotKnown(AbstractFile abstractFile) throws FileTypeDetector.FileTypeDetectorInitException {
|
||||
private static boolean isDrawableAndNotKnown(AbstractFile abstractFile) throws FileTypeDetector.FileTypeDetectorInitException {
|
||||
return (abstractFile.getKnown() != TskData.FileKnown.KNOWN) && FileTypeUtils.isDrawable(abstractFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for IngestModuleEvents
|
||||
* A listener for ingest module application events.
|
||||
*/
|
||||
static private class IngestModuleEventListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (RuntimeProperties.runningWithGUI() == false) {
|
||||
/*
|
||||
* Running in "headless" mode, no need to process any events.
|
||||
* This cannot be done earlier because the switch to core
|
||||
* components inactive may not have been made at start up.
|
||||
*/
|
||||
IngestManager.getInstance().removeIngestModuleEventListener(this);
|
||||
public void propertyChange(PropertyChangeEvent event) {
|
||||
/*
|
||||
* Only process individual files and artifacts in "real time" on the
|
||||
* node that is running the ingest job. On a remote node, image
|
||||
* files are processed as a group when the ingest job is complete.
|
||||
*/
|
||||
// RJCTODO: DO we need to handle any events at all on an auot ingest node?
|
||||
if (((AutopsyEvent) event).getSourceType() != AutopsyEvent.SourceType.LOCAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* only process individual files in realtime on the node that is
|
||||
* running the ingest. on a remote node, image files are processed
|
||||
* enblock when ingest is complete */
|
||||
if (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail out if the case is closed
|
||||
ImageGalleryController currentController;
|
||||
try {
|
||||
if (controller == null || Case.getCurrentCaseThrows() == null) {
|
||||
return;
|
||||
}
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName()) == FILE_DONE) {
|
||||
|
||||
// getOldValue has fileID getNewValue has Abstractfile
|
||||
AbstractFile file = (AbstractFile) evt.getNewValue();
|
||||
if (false == file.isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ImageGalleryController con = getController();
|
||||
if (con.isListeningEnabled()) {
|
||||
try {
|
||||
// Update the entry if it is a picture and not in NSRL
|
||||
if (isDrawableAndNotKnown(file)) {
|
||||
con.queueDBTask(new ImageGalleryController.UpdateFileTask(file, controller.getDatabase()));
|
||||
}
|
||||
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to determine if file is drawable and not known. Not making any changes to DB", ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error("Image Gallery Error",
|
||||
"Unable to determine if file is drawable and not known. Not making any changes to DB. See the logs for details.");
|
||||
}
|
||||
}
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
else if (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName()) == DATA_ADDED) {
|
||||
ModuleDataEvent mde = (ModuleDataEvent) evt.getOldValue();
|
||||
|
||||
if (mde.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
|
||||
DrawableDB drawableDB = controller.getDatabase();
|
||||
if (mde.getArtifacts() != null) {
|
||||
for (BlackboardArtifact art : mde.getArtifacts()) {
|
||||
drawableDB.addExifCache(art.getObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mde.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
|
||||
DrawableDB drawableDB = controller.getDatabase();
|
||||
if (mde.getArtifacts() != null) {
|
||||
for (BlackboardArtifact art : mde.getArtifacts()) {
|
||||
drawableDB.addHashSetCache(art.getObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for case events.
|
||||
*/
|
||||
static private class CaseEventListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (RuntimeProperties.runningWithGUI() == false) {
|
||||
/*
|
||||
* Running in "headless" mode, no need to process any events.
|
||||
* This cannot be done earlier because the switch to core
|
||||
* components inactive may not have been made at start up.
|
||||
*/
|
||||
Case.removePropertyChangeListener(this);
|
||||
return;
|
||||
}
|
||||
ImageGalleryController con;
|
||||
try {
|
||||
con = getController();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS
|
||||
return;
|
||||
currentController = getController();
|
||||
// RJCTODO: If a closed controller had a method that could be
|
||||
// queried to determine whether it was shut down, we could
|
||||
// bail out here. The older code that used to try to check for
|
||||
// a current case was flawed; there was no guarantee the current
|
||||
// case was the same case associated with the event.
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, String.format("Failed to handle %s event", event.getPropertyName()), ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
switch (Case.Events.valueOf(evt.getPropertyName())) {
|
||||
case CURRENT_CASE:
|
||||
synchronized (controllerLock) {
|
||||
// case has changes: close window, reset everything
|
||||
SwingUtilities.invokeLater(ImageGalleryTopComponent::closeTopComponent);
|
||||
if (controller != null) {
|
||||
controller.reset();
|
||||
}
|
||||
controller = null;
|
||||
|
||||
Case newCase = (Case) evt.getNewValue();
|
||||
if (newCase != null) {
|
||||
// a new case has been opened: connect db, groupmanager, start worker thread
|
||||
try {
|
||||
controller = new ImageGalleryController(newCase);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error changing case in ImageGallery.", ex);
|
||||
String eventType = event.getPropertyName();
|
||||
switch (IngestManager.IngestModuleEvent.valueOf(eventType)) {
|
||||
case FILE_DONE:
|
||||
AbstractFile file = (AbstractFile) event.getNewValue();
|
||||
if (!file.isFile()) {
|
||||
return;
|
||||
}
|
||||
if (currentController.isListeningEnabled()) {
|
||||
try {
|
||||
if (isDrawableAndNotKnown(file)) {
|
||||
currentController.queueDBTask(new ImageGalleryController.UpdateFileTask(file, currentController.getDatabase()));
|
||||
}
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to determine if file is of interest to the image gallery module, ignoring file (obj_id=%d)", file.getId()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DATA_ADDED:
|
||||
ModuleDataEvent artifactAddedEvent = (ModuleDataEvent) event.getOldValue();
|
||||
if (CollectionUtils.isNotEmpty(artifactAddedEvent.getArtifacts())) {
|
||||
DrawableDB drawableDB = currentController.getDatabase();
|
||||
for (BlackboardArtifact art : artifactAddedEvent.getArtifacts()) {
|
||||
if (artifactAddedEvent.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
|
||||
drawableDB.addExifCache(art.getObjectID());
|
||||
} else if (artifactAddedEvent.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
|
||||
drawableDB.addHashSetCache(art.getObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DATA_SOURCE_ADDED:
|
||||
//For a data source added on the local node, prepopulate all file data to drawable database
|
||||
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
Content newDataSource = (Content) evt.getNewValue();
|
||||
if (con.isListeningEnabled()) {
|
||||
controller.getDatabase().insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_ADDED:
|
||||
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
|
||||
|
||||
long objId = tagAddedEvent.getAddedTag().getContent().getId();
|
||||
|
||||
// update the cache
|
||||
DrawableDB drawableDB = controller.getDatabase();
|
||||
drawableDB.addTagCache(objId);
|
||||
|
||||
if (con.getDatabase().isInDB(objId)) {
|
||||
con.getTagsManager().fireTagAddedEvent(tagAddedEvent);
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_DELETED:
|
||||
final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) evt;
|
||||
if (con.getDatabase().isInDB(tagDeletedEvent.getDeletedTagInfo().getContentID())) {
|
||||
con.getTagsManager().fireTagDeletedEvent(tagDeletedEvent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//we don't need to do anything for other events.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for Ingest Job events.
|
||||
* A listener for case application events.
|
||||
*/
|
||||
// RJCTODO: This code would be easier to read if there were two case event
|
||||
// listeners, one that handled CURRENT_CASE events and one that handled
|
||||
// the other events. Or event better, move the handling of Case events other
|
||||
// than CURRENT_CASE into ImageGalleryController.
|
||||
static private class CaseEventListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent event) {
|
||||
// RJCTODO: DO we need to handle any events at all on an auot ingest node?
|
||||
Case.Events eventType = Case.Events.valueOf(event.getPropertyName());
|
||||
if (eventType == Case.Events.CURRENT_CASE) {
|
||||
synchronized (controllerLock) {
|
||||
if (event.getNewValue() != null) {
|
||||
/*
|
||||
* CURRENT_CASE(_OPENED) event.
|
||||
*/
|
||||
Case newCase = (Case) event.getNewValue();
|
||||
try {
|
||||
controller = new ImageGalleryController(newCase);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to construct controller for new case %s (%s)", newCase.getDisplayName(), newCase.getName()), ex);
|
||||
}
|
||||
} else if (event.getOldValue() != null) {
|
||||
/*
|
||||
* CURRENT_CASE(_CLOSED) event.
|
||||
*/
|
||||
SwingUtilities.invokeLater(ImageGalleryTopComponent::closeTopComponent);
|
||||
controller.shutDown();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImageGalleryController currentController;
|
||||
try {
|
||||
currentController = getController();
|
||||
// RJCTODO: I think it would be best to move handling of these
|
||||
// case events into the controller class and have the controller
|
||||
// instance register/unregister as a listener when it is
|
||||
// contructed and shuts down. This will improve the encapsulation
|
||||
// of ImageGalleryController and allow it to check its own open/closed
|
||||
// state before handling an event.
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to handle %s event", event.getPropertyName()), ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case DATA_SOURCE_ADDED:
|
||||
if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
Content newDataSource = (Content) event.getNewValue();
|
||||
if (currentController.isListeningEnabled()) {
|
||||
currentController.getDatabase().insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_ADDED:
|
||||
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) event;
|
||||
long objId = tagAddedEvent.getAddedTag().getContent().getId();
|
||||
DrawableDB drawableDB = currentController.getDatabase();
|
||||
drawableDB.addTagCache(objId); // RJCTODO: Why add the tag to the cache before doing the in DB check?
|
||||
if (drawableDB.isInDB(objId)) {
|
||||
currentController.getTagsManager().fireTagAddedEvent(tagAddedEvent);
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_DELETED:
|
||||
final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) event;
|
||||
if (currentController.getDatabase().isInDB(tagDeletedEvent.getDeletedTagInfo().getContentID())) {
|
||||
currentController.getTagsManager().fireTagDeletedEvent(tagDeletedEvent);
|
||||
} // RJCTODO: Why not remove the tag from the cache?
|
||||
break;
|
||||
default:
|
||||
logger.log(Level.SEVERE, String.format("Received %s event with no subscription", event.getPropertyName())); //NON-NLS
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for ingest job application events.
|
||||
*/
|
||||
static private class IngestJobEventListener implements PropertyChangeListener {
|
||||
|
||||
@ -319,80 +324,105 @@ public class ImageGalleryModule {
|
||||
"ImageGalleryController.dataSourceAnalyzed.confDlg.title=Image Gallery"
|
||||
})
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
IngestJobEvent eventType = IngestJobEvent.valueOf(evt.getPropertyName());
|
||||
|
||||
try {
|
||||
ImageGalleryController controller = getController();
|
||||
|
||||
if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED) {
|
||||
|
||||
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
if (controller.isListeningEnabled()) {
|
||||
DataSourceAnalysisStartedEvent dataSourceAnalysisStartedEvent = (DataSourceAnalysisStartedEvent) evt;
|
||||
Content dataSource = dataSourceAnalysisStartedEvent.getDataSource();
|
||||
|
||||
DrawableDB drawableDb = controller.getDatabase();
|
||||
// Don't update status if it is is already marked as COMPLETE
|
||||
if (drawableDb.getDataSourceDbBuildStatus(dataSource.getId()) != DrawableDB.DrawableDbBuildStatusEnum.COMPLETE) {
|
||||
drawableDb.insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED) {
|
||||
|
||||
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
if (controller.isListeningEnabled()) {
|
||||
DataSourceAnalysisCompletedEvent dataSourceAnalysisCompletedEvent = (DataSourceAnalysisCompletedEvent) evt;
|
||||
Content dataSource = dataSourceAnalysisCompletedEvent.getDataSource();
|
||||
|
||||
DrawableDB drawableDb = controller.getDatabase();
|
||||
if (drawableDb.getDataSourceDbBuildStatus(dataSource.getId()) == DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS) {
|
||||
|
||||
// If at least one file in CaseDB has mime type, then set to COMPLETE
|
||||
// Otherwise, back to UNKNOWN since we assume file type module was not run
|
||||
DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus =
|
||||
controller.hasFilesWithMimeType(dataSource.getId()) ?
|
||||
DrawableDB.DrawableDbBuildStatusEnum.COMPLETE :
|
||||
DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN;
|
||||
|
||||
controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), datasourceDrawableDBStatus);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
|
||||
// A remote node added a new data source and just finished ingest on it.
|
||||
//drawable db is stale, and if ImageGallery is open, ask user what to do
|
||||
controller.setCaseStale(true);
|
||||
if (controller.isListeningEnabled()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (ImageGalleryTopComponent.isImageGalleryOpen()) {
|
||||
int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
switch (showAnswer) {
|
||||
case JOptionPane.YES_OPTION:
|
||||
controller.rebuildDB();
|
||||
break;
|
||||
case JOptionPane.NO_OPTION:
|
||||
case JOptionPane.CANCEL_OPTION:
|
||||
default:
|
||||
break; //do nothing
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public void propertyChange(PropertyChangeEvent event) {
|
||||
/*
|
||||
* Only handling data source analysis events.
|
||||
*/
|
||||
// RJCTODO: Do we need to handle any events at all on an auto ingest node?
|
||||
// RJCTODO: This would be less messy if IngestManager supported
|
||||
// subscribing for a subset of events the way case does, and it the
|
||||
// conditional blocks became method calls.
|
||||
if (!(event instanceof DataSourceAnalysisEvent)) {
|
||||
return;
|
||||
}
|
||||
catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS
|
||||
|
||||
ImageGalleryController controller;
|
||||
try {
|
||||
controller = getController();
|
||||
// RJCTODO: I think it would be best to move handling of these
|
||||
// case events into the controller class and have the controller
|
||||
// instance register/unregister as a listener when it is
|
||||
// contructed and shuts down. This will improve the encapsulation
|
||||
// of ImageGalleryController and allow it to check its own open/closed
|
||||
// state before handling an event.
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, String.format("Failed to handle %s event", event.getPropertyName()), ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
DataSourceAnalysisEvent dataSourceEvent = (DataSourceAnalysisEvent) event;
|
||||
Content dataSource = dataSourceEvent.getDataSource();
|
||||
long dataSourceObjId = dataSource.getId();
|
||||
String eventType = dataSourceEvent.getPropertyName();
|
||||
try {
|
||||
switch (IngestManager.IngestJobEvent.valueOf(eventType)) {
|
||||
case DATA_SOURCE_ANALYSIS_STARTED:
|
||||
if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
if (controller.isListeningEnabled()) {
|
||||
DrawableDB drawableDb = controller.getDatabase();
|
||||
// Don't update status if it is is already marked as COMPLETE
|
||||
if (drawableDb.getDataSourceDbBuildStatus(dataSourceObjId) != DrawableDB.DrawableDbBuildStatusEnum.COMPLETE) {
|
||||
drawableDb.insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DATA_SOURCE_ANALYSIS_COMPLETED:
|
||||
if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
|
||||
/*
|
||||
* This node just completed analysis of a data
|
||||
* source. Set the state of the local drawables
|
||||
* database.
|
||||
*/
|
||||
if (controller.isListeningEnabled()) {
|
||||
DrawableDB drawableDb = controller.getDatabase();
|
||||
if (drawableDb.getDataSourceDbBuildStatus(dataSourceObjId) == DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS) {
|
||||
|
||||
// If at least one file in CaseDB has mime type, then set to COMPLETE
|
||||
// Otherwise, back to UNKNOWN since we assume file type module was not run
|
||||
DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus
|
||||
= controller.hasFilesWithMimeType(dataSourceObjId)
|
||||
? DrawableDB.DrawableDbBuildStatusEnum.COMPLETE
|
||||
: DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN;
|
||||
|
||||
controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), datasourceDrawableDBStatus);
|
||||
}
|
||||
}
|
||||
} else if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
|
||||
/*
|
||||
* A remote node just completed analysis of a data
|
||||
* source. The local drawables database is therefore
|
||||
* stale. If the image gallery top component is
|
||||
* open, give the user an opportunity to update the
|
||||
* drawables database now.
|
||||
*/
|
||||
controller.setCaseStale(true);
|
||||
if (controller.isListeningEnabled()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (ImageGalleryTopComponent.isImageGalleryOpen()) {
|
||||
int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
switch (showAnswer) {
|
||||
case JOptionPane.YES_OPTION:
|
||||
controller.rebuildDB();
|
||||
break;
|
||||
case JOptionPane.NO_OPTION:
|
||||
case JOptionPane.CANCEL_OPTION:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to handle %s event for %s (objId=%d)", dataSourceEvent.getPropertyName(), dataSource.getName(), dataSourceObjId), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ final class ImageGalleryOptionsPanel extends javax.swing.JPanel {
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
// It's not an error if there's no case open
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to get image gallery controller", ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -49,6 +49,7 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Modality;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.SwingUtilities;
|
||||
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
|
||||
import static org.apache.commons.lang3.ObjectUtils.notEqual;
|
||||
@ -61,7 +62,6 @@ import org.openide.windows.Mode;
|
||||
import org.openide.windows.RetainLocation;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
@ -81,16 +81,11 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Top component which displays ImageGallery interface.
|
||||
*
|
||||
* Although ImageGallery doesn't currently use the explorer manager, this
|
||||
* TopComponent provides one through the getExplorerManager method. However,
|
||||
* this does not seem to function correctly unless a Netbeans provided explorer
|
||||
* view is present in the TopComponenet, even if it is invisible/ zero sized
|
||||
* The singleton Image Gallery top component.
|
||||
*/
|
||||
@TopComponent.Description(
|
||||
preferredID = "ImageGalleryTopComponent",
|
||||
//iconBase = "org/sleuthkit/autopsy/imagegallery/images/lightbulb.png" use this to put icon in window title area,
|
||||
//iconBase = "org/sleuthkit/autopsy/imagegallery/images/lightbulb.png", /*use this to put icon in window title area*/
|
||||
persistenceType = TopComponent.PERSISTENCE_NEVER)
|
||||
@RetainLocation("ImageGallery")
|
||||
@TopComponent.Registration(mode = "ImageGallery", openAtStartup = false)
|
||||
@ -101,13 +96,15 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class ImageGalleryTopComponent extends TopComponent implements ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
public final static String PREFERRED_ID = "ImageGalleryTopComponent"; // NON-NLS
|
||||
private static final long serialVersionUID = 1L;
|
||||
public final static String PREFERRED_ID = "ImageGalleryTopComponent"; // NON-NLS // RJCTODO: This should not be public, clients should call getTopComponent instead
|
||||
private static final Logger logger = Logger.getLogger(ImageGalleryTopComponent.class.getName());
|
||||
private static volatile boolean topComponentInitialized = false;
|
||||
|
||||
private final ExplorerManager em = new ExplorerManager();
|
||||
private final Lookup lookup = (ExplorerUtils.createLookup(em, getActionMap()));
|
||||
|
||||
private final Object controllerLock = new Object();
|
||||
@GuardedBy("controllerLock")
|
||||
private ImageGalleryController controller;
|
||||
|
||||
private SplitPane splitPane;
|
||||
@ -125,148 +122,82 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
private final Region infoOverLayBackground = new TranslucentRegion();
|
||||
|
||||
/**
|
||||
* Returns whether the ImageGallery window is open or not.
|
||||
* Queries whether the singleton Image Gallery top component's window is
|
||||
* open. Note that calling this method will cause the top component to be
|
||||
* constructed if it does not already exist.
|
||||
*
|
||||
* @return true, if Image gallery is opened, false otherwise
|
||||
* @return True or false.
|
||||
*/
|
||||
public static boolean isImageGalleryOpen() {
|
||||
|
||||
final TopComponent topComponent = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
if (topComponent != null) {
|
||||
return topComponent.isOpened();
|
||||
}
|
||||
return false;
|
||||
return getTopComponent().isOpened();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top component window.
|
||||
* Gets the singleton Image Gallery top component. Note that calling this
|
||||
* method will cause the top component to be constructed if it does not
|
||||
* already exist.
|
||||
*
|
||||
* @return Image gallery top component window, null if it's not open
|
||||
* @return The top component.
|
||||
*/
|
||||
public static TopComponent getTopComponent() {
|
||||
return WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
public static ImageGalleryTopComponent getTopComponent() {
|
||||
return (ImageGalleryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the ImageGalleryTopComponent.
|
||||
* Creates the Image Gallery top component if it does not already exist and
|
||||
* opens its window.
|
||||
*
|
||||
* @throws NoCurrentCaseException If there is no case open.
|
||||
* @throws TskCoreException If there is a problem accessing the case
|
||||
* db.
|
||||
* @throws TskCoreException If there is a problem opening the top component.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@Messages({
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.headerText=Choose a data source to view.",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.contentText=Data source:",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.all=All",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.titleText=Image Gallery",})
|
||||
public static void openTopComponent() throws NoCurrentCaseException, TskCoreException {
|
||||
|
||||
// This creates the top component and adds the UI widgets (via the constructor) if it has not yet been opened
|
||||
final TopComponent topComponent = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
if (topComponent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void openTopComponent() throws TskCoreException {
|
||||
final ImageGalleryTopComponent topComponent = getTopComponent();
|
||||
if (topComponent.isOpened()) {
|
||||
showTopComponent(topComponent);
|
||||
return;
|
||||
showTopComponent();
|
||||
} else {
|
||||
topComponent.getCurrentControllerAndOpen();
|
||||
}
|
||||
|
||||
// Wait until the FX UI has been created. This way, we can always
|
||||
// show the gray progress screen
|
||||
// TODO: do this in a more elegant way.
|
||||
while (topComponentInitialized == false) {
|
||||
}
|
||||
|
||||
ImageGalleryController controller = ImageGalleryModule.getController();
|
||||
ImageGalleryTopComponent igTopComponent = (ImageGalleryTopComponent) topComponent;
|
||||
igTopComponent.setController(controller);
|
||||
|
||||
//gather information about datasources and the groupmanager in a bg thread.
|
||||
new Thread(new Task<Void>() {
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
Map<DataSource, Boolean> dataSourcesTooManyFiles = new HashMap<>();
|
||||
List<DataSource> dataSources = controller.getSleuthKitCase().getDataSources();
|
||||
|
||||
/*
|
||||
* If there is only one datasource or the grouping is already
|
||||
* set to something other than path , don't bother to ask for
|
||||
* datasource.
|
||||
*/
|
||||
if (dataSources.size() <= 1
|
||||
|| controller.getGroupManager().getGroupBy() != DrawableAttribute.PATH) {
|
||||
// null represents all datasources, which is only one in this case.
|
||||
dataSourcesTooManyFiles.put(null, controller.hasTooManyFiles(null));
|
||||
igTopComponent.showDataSource(null, dataSourcesTooManyFiles);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Else there is more than one data source and the grouping is
|
||||
* PATH (the default): open a dialog prompting the user to pick
|
||||
* a datasource.
|
||||
*/
|
||||
dataSources.add(0, null); //null represents all datasources
|
||||
//first, while still on background thread, gather viewability info for the datasources.
|
||||
for (DataSource dataSource : dataSources) {
|
||||
dataSourcesTooManyFiles.put(dataSource, controller.hasTooManyFiles(dataSource));
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
//configure the dialog
|
||||
List<Optional<DataSource>> dataSourceOptionals = dataSources.stream().map(Optional::ofNullable).collect(Collectors.toList());
|
||||
ChoiceDialog<Optional<DataSource>> datasourceDialog = new ChoiceDialog<>(null, dataSourceOptionals);
|
||||
datasourceDialog.setTitle(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_titleText());
|
||||
datasourceDialog.setHeaderText(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_headerText());
|
||||
datasourceDialog.setContentText(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_contentText());
|
||||
datasourceDialog.initModality(Modality.APPLICATION_MODAL);
|
||||
GuiUtils.setDialogIcons(datasourceDialog);
|
||||
//get the combobox by its css class... this is hacky but should be safe.
|
||||
@SuppressWarnings(value = "unchecked")
|
||||
ComboBox<Optional<DataSource>> comboBox = (ComboBox<Optional<DataSource>>) datasourceDialog.getDialogPane().lookup(".combo-box");
|
||||
//set custom cell renderer
|
||||
comboBox.setCellFactory((ListView<Optional<DataSource>> param) -> new DataSourceCell(dataSourcesTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
comboBox.setButtonCell(new DataSourceCell(dataSourcesTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
|
||||
DataSource dataSource = datasourceDialog.showAndWait().orElse(Optional.empty()).orElse(null);
|
||||
try {
|
||||
|
||||
igTopComponent.showDataSource(dataSource, dataSourcesTooManyFiles);
|
||||
} catch (TskCoreException ex) {
|
||||
if (dataSource != null) {
|
||||
logger.log(Level.SEVERE, "Error showing data source " + dataSource.getName() + ":" + dataSource.getId() + " in Image Gallery", ex);
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "Error showing all data sources in Image Gallery.", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
synchronized private void showDataSource(DataSource datasource, Map<DataSource, Boolean> dataSourcesTooManyFiles) throws TskCoreException {
|
||||
if (dataSourcesTooManyFiles.get(datasource)) {
|
||||
/**
|
||||
* Configures the groups manager for the selected data source(s) and opens
|
||||
* this top component's window.
|
||||
*
|
||||
* @param selectedDataSource The data source selected, null if all data
|
||||
* sources are selected.
|
||||
* @param dataSourcesTooManyFiles A map of data sources to flags indicating
|
||||
* whether or not the data source has to many
|
||||
* files to actually be displayed.
|
||||
*/
|
||||
private void openWithSelectedDataSources(DataSource selectedDataSource, Map<DataSource, Boolean> dataSourcesTooManyFiles) {
|
||||
if (dataSourcesTooManyFiles.get(selectedDataSource)) {
|
||||
Platform.runLater(ImageGalleryTopComponent::showTooManyFiles);
|
||||
return;
|
||||
}
|
||||
// Display the UI so that they can see the progress screen
|
||||
SwingUtilities.invokeLater(() -> showTopComponent(this));
|
||||
GroupManager groupManager = controller.getGroupManager();
|
||||
synchronized (groupManager) {
|
||||
groupManager.regroup(datasource, groupManager.getGroupBy(), groupManager.getSortBy(), groupManager.getSortOrder(), true);
|
||||
} else {
|
||||
SwingUtilities.invokeLater(() -> showTopComponent());
|
||||
synchronized (controllerLock) {
|
||||
GroupManager groupManager = controller.getGroupManager();
|
||||
// RJCTODO: Why are there potentially hazardous nested synchronized
|
||||
// blocks here (note: method used to be synchronized, my
|
||||
// dedicated controllerLock lock just makes the nesting more obvious)?
|
||||
// Why is the groups manager not taking responsibility for its own thread
|
||||
// safety policy?
|
||||
synchronized (groupManager) {
|
||||
groupManager.regroup(selectedDataSource, groupManager.getGroupBy(), groupManager.getSortBy(), groupManager.getSortOrder(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dialog box informing the user that the data source(s) selected
|
||||
* to have their images displayed have too many image files and will not be
|
||||
* displayed.
|
||||
*/
|
||||
@NbBundle.Messages({"ImageGallery.dialogTitle=Image Gallery",
|
||||
"ImageGallery.showTooManyFiles.contentText=There are too many files in the selected datasource(s) to ensure reasonable performance.",
|
||||
"ImageGallery.showTooManyFiles.headerText="})
|
||||
public static void showTooManyFiles() {
|
||||
Alert dialog = new Alert(Alert.AlertType.INFORMATION,
|
||||
Bundle.ImageGallery_showTooManyFiles_contentText(), ButtonType.OK);
|
||||
private static void showTooManyFiles() {
|
||||
Alert dialog = new Alert(Alert.AlertType.INFORMATION, Bundle.ImageGallery_showTooManyFiles_contentText(), ButtonType.OK);
|
||||
dialog.initModality(Modality.APPLICATION_MODAL);
|
||||
dialog.setTitle(Bundle.ImageGallery_dialogTitle());
|
||||
GuiUtils.setDialogIcons(dialog);
|
||||
@ -274,8 +205,14 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
dialog.showAndWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the singleton top component's window, brings it to the front and
|
||||
* gives it focus. Note that calling this method will cause the top
|
||||
* component to be constructed if it does not already exist.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public static void showTopComponent(TopComponent topComponent) {
|
||||
private static void showTopComponent() {
|
||||
final ImageGalleryTopComponent topComponent = getTopComponent();
|
||||
if (topComponent.isOpened() == false) {
|
||||
topComponent.open();
|
||||
}
|
||||
@ -283,76 +220,177 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
topComponent.requestActive();
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes the singleton Image Gallery top component. Note that calling this
|
||||
* method will cause the top component to be constructed if it does not
|
||||
* already exist.
|
||||
*/
|
||||
public static void closeTopComponent() {
|
||||
if (topComponentInitialized) {
|
||||
final TopComponent etc = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
if (etc != null) {
|
||||
try {
|
||||
etc.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "failed to close " + PREFERRED_ID, e); // NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
// RJCTODO: Could add the flag that used to be used for the busy wait on
|
||||
// the initial JavaFX thread task to avoid superfluous construction here.
|
||||
getTopComponent().close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contructs the singleton Image Gallery top component. Called by the
|
||||
* NetBeans WindowManager.
|
||||
*/
|
||||
public ImageGalleryTopComponent() {
|
||||
setName(Bundle.CTL_ImageGalleryTopComponent());
|
||||
initComponents();
|
||||
try {
|
||||
setController(ImageGalleryModule.getController());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private void setController(ImageGalleryController controller) {
|
||||
if (notEqual(this.controller, controller)) {
|
||||
if (this.controller != null) {
|
||||
this.controller.reset();
|
||||
}
|
||||
this.controller = controller;
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//initialize jfx ui
|
||||
fullUIStack = new StackPane(); //this is passed into controller
|
||||
myScene = new Scene(fullUIStack);
|
||||
jfxPanel.setScene(myScene);
|
||||
groupPane = new GroupPane(controller);
|
||||
centralStack = new StackPane(groupPane); //this is passed into controller
|
||||
fullUIStack.getChildren().add(borderPane);
|
||||
splitPane = new SplitPane();
|
||||
borderPane.setCenter(splitPane);
|
||||
Toolbar toolbar = new Toolbar(controller);
|
||||
borderPane.setTop(toolbar);
|
||||
borderPane.setBottom(new StatusBar(controller));
|
||||
metaDataTable = new MetaDataPane(controller);
|
||||
groupTree = new GroupTree(controller);
|
||||
hashHitList = new HashHitGroupList(controller);
|
||||
TabPane tabPane = new TabPane(groupTree, hashHitList);
|
||||
tabPane.setPrefWidth(TabPane.USE_COMPUTED_SIZE);
|
||||
tabPane.setMinWidth(TabPane.USE_PREF_SIZE);
|
||||
VBox.setVgrow(tabPane, Priority.ALWAYS);
|
||||
leftPane = new VBox(tabPane, new SummaryTablePane(controller));
|
||||
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
|
||||
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
|
||||
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
|
||||
splitPane.getItems().addAll(leftPane, centralStack, metaDataTable);
|
||||
splitPane.setDividerPositions(0.1, 1.0);
|
||||
/**
|
||||
* Gets the current controller, allows the user to select the data sources
|
||||
* for which images are to be displayed and opens the top component's
|
||||
* window.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the current
|
||||
* controller.
|
||||
*/
|
||||
@Messages({
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.headerText=Choose a data source to view.",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.contentText=Data source:",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.all=All",
|
||||
"ImageGalleryTopComponent.chooseDataSourceDialog.titleText=Image Gallery",})
|
||||
private void getCurrentControllerAndOpen() throws TskCoreException {
|
||||
ImageGalleryController currentController = ImageGalleryModule.getController();
|
||||
/*
|
||||
* Dispatch a task to run in the JavaFX thread. This task will swap the
|
||||
* new controller, if there is one, into this top component and its
|
||||
* child UI components. This task also queues another JavaFX thread task
|
||||
* to check for analyzed groups, which has the side effect of starting
|
||||
* the spinner(s) that take the place of a wait cursor. Finally, this
|
||||
* task starts a background thread to query the case database. This
|
||||
* background task may dispatch a JavaFX thread task to do a data source
|
||||
* selection dialog. Ultimately, there is a final task that either opens
|
||||
* the window in the AWT EDT or displays a "too many files" dialog in
|
||||
* the JFX thread.
|
||||
*/
|
||||
// RJCTODO: Verify the side effect remark above.
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (controllerLock) {
|
||||
if (notEqual(controller, currentController)) {
|
||||
controller = currentController;
|
||||
/*
|
||||
* Create or re-create the top component's child UI
|
||||
* components. This is currently done every time a new
|
||||
* controller is created (i.e., a new case is opened).
|
||||
* It could be done by resetting the controller in the
|
||||
* child UI components instead.
|
||||
*/
|
||||
// RJCTODO: Construction of these components can perhaps
|
||||
// be separated from opening the window again so that
|
||||
// a setController implementation could be called from
|
||||
// the case opened event handler in the ImageGalleryModule
|
||||
// object.
|
||||
fullUIStack = new StackPane();
|
||||
myScene = new Scene(fullUIStack);
|
||||
jfxPanel.setScene(myScene);
|
||||
groupPane = new GroupPane(controller);
|
||||
centralStack = new StackPane(groupPane);
|
||||
fullUIStack.getChildren().add(borderPane);
|
||||
splitPane = new SplitPane();
|
||||
borderPane.setCenter(splitPane);
|
||||
Toolbar toolbar = new Toolbar(controller);
|
||||
borderPane.setTop(toolbar);
|
||||
borderPane.setBottom(new StatusBar(controller));
|
||||
metaDataTable = new MetaDataPane(controller);
|
||||
groupTree = new GroupTree(controller);
|
||||
hashHitList = new HashHitGroupList(controller);
|
||||
TabPane tabPane = new TabPane(groupTree, hashHitList);
|
||||
tabPane.setPrefWidth(TabPane.USE_COMPUTED_SIZE);
|
||||
tabPane.setMinWidth(TabPane.USE_PREF_SIZE);
|
||||
VBox.setVgrow(tabPane, Priority.ALWAYS);
|
||||
leftPane = new VBox(tabPane, new SummaryTablePane(controller));
|
||||
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
|
||||
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
|
||||
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
|
||||
splitPane.getItems().addAll(leftPane, centralStack, metaDataTable);
|
||||
splitPane.setDividerPositions(0.1, 1.0);
|
||||
|
||||
controller.regroupDisabledProperty().addListener((Observable observable) -> checkForGroups());
|
||||
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> Platform.runLater(() -> checkForGroups()));
|
||||
/*
|
||||
* Set up for a call to checkForGroups to happen
|
||||
* whenever the controller's regrouping disabled
|
||||
* property or the group manager's analyzed groups
|
||||
* property changes.
|
||||
*/
|
||||
controller.regroupDisabledProperty().addListener((Observable unused) -> Platform.runLater(() -> checkForAnalyzedGroups()));
|
||||
controller.getGroupManager().getAnalyzedGroups().addListener((Observable unused) -> Platform.runLater(() -> checkForAnalyzedGroups()));
|
||||
|
||||
topComponentInitialized = true;
|
||||
/*
|
||||
* Dispatch a later task to call check for groups. Note
|
||||
* that this method displays one or more spinner(s) that
|
||||
* take the place of a wait cursor if there are no
|
||||
* analyzed groups yet, ingest is running, etc.
|
||||
*/
|
||||
// RJCTODO: Is there a race condition here, since this task could be
|
||||
// executed before the task to actually open the top component window?
|
||||
// It seems like this might be a sort of a hack and I am wondering
|
||||
// why this can't be done in openWithSelectedDataSources instead.
|
||||
Platform.runLater(() -> checkForAnalyzedGroups());
|
||||
}
|
||||
|
||||
// This will cause the UI to show the progress dialog
|
||||
Platform.runLater(() -> checkForGroups());
|
||||
/*
|
||||
* Kick off a background task to query the case database for
|
||||
* data sources. This task may queue another task for the
|
||||
* JavaFX thread to allow the user to select which data
|
||||
* sources for which to display images. Ultimately, a task
|
||||
* will be queued for the AWT EDT that will show the top
|
||||
* component window.
|
||||
*/
|
||||
new Thread(new Task<Void>() {
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
synchronized (controllerLock) {
|
||||
/*
|
||||
* If there is only one datasource or the
|
||||
* grouping criterion is already set to
|
||||
* something other than by path (the default),
|
||||
* proceed to open this top component.
|
||||
* Otherwise, do a dialog to allow the user to
|
||||
* select the data sources for which images are
|
||||
* to be displayed, then open the top component.
|
||||
*/
|
||||
List<DataSource> dataSources = controller.getSleuthKitCase().getDataSources();
|
||||
Map<DataSource, Boolean> dataSourcesWithTooManyFiles = new HashMap<>();
|
||||
// RJCTODO: At least some of this designation of "all data sources" with null seems uneccessary;
|
||||
// in any case, the use of nulls and zeros here is
|
||||
// very confusing and should be reworked.
|
||||
if (dataSources.size() <= 1
|
||||
|| controller.getGroupManager().getGroupBy() != DrawableAttribute.PATH) {
|
||||
dataSourcesWithTooManyFiles.put(null, controller.hasTooManyFiles(null));
|
||||
openWithSelectedDataSources(null, dataSourcesWithTooManyFiles);
|
||||
} else {
|
||||
dataSources.add(0, null);
|
||||
for (DataSource dataSource : dataSources) {
|
||||
dataSourcesWithTooManyFiles.put(dataSource, controller.hasTooManyFiles(dataSource));
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
List<Optional<DataSource>> dataSourceOptionals = dataSources.stream().map(Optional::ofNullable).collect(Collectors.toList());
|
||||
ChoiceDialog<Optional<DataSource>> datasourceDialog = new ChoiceDialog<>(null, dataSourceOptionals);
|
||||
datasourceDialog.setTitle(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_titleText());
|
||||
datasourceDialog.setHeaderText(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_headerText());
|
||||
datasourceDialog.setContentText(Bundle.ImageGalleryTopComponent_chooseDataSourceDialog_contentText());
|
||||
datasourceDialog.initModality(Modality.APPLICATION_MODAL);
|
||||
GuiUtils.setDialogIcons(datasourceDialog);
|
||||
@SuppressWarnings(value = "unchecked")
|
||||
ComboBox<Optional<DataSource>> comboBox = (ComboBox<Optional<DataSource>>) datasourceDialog.getDialogPane().lookup(".combo-box");
|
||||
comboBox.setCellFactory((ListView<Optional<DataSource>> unused) -> new DataSourceCell(dataSourcesWithTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
comboBox.setButtonCell(new DataSourceCell(dataSourcesWithTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
DataSource dataSource = datasourceDialog.showAndWait().orElse(Optional.empty()).orElse(null);
|
||||
openWithSelectedDataSources(dataSource, dataSourcesWithTooManyFiles);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,7 +425,6 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
* to indicate this method is effectively deprecated. A break point
|
||||
* placed here was never hit.
|
||||
*/
|
||||
|
||||
return modes.stream().filter(mode -> mode.getName().equals("timeline") || mode.getName().equals("ImageGallery"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@ -409,11 +446,10 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any fully analyzed groups available from the
|
||||
* GroupManager and remove blocking progress spinners if there are. If there
|
||||
* aren't, add a blocking progress spinner with appropriate message.
|
||||
*
|
||||
* This gets called when any group becomes analyzed and when started.
|
||||
* Checks if there are any fully analyzed groups available from the groups
|
||||
* manager and removes the blocking progress spinner if there are analyzed
|
||||
* groups; otherwise adds a blocking progress spinner with an appropriate
|
||||
* message.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@NbBundle.Messages({
|
||||
@ -427,57 +463,60 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
"ImageGalleryController.noGroupsDlg.msg6=There are no fully analyzed groups to display:"
|
||||
+ " the current Group By setting resulted in no groups, "
|
||||
+ "or no groups are fully analyzed but ingest is not running."})
|
||||
private void checkForGroups() {
|
||||
GroupManager groupManager = controller.getGroupManager();
|
||||
private void checkForAnalyzedGroups() {
|
||||
synchronized (controllerLock) {
|
||||
GroupManager groupManager = controller.getGroupManager();
|
||||
|
||||
// if there are groups to display, then display them
|
||||
// @@@ Need to check timing on this and make sure we have only groups for the selected DS. Seems like rebuild can cause groups to be created for a DS that is not later selected...
|
||||
if (isNotEmpty(groupManager.getAnalyzedGroups())) {
|
||||
clearNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
// display a message based on if ingest is running and/or listening
|
||||
if (IngestManager.getInstance().isIngestRunning()) {
|
||||
if (controller.isListeningEnabled()) {
|
||||
replaceNotification(centralStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg2(),
|
||||
new ProgressIndicator()));
|
||||
} else {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1()));
|
||||
// if there are groups to display, then display them
|
||||
// @@@ Need to check timing on this and make sure we have only groups for the selected DS. Seems like rebuild can cause groups to be created for a DS that is not later selected...
|
||||
// RJCTODO: Get Brian's TODO resolved.
|
||||
if (isNotEmpty(groupManager.getAnalyzedGroups())) {
|
||||
clearNotification();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// display a message about stuff still being in the queue
|
||||
if (controller.getDBTasksQueueSizeProperty().get() > 0) {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(),
|
||||
new ProgressIndicator()));
|
||||
return;
|
||||
}
|
||||
|
||||
// are there are files in the DB?
|
||||
try {
|
||||
if (controller.getDatabase().countAllFiles() <= 0) {
|
||||
// there are no files in db
|
||||
// display a message based on if ingest is running and/or listening
|
||||
if (IngestManager.getInstance().isIngestRunning()) {
|
||||
if (controller.isListeningEnabled()) {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg5()));
|
||||
replaceNotification(centralStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg2(),
|
||||
new ProgressIndicator()));
|
||||
} else {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg4()));
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error counting files in the database.", tskCoreException);
|
||||
}
|
||||
|
||||
if (false == groupManager.isRegrouping()) {
|
||||
replaceNotification(centralStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6()));
|
||||
// display a message about stuff still being in the queue
|
||||
if (controller.getDBTasksQueueSizeProperty().get() > 0) {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(),
|
||||
new ProgressIndicator()));
|
||||
return;
|
||||
}
|
||||
|
||||
// are there are files in the DB?
|
||||
try {
|
||||
if (controller.getDatabase().countAllFiles() <= 0) {
|
||||
// there are no files in db
|
||||
if (controller.isListeningEnabled()) {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg5()));
|
||||
} else {
|
||||
replaceNotification(fullUIStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg4()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error counting files in the database.", tskCoreException);
|
||||
}
|
||||
|
||||
if (false == groupManager.isRegrouping()) {
|
||||
replaceNotification(centralStack,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,17 +530,17 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the spinner(s).
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void clearNotification() {
|
||||
//remove the ingest spinner
|
||||
fullUIStack.getChildren().remove(infoOverlay);
|
||||
//remove the ingest spinner
|
||||
centralStack.getChildren().remove(infoOverlay);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Region with partialy opacity used to block out parts of the UI behind a
|
||||
* A partially opaque region used to block out parts of the UI behind a
|
||||
* pseudo dialog.
|
||||
*/
|
||||
static final private class TranslucentRegion extends Region {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -71,7 +71,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
+ "Do you want to update and listen for further ingest results?\n"
|
||||
+ "Choosing 'yes' will update the database and enable listening to future ingests.",
|
||||
"OpenAction.notAnalyzedDlg.msg=No image/video files available to display yet.\n"
|
||||
+ "Please run FileType and EXIF ingest modules.",
|
||||
+ "Please run FileType and EXIF ingest modules.",
|
||||
"OpenAction.stale.confDlg.title=Image Gallery"})
|
||||
public final class OpenAction extends CallableSystemAction {
|
||||
|
||||
@ -148,19 +148,20 @@ public final class OpenAction extends CallableSystemAction {
|
||||
try {
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return;
|
||||
}
|
||||
ImageGalleryController controller;
|
||||
try {
|
||||
controller = ImageGalleryModule.getController();
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting ImageGalleryController for current case.", ex);
|
||||
logger.log(Level.SEVERE, "No current case", ex);
|
||||
return;
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
ImageGalleryController controller;
|
||||
try {
|
||||
controller = ImageGalleryModule.getController();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get ImageGalleryController", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE
|
||||
&& ImageGalleryPreferences.isMultiUserCaseInfoDialogDisabled() == false) {
|
||||
&& ImageGalleryPreferences.isMultiUserCaseInfoDialogDisabled() == false) {
|
||||
Alert dialog = new Alert(Alert.AlertType.INFORMATION);
|
||||
dialog.initModality(Modality.APPLICATION_MODAL);
|
||||
dialog.setResizable(true);
|
||||
@ -186,13 +187,12 @@ public final class OpenAction extends CallableSystemAction {
|
||||
}
|
||||
|
||||
private void checkDBStale(ImageGalleryController controller) {
|
||||
|
||||
ListenableFuture<Map<Long, DrawableDB.DrawableDbBuildStatusEnum>> dataSourceStatusMapFuture = TaskUtils.getExecutorForClass(OpenAction.class)
|
||||
|
||||
ListenableFuture<Map<Long, DrawableDB.DrawableDbBuildStatusEnum>> dataSourceStatusMapFuture = TaskUtils.getExecutorForClass(OpenAction.class)
|
||||
.submit(controller::getAllDataSourcesDrawableDBStatus);
|
||||
|
||||
|
||||
addFXCallback(dataSourceStatusMapFuture,
|
||||
dataSourceStatusMap -> {
|
||||
|
||||
dataSourceStatusMap -> {
|
||||
int numStale = 0;
|
||||
int numNoAnalysis = 0;
|
||||
// NOTE: There is some overlapping code here with Controller.getStaleDataSourceIds(). We could possibly just use
|
||||
@ -239,7 +239,8 @@ public final class OpenAction extends CallableSystemAction {
|
||||
// NOTE: There could be no data....
|
||||
} else if (answer == ButtonType.YES) {
|
||||
if (controller.getAutopsyCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE) {
|
||||
/* For a single-user case, we favor user
|
||||
/*
|
||||
* For a single-user case, we favor user
|
||||
* experience, and rebuild the database as soon
|
||||
* as Image Gallery is enabled for the case.
|
||||
*
|
||||
@ -285,10 +286,9 @@ public final class OpenAction extends CallableSystemAction {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
ImageGalleryTopComponent.openTopComponent();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex);//NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS}
|
||||
logger.log(Level.SEVERE, "Failed to open Image Gallery top component", ex); //NON-NLS}
|
||||
// RJCTODO: Give the user some feedback here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ public abstract class DrawableFile {
|
||||
}
|
||||
|
||||
public DataSource getDataSource() throws TskCoreException, TskDataException {
|
||||
return getSleuthkitCase().getDataSource(file.getDataSource().getId());
|
||||
return getSleuthkitCase().getDataSource(file.getDataSourceObjectId());
|
||||
}
|
||||
|
||||
private Pair<DrawableAttribute<?>, Collection<?>> makeAttributeValuePair(DrawableAttribute<?> attribute) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-18 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -31,9 +31,7 @@ import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyLongProperty;
|
||||
import javafx.beans.property.ReadOnlyLongWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||
@ -120,10 +118,8 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
||||
.map(ImageGalleryModule.getController().getHashSetManager()::isInAnyHashSet)
|
||||
.filter(Boolean::booleanValue)
|
||||
.count());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Could not access case during getFilesWithHashSetHitsCount()"); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to get image gallery controller", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
return hashSetHitsCount.get();
|
||||
@ -138,12 +134,10 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
||||
if (uncatCount.get() < 0) {
|
||||
try {
|
||||
uncatCount.set(ImageGalleryModule.getController().getDatabase().getUncategorizedCount(fileIDs));
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Could not access case during getFilesWithHashSetHitsCount()"); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get image gallery controller", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
return uncatCount.get();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-18 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-18 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -20,9 +20,7 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import static com.google.common.collect.Lists.transform;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -35,7 +33,6 @@ import java.util.Map;
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.IntStream;
|
||||
import javafx.animation.Interpolator;
|
||||
@ -135,18 +132,15 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
|
||||
import static org.sleuthkit.autopsy.imagegallery.gui.GuiUtils.createAutoAssigningMenuItem;
|
||||
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
|
||||
import static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* A GroupPane displays the contents of a DrawableGroup. It supports both
|
||||
* GridView and SlideShowView modes by swapping out its internal components.
|
||||
*
|
||||
*
|
||||
* TODO: Extract the The GridView instance to a separate class analogous to the
|
||||
* SlideShow.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Updated by build script
|
||||
#Tue, 13 Nov 2018 17:30:09 -0500
|
||||
#Thu, 29 Nov 2018 12:23:03 -0500
|
||||
LBL_splash_window_title=Starting Autopsy
|
||||
SPLASH_HEIGHT=314
|
||||
SPLASH_WIDTH=538
|
||||
|
@ -1,4 +1,4 @@
|
||||
#Updated by build script
|
||||
#Tue, 13 Nov 2018 17:30:09 -0500
|
||||
#Thu, 29 Nov 2018 12:23:03 -0500
|
||||
CTL_MainWindow_Title=Autopsy 4.9.1
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.9.1
|
||||
|
@ -333,7 +333,7 @@ class TskDbDiff(object):
|
||||
for line in postgreSQL_db:
|
||||
line = line.strip('\r\n ')
|
||||
# Deal with pg_dump result file
|
||||
if line.startswith('--') or line.lower().startswith('alter') or "pg_catalog" in line or not line: # It's comment or alter statement or catalog entry or empty line
|
||||
if line.startswith('--') or line.lower().startswith('alter') or "pg_catalog" in line or "idle_in_transaction_session_timeout" in line or not line: # It's comment or alter statement or catalog entry or set idle entry or empty line
|
||||
continue
|
||||
elif not line.endswith(';'): # Statement not finished
|
||||
dump_line += line
|
||||
|
Loading…
x
Reference in New Issue
Block a user