mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-20 03:24:55 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 7142-ShowPastCasesDomainDiscovery
This commit is contained in:
commit
86d1dd59d7
@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
|
|||||||
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel;
|
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The final panel of the add image wizard. It displays a progress bar and
|
* The final panel of the add image wizard. It displays a progress bar and
|
||||||
@ -289,7 +290,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param errorString the error string to be displayed
|
* @param errorString the error string to be displayed
|
||||||
* @param critical true if this is a critical error
|
* @param critical true if this is a critical error
|
||||||
*/
|
*/
|
||||||
void addErrors(String errorString, boolean critical) {
|
void addErrors(String errorString, boolean critical) {
|
||||||
getComponent().showErrors(errorString, critical);
|
getComponent().showErrors(errorString, critical);
|
||||||
@ -302,7 +303,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
|||||||
private void startIngest() {
|
private void startIngest() {
|
||||||
if (!newContents.isEmpty() && readyToIngest && !ingested) {
|
if (!newContents.isEmpty() && readyToIngest && !ingested) {
|
||||||
ingested = true;
|
ingested = true;
|
||||||
if (dsProcessor != null && ! dsProcessor.supportsIngestStream()) {
|
if (dsProcessor != null && !dsProcessor.supportsIngestStream()) {
|
||||||
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings);
|
IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings);
|
||||||
}
|
}
|
||||||
setStateFinished();
|
setStateFinished();
|
||||||
@ -323,8 +324,13 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
|||||||
/**
|
/**
|
||||||
* Starts the Data source processing by kicking off the selected
|
* Starts the Data source processing by kicking off the selected
|
||||||
* DataSourceProcessor
|
* DataSourceProcessor
|
||||||
|
*
|
||||||
|
* @param dsp The data source processor providing configuration for how to
|
||||||
|
* process the specific data source type.
|
||||||
|
* @param selectedHost The host to which this data source belongs or null
|
||||||
|
* for a default host.
|
||||||
*/
|
*/
|
||||||
void startDataSourceProcessing(DataSourceProcessor dsp) {
|
void startDataSourceProcessing(DataSourceProcessor dsp, Host selectedHost) {
|
||||||
if (dsProcessor == null) { //this can only be run once
|
if (dsProcessor == null) { //this can only be run once
|
||||||
final UUID dataSourceId = UUID.randomUUID();
|
final UUID dataSourceId = UUID.randomUUID();
|
||||||
newContents.clear();
|
newContents.clear();
|
||||||
@ -349,7 +355,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
|||||||
try {
|
try {
|
||||||
Case.getCurrentCaseThrows().notifyAddingDataSource(dataSourceId);
|
Case.getCurrentCaseThrows().notifyAddingDataSource(dataSourceId);
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
|
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
|
||||||
@ -365,9 +371,9 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
|||||||
if (dsProcessor.supportsIngestStream()) {
|
if (dsProcessor.supportsIngestStream()) {
|
||||||
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
|
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
|
||||||
readyToIngest = false;
|
readyToIngest = false;
|
||||||
dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
|
dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
|
||||||
} else {
|
} else {
|
||||||
dsProcessor.run(getDSPProgressMonitorImpl(), cbObj);
|
dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript
|
|||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel {
|
class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel {
|
||||||
|
|
||||||
@Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules")
|
@Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest")
|
||||||
private final IngestJobSettingsPanel ingestJobSettingsPanel;
|
private final IngestJobSettingsPanel ingestJobSettingsPanel;
|
||||||
/**
|
/**
|
||||||
* The visual component that displays this panel. If you need to access the
|
* The visual component that displays this panel. If you need to access the
|
||||||
|
@ -29,6 +29,7 @@ import org.openide.util.NbBundle;
|
|||||||
import org.sleuthkit.autopsy.ingest.IngestProfiles;
|
import org.sleuthkit.autopsy.ingest.IngestProfiles;
|
||||||
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.IngestProfileSelectionWizardPanel;
|
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.IngestProfileSelectionWizardPanel;
|
||||||
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel;
|
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The iterator class for the "Add Image" wizard panel. This class is used to
|
* The iterator class for the "Add Image" wizard panel. This class is used to
|
||||||
@ -41,8 +42,10 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
|
|||||||
private final AddImageAction action;
|
private final AddImageAction action;
|
||||||
private int progressPanelIndex;
|
private int progressPanelIndex;
|
||||||
private int dsPanelIndex;
|
private int dsPanelIndex;
|
||||||
|
private int hostPanelIndex;
|
||||||
private int ingestPanelIndex;
|
private int ingestPanelIndex;
|
||||||
private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS
|
private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS
|
||||||
|
private AddImageWizardSelectHostPanel hostPanel = null;
|
||||||
|
|
||||||
AddImageWizardIterator(AddImageAction action) {
|
AddImageWizardIterator(AddImageAction action) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
@ -55,10 +58,12 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
|
|||||||
private List<ShortcutWizardDescriptorPanel> getPanels() {
|
private List<ShortcutWizardDescriptorPanel> getPanels() {
|
||||||
if (panels == null) {
|
if (panels == null) {
|
||||||
panels = new ArrayList<>();
|
panels = new ArrayList<>();
|
||||||
|
hostPanel = new AddImageWizardSelectHostPanel();
|
||||||
|
panels.add(hostPanel);
|
||||||
|
hostPanelIndex = panels.indexOf(hostPanel);
|
||||||
AddImageWizardSelectDspPanel dspSelection = new AddImageWizardSelectDspPanel();
|
AddImageWizardSelectDspPanel dspSelection = new AddImageWizardSelectDspPanel();
|
||||||
panels.add(dspSelection);
|
panels.add(dspSelection);
|
||||||
AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(action);
|
AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(action);
|
||||||
|
|
||||||
AddImageWizardDataSourceSettingsPanel dsPanel = new AddImageWizardDataSourceSettingsPanel();
|
AddImageWizardDataSourceSettingsPanel dsPanel = new AddImageWizardDataSourceSettingsPanel();
|
||||||
AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(progressPanel);
|
AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(progressPanel);
|
||||||
panels.add(dsPanel);
|
panels.add(dsPanel);
|
||||||
@ -164,7 +169,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
|
|||||||
@Override
|
@Override
|
||||||
// disable the previous button on all panels except the data source panel
|
// disable the previous button on all panels except the data source panel
|
||||||
public boolean hasPrevious() {
|
public boolean hasPrevious() {
|
||||||
return (index == dsPanelIndex); //Users should be able to back up to select a different DSP
|
return (index <= dsPanelIndex && index > 0); //Users should be able to back up to select a different DSP
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,8 +185,10 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator<WizardDescript
|
|||||||
// so it gets going in the background while the user is still picking the Ingest modules
|
// so it gets going in the background while the user is still picking the Ingest modules
|
||||||
// This will occur when the next button is clicked on the panel where you have chosen your data to process
|
// This will occur when the next button is clicked on the panel where you have chosen your data to process
|
||||||
if (index == ingestPanelIndex) {
|
if (index == ingestPanelIndex) {
|
||||||
((AddImageWizardAddingProgressPanel) panels.get(progressPanelIndex)).
|
AddImageWizardAddingProgressPanel addingProgressPanel = (AddImageWizardAddingProgressPanel) panels.get(progressPanelIndex);
|
||||||
startDataSourceProcessing(((AddImageWizardDataSourceSettingsPanel) panels.get(dsPanelIndex)).getComponent().getCurrentDSProcessor());
|
AddImageWizardDataSourceSettingsVisual dspSettingsPanel = ((AddImageWizardDataSourceSettingsPanel) panels.get(dsPanelIndex)).getComponent();
|
||||||
|
Host host = (hostPanel == null) ? null : hostPanel.getSelectedHost();
|
||||||
|
addingProgressPanel.startDataSourceProcessing(dspSettingsPanel.getCurrentDSProcessor(), host);
|
||||||
}
|
}
|
||||||
boolean panelEnablesSkipping = current().panelEnablesSkipping();
|
boolean panelEnablesSkipping = current().panelEnablesSkipping();
|
||||||
boolean skipNextPanel = current().skipNextPanel();
|
boolean skipNextPanel = current().skipNextPanel();
|
||||||
|
@ -39,7 +39,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript
|
|||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener {
|
final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener {
|
||||||
|
|
||||||
@NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Type of Data Source To Add")
|
@NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Data Source Type")
|
||||||
private AddImageWizardSelectDspVisual component;
|
private AddImageWizardSelectDspVisual component;
|
||||||
private static final String LAST_DSP_PROPERTIES_FILE = "LastDspUsed"; //NON-NLS
|
private static final String LAST_DSP_PROPERTIES_FILE = "LastDspUsed"; //NON-NLS
|
||||||
private static final String LAST_DSP_USED_KEY = "Last_Dsp_Used"; //NON-NLS
|
private static final String LAST_DSP_USED_KEY = "Last_Dsp_Used"; //NON-NLS
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.casemodule;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import org.openide.WizardDescriptor;
|
||||||
|
import org.openide.util.ChangeSupport;
|
||||||
|
import org.openide.util.HelpCtx;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a wizard panel which contains a panel allowing the selection of a host
|
||||||
|
* for a data source.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
|
@Messages("AddImageWizardSelectHostPanel_title=Select Host To Add The Data Source To")
|
||||||
|
final class AddImageWizardSelectHostPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener {
|
||||||
|
|
||||||
|
private final AddImageWizardSelectHostVisual component = new AddImageWizardSelectHostVisual();
|
||||||
|
private final ChangeSupport changeSupport = new ChangeSupport(this);
|
||||||
|
|
||||||
|
AddImageWizardSelectHostPanel() {
|
||||||
|
component.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getComponent() {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HelpCtx getHelp() {
|
||||||
|
return HelpCtx.DEFAULT_HELP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readSettings(WizardDescriptor data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns or generates the selected host. If user specifies 'generate
|
||||||
|
* new...', then null will be returned.
|
||||||
|
*
|
||||||
|
* @return The selected host or null if to be auto generated.
|
||||||
|
*/
|
||||||
|
Host getSelectedHost() {
|
||||||
|
return component.getSelectedHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeSettings(WizardDescriptor data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return component.hasValidData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChangeListener(ChangeListener cl) {
|
||||||
|
changeSupport.addChangeListener(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChangeListener(ChangeListener cl) {
|
||||||
|
changeSupport.removeChangeListener(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
changeSupport.fireChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
|
<NonVisualComponents>
|
||||||
|
<Component class="javax.swing.ButtonGroup" name="radioButtonGroup">
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</NonVisualComponents>
|
||||||
|
<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 max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="validationMessage" max="32767" attributes="0"/>
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="generateNewRadio" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="useExistingHostRadio" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostDescription" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
|
||||||
|
<Component id="jScrollPane1" min="-2" pref="270" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<Component id="specifyNewHostRadio" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="specifyNewHostTextField" min="-2" pref="270" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="0" pref="13" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</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"/>
|
||||||
|
<Component id="hostDescription" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="generateNewRadio" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="specifyNewHostRadio" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="specifyNewHostTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="useExistingHostRadio" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="jScrollPane1" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||||
|
<Component id="validationMessage" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace pref="18" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JRadioButton" name="generateNewRadio">
|
||||||
|
<Properties>
|
||||||
|
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||||
|
<ComponentRef name="radioButtonGroup"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="selected" type="boolean" value="true"/>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.generateNewRadio.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="generateNewRadioActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JRadioButton" name="specifyNewHostRadio">
|
||||||
|
<Properties>
|
||||||
|
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||||
|
<ComponentRef name="radioButtonGroup"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.specifyNewHostRadio.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="specifyNewHostRadioActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="specifyNewHostTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.specifyNewHostTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JRadioButton" name="useExistingHostRadio">
|
||||||
|
<Properties>
|
||||||
|
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||||
|
<ComponentRef name="radioButtonGroup"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.useExistingHostRadio.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="useExistingHostRadioActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JList" name="existingHostList">
|
||||||
|
<Properties>
|
||||||
|
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||||
|
<StringArray count="0"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="selectionMode" type="int" value="0"/>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<HostListItem>"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="hostDescription">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.hostDescription.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="validationMessage">
|
||||||
|
<Properties>
|
||||||
|
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
|
<Connection code="java.awt.Color.RED" type="code"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="AddImageWizardSelectHostVisual.validationMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Form>
|
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.casemodule;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.beans.PropertyChangeSupport;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.hosts.HostNameValidator;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel to be displayed as a part of the add datasource wizard. Provides the
|
||||||
|
* ability to select current host.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"AddImageWizardSelectHostVisual_title=Select Host"
|
||||||
|
})
|
||||||
|
class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combo box item for a host (or null for default).
|
||||||
|
*/
|
||||||
|
private static class HostListItem {
|
||||||
|
|
||||||
|
private final Host host;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param host The host.
|
||||||
|
*/
|
||||||
|
HostListItem(Host host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The host.
|
||||||
|
*/
|
||||||
|
Host getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (host == null || host.getName() == null) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return host.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 7;
|
||||||
|
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final HostListItem other = (HostListItem) obj;
|
||||||
|
if (!Objects.equals(
|
||||||
|
this.host == null ? 0 : this.host.getId(),
|
||||||
|
other.host == null ? 0 : other.host.getId())) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AddImageWizardSelectHostVisual.class.getName());
|
||||||
|
|
||||||
|
private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
|
||||||
|
private Set<String> sanitizedHostSet = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new form SelectHostPanel
|
||||||
|
*/
|
||||||
|
AddImageWizardSelectHostVisual() {
|
||||||
|
initComponents();
|
||||||
|
|
||||||
|
specifyNewHostTextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
existingHostList.addListSelectionListener((evt) -> refresh());
|
||||||
|
|
||||||
|
loadHostData();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listener for validation change events.
|
||||||
|
*
|
||||||
|
* @param pcl The property change listener.
|
||||||
|
*/
|
||||||
|
void addListener(PropertyChangeListener pcl) {
|
||||||
|
changeSupport.addPropertyChangeListener(pcl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove listener from validation change events.
|
||||||
|
*
|
||||||
|
* @param pcl The property change listener.
|
||||||
|
*/
|
||||||
|
void removeListener(PropertyChangeListener pcl) {
|
||||||
|
changeSupport.removePropertyChangeListener(pcl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The currently selected host or null if no selection. This will
|
||||||
|
* generate a new host if 'Specify New Host Name'
|
||||||
|
*/
|
||||||
|
Host getSelectedHost() {
|
||||||
|
if (specifyNewHostRadio.isSelected() && StringUtils.isNotEmpty(specifyNewHostTextField.getText())) {
|
||||||
|
String newHostName = specifyNewHostTextField.getText();
|
||||||
|
try {
|
||||||
|
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, String.format("Unable to create host '%s'.", newHostName), ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (useExistingHostRadio.isSelected()
|
||||||
|
&& existingHostList.getSelectedValue() != null
|
||||||
|
&& existingHostList.getSelectedValue().getHost() != null) {
|
||||||
|
|
||||||
|
return existingHostList.getSelectedValue().getHost();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads hosts from database and displays in combo box.
|
||||||
|
*/
|
||||||
|
private void loadHostData() {
|
||||||
|
try {
|
||||||
|
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
|
||||||
|
sanitizedHostSet = HostNameValidator.getSanitizedHostNames(hosts);
|
||||||
|
|
||||||
|
Vector<HostListItem> hostListItems = hosts.stream()
|
||||||
|
.filter(h -> h != null)
|
||||||
|
.sorted((a, b) -> getNameOrEmpty(a).compareToIgnoreCase(getNameOrEmpty(b)))
|
||||||
|
.map((h) -> new HostListItem(h))
|
||||||
|
.collect(Collectors.toCollection(Vector::new));
|
||||||
|
|
||||||
|
existingHostList.setListData(hostListItems);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to display host items with no current case.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the host or an empty string if the host or host name
|
||||||
|
* is null.
|
||||||
|
*
|
||||||
|
* @param host The host.
|
||||||
|
* @return The host name or empty string.
|
||||||
|
*/
|
||||||
|
private String getNameOrEmpty(Host host) {
|
||||||
|
return host == null || host.getName() == null ? "" : host.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh() {
|
||||||
|
specifyNewHostTextField.setEnabled(specifyNewHostRadio.isSelected());
|
||||||
|
existingHostList.setEnabled(useExistingHostRadio.isSelected());
|
||||||
|
|
||||||
|
String prevValidationMessage = validationMessage.getText();
|
||||||
|
String newValidationMessage = getValidationMessage();
|
||||||
|
validationMessage.setText(newValidationMessage);
|
||||||
|
// if validation message changed (empty to non-empty or vice-versa) fire validation update
|
||||||
|
if (StringUtils.isBlank(prevValidationMessage) != StringUtils.isBlank(newValidationMessage)) {
|
||||||
|
changeSupport.firePropertyChange("validation", prevValidationMessage, newValidationMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Messages({
|
||||||
|
"AddImageWizardSelectHostVisual_getValidationMessage_noHostSelected=Please select an existing host.",})
|
||||||
|
private String getValidationMessage() {
|
||||||
|
if (specifyNewHostRadio.isSelected()) {
|
||||||
|
// if problematic new name for host
|
||||||
|
return HostNameValidator.getValidationMessage(specifyNewHostTextField.getText(), null, sanitizedHostSet);
|
||||||
|
|
||||||
|
// or use existing host and no host is selected
|
||||||
|
} else if (useExistingHostRadio.isSelected()
|
||||||
|
&& (existingHostList.getSelectedValue() == null
|
||||||
|
|| existingHostList.getSelectedValue().getHost() == null)) {
|
||||||
|
return Bundle.AddImageWizardSelectHostVisual_getValidationMessage_noHostSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return Bundle.AddImageWizardSelectHostVisual_title();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasValidData() {
|
||||||
|
return StringUtils.isBlank(validationMessage.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() {
|
||||||
|
|
||||||
|
javax.swing.ButtonGroup radioButtonGroup = new javax.swing.ButtonGroup();
|
||||||
|
generateNewRadio = new javax.swing.JRadioButton();
|
||||||
|
specifyNewHostRadio = new javax.swing.JRadioButton();
|
||||||
|
specifyNewHostTextField = new javax.swing.JTextField();
|
||||||
|
useExistingHostRadio = new javax.swing.JRadioButton();
|
||||||
|
javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
|
||||||
|
existingHostList = new javax.swing.JList<>();
|
||||||
|
hostDescription = new javax.swing.JLabel();
|
||||||
|
validationMessage = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
radioButtonGroup.add(generateNewRadio);
|
||||||
|
generateNewRadio.setSelected(true);
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(generateNewRadio, org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.generateNewRadio.text")); // NOI18N
|
||||||
|
generateNewRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
generateNewRadioActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
radioButtonGroup.add(specifyNewHostRadio);
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(specifyNewHostRadio, org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.specifyNewHostRadio.text")); // NOI18N
|
||||||
|
specifyNewHostRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
specifyNewHostRadioActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
specifyNewHostTextField.setText(org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.specifyNewHostTextField.text")); // NOI18N
|
||||||
|
|
||||||
|
radioButtonGroup.add(useExistingHostRadio);
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(useExistingHostRadio, org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.useExistingHostRadio.text")); // NOI18N
|
||||||
|
useExistingHostRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
useExistingHostRadioActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
existingHostList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
jScrollPane1.setViewportView(existingHostList);
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(hostDescription, org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.hostDescription.text")); // NOI18N
|
||||||
|
|
||||||
|
validationMessage.setForeground(java.awt.Color.RED);
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(validationMessage, org.openide.util.NbBundle.getMessage(AddImageWizardSelectHostVisual.class, "AddImageWizardSelectHostVisual.validationMessage.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(validationMessage, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(generateNewRadio)
|
||||||
|
.addComponent(useExistingHostRadio)
|
||||||
|
.addComponent(hostDescription)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGap(21, 21, 21)
|
||||||
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(specifyNewHostRadio)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(specifyNewHostTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
|
.addGap(0, 13, Short.MAX_VALUE)))
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addComponent(hostDescription)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(generateNewRadio)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(specifyNewHostRadio)
|
||||||
|
.addComponent(specifyNewHostTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(useExistingHostRadio)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addGap(10, 10, 10)
|
||||||
|
.addComponent(validationMessage)
|
||||||
|
.addContainerGap(18, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
private void generateNewRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_generateNewRadioActionPerformed
|
||||||
|
refresh();
|
||||||
|
}//GEN-LAST:event_generateNewRadioActionPerformed
|
||||||
|
|
||||||
|
private void specifyNewHostRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specifyNewHostRadioActionPerformed
|
||||||
|
refresh();
|
||||||
|
}//GEN-LAST:event_specifyNewHostRadioActionPerformed
|
||||||
|
|
||||||
|
private void useExistingHostRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useExistingHostRadioActionPerformed
|
||||||
|
refresh();
|
||||||
|
}//GEN-LAST:event_useExistingHostRadioActionPerformed
|
||||||
|
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JList<HostListItem> existingHostList;
|
||||||
|
private javax.swing.JRadioButton generateNewRadio;
|
||||||
|
private javax.swing.JLabel hostDescription;
|
||||||
|
private javax.swing.JRadioButton specifyNewHostRadio;
|
||||||
|
private javax.swing.JTextField specifyNewHostTextField;
|
||||||
|
private javax.swing.JRadioButton useExistingHostRadio;
|
||||||
|
private javax.swing.JLabel validationMessage;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
}
|
@ -59,7 +59,7 @@ AddImageWizardChooseDataSourcePanel.moveFocusNext=Next >
|
|||||||
AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source
|
AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source
|
||||||
AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added.
|
AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added.
|
||||||
AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in adding Data Source.
|
AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in adding Data Source.
|
||||||
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules
|
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest
|
||||||
AddImageWizardIterator.stepXofN=Step {0} of {1}
|
AddImageWizardIterator.stepXofN=Step {0} of {1}
|
||||||
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
|
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
|
||||||
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\!
|
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\!
|
||||||
@ -258,3 +258,9 @@ SolrNotConfiguredDialog.okButton.text=OK
|
|||||||
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
||||||
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
||||||
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
||||||
|
AddImageWizardSelectHostVisual.hostDescription.text=Hosts are used to organize data sources and other data.
|
||||||
|
AddImageWizardSelectHostVisual.useExistingHostRadio.text=Use existing host
|
||||||
|
AddImageWizardSelectHostVisual.specifyNewHostTextField.text=
|
||||||
|
AddImageWizardSelectHostVisual.specifyNewHostRadio.text=Specify new host name
|
||||||
|
AddImageWizardSelectHostVisual.generateNewRadio.text=Generate new host name based on data source name
|
||||||
|
AddImageWizardSelectHostVisual.validationMessage.text=\
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules
|
AddImageWizardIngestConfigPanel.name.text=Configure Ingest
|
||||||
AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode
|
AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode
|
||||||
|
AddImageWizardSelectHostPanel_title=Select Host To Add The Data Source To
|
||||||
|
AddImageWizardSelectHostVisual_getValidationMessage_noHostSelected=Please select an existing host.
|
||||||
|
AddImageWizardSelectHostVisual_title=Select Host
|
||||||
# {0} - exception message
|
# {0} - exception message
|
||||||
Case.closeException.couldNotCloseCase=Error closing case: {0}
|
Case.closeException.couldNotCloseCase=Error closing case: {0}
|
||||||
Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources
|
Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources
|
||||||
@ -242,7 +245,7 @@ AddImageWizardChooseDataSourcePanel.moveFocusNext=Next >
|
|||||||
AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source
|
AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source
|
||||||
AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added.
|
AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added.
|
||||||
AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in adding Data Source.
|
AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in adding Data Source.
|
||||||
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules
|
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest
|
||||||
AddImageWizardIterator.stepXofN=Step {0} of {1}
|
AddImageWizardIterator.stepXofN=Step {0} of {1}
|
||||||
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
|
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
|
||||||
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\!
|
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\!
|
||||||
@ -341,7 +344,7 @@ RecentCases.exception.caseIdxOutOfRange.msg=Recent case index {0} is out of rang
|
|||||||
RecentCases.getName.text=Clear Recent Cases
|
RecentCases.getName.text=Clear Recent Cases
|
||||||
# {0} - case name
|
# {0} - case name
|
||||||
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
|
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
|
||||||
SelectDataSourceProcessorPanel.name.text=Select Type of Data Source To Add
|
SelectDataSourceProcessorPanel.name.text=Select Data Source Type
|
||||||
StartupWindow.title.text=Welcome
|
StartupWindow.title.text=Welcome
|
||||||
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
|
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
|
||||||
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
|
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
|
||||||
@ -476,3 +479,9 @@ SolrNotConfiguredDialog.okButton.text=OK
|
|||||||
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
SolrNotConfiguredDialog.title=Solr 8 Server Not Configured
|
||||||
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User.
|
||||||
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
SolrNotConfiguredDialog.messageLabel.text=<html>Multi-User cases are enabled but Solr 8 server has not been configured.<br>\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n</html>
|
||||||
|
AddImageWizardSelectHostVisual.hostDescription.text=Hosts are used to organize data sources and other data.
|
||||||
|
AddImageWizardSelectHostVisual.useExistingHostRadio.text=Use existing host
|
||||||
|
AddImageWizardSelectHostVisual.specifyNewHostTextField.text=
|
||||||
|
AddImageWizardSelectHostVisual.specifyNewHostRadio.text=Specify new host name
|
||||||
|
AddImageWizardSelectHostVisual.generateNewRadio.text=Generate new host name based on data source name
|
||||||
|
AddImageWizardSelectHostVisual.validationMessage.text=\
|
||||||
|
@ -81,6 +81,8 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
|||||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
||||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
|
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
|
||||||
@ -104,6 +106,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|||||||
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Version;
|
import org.sleuthkit.autopsy.coreutils.Version;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction;
|
||||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||||
import org.sleuthkit.autopsy.events.AutopsyEventException;
|
import org.sleuthkit.autopsy.events.AutopsyEventException;
|
||||||
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
|
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
|
||||||
@ -128,6 +131,10 @@ import org.sleuthkit.datamodel.ContentTag;
|
|||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.FileSystem;
|
import org.sleuthkit.datamodel.FileSystem;
|
||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountManager;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
|
||||||
import org.sleuthkit.datamodel.Report;
|
import org.sleuthkit.datamodel.Report;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TimelineManager;
|
import org.sleuthkit.datamodel.TimelineManager;
|
||||||
@ -409,7 +416,17 @@ public class Case {
|
|||||||
* An item in the central repository has had its comment modified. The
|
* An item in the central repository has had its comment modified. The
|
||||||
* old value is null, the new value is string for current comment.
|
* old value is null, the new value is string for current comment.
|
||||||
*/
|
*/
|
||||||
CR_COMMENT_CHANGED;
|
CR_COMMENT_CHANGED,
|
||||||
|
/**
|
||||||
|
* OSAccount associated with the current case added. Call getOsAccount
|
||||||
|
* to get the added account;
|
||||||
|
*/
|
||||||
|
OS_ACCOUNT_ADDED,
|
||||||
|
/**
|
||||||
|
* OSAccount associated with the current case has changed.
|
||||||
|
* Call getOsAccount to get the changed account;
|
||||||
|
*/
|
||||||
|
OS_ACCOUNT_CHANGED;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -443,6 +460,20 @@ public class Case {
|
|||||||
event.getArtifacts(artifactType)));
|
event.getArtifacts(artifactType)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void publishOsAccountAddedEvent(OsAccountsCreationEvent event) {
|
||||||
|
for(OsAccount account: event.getOsAcounts()) {
|
||||||
|
eventPublisher.publish(new OsAccountAddedEvent(account));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void publishOsAccountChangedEvent(OsAccountsUpdateEvent event) {
|
||||||
|
for(OsAccount account: event.getOsAcounts()) {
|
||||||
|
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1111,6 +1142,7 @@ public class Case {
|
|||||||
* Enable the case-specific actions.
|
* Enable the case-specific actions.
|
||||||
*/
|
*/
|
||||||
CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
|
CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
|
||||||
|
CallableSystemAction.get(OpenHostsAction.class).setEnabled(true);
|
||||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
|
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
|
||||||
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
|
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
|
||||||
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
|
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
|
||||||
@ -1166,6 +1198,7 @@ public class Case {
|
|||||||
* Disable the case-specific menu items.
|
* Disable the case-specific menu items.
|
||||||
*/
|
*/
|
||||||
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
||||||
|
CallableSystemAction.get(OpenHostsAction.class).setEnabled(false);
|
||||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
|
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
|
||||||
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
|
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
|
||||||
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
|
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
|
||||||
@ -1640,6 +1673,14 @@ public class Case {
|
|||||||
eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
|
eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void notifyOsAccountAdded(OsAccount account) {
|
||||||
|
eventPublisher.publish(new OsAccountAddedEvent(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyOsAccountChanged(OsAccount account) {
|
||||||
|
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a report to the case.
|
* Adds a report to the case.
|
||||||
*
|
*
|
||||||
|
@ -269,14 +269,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
|||||||
readConfigSettings();
|
readConfigSettings();
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
|
||||||
// HOSTTODO - remove once passing in a host
|
|
||||||
try {
|
|
||||||
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("ImageDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
this.host = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the data source before creating the ingest stream
|
// Set up the data source before creating the ingest stream
|
||||||
try {
|
try {
|
||||||
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
|
||||||
|
@ -171,13 +171,6 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
// HOSTTODO - set to value from config panel
|
|
||||||
try {
|
|
||||||
this.host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalDiskDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
this.host = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Image image;
|
Image image;
|
||||||
try {
|
try {
|
||||||
|
@ -177,14 +177,6 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
|||||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
if (!setDataSourceOptionsCalled) {
|
if (!setDataSourceOptionsCalled) {
|
||||||
|
|
||||||
// HOSTTODO - use passed in value
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LocalFilesDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
localFilePaths = configPanel.getContentPaths();
|
localFilePaths = configPanel.getContentPaths();
|
||||||
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
||||||
try {
|
try {
|
||||||
|
35
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java
Executable file
35
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.casemodule.events;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event published when an OsAccount is added to a case.
|
||||||
|
*/
|
||||||
|
public final class OsAccountAddedEvent extends OsAccountEvent {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public OsAccountAddedEvent(OsAccount account) {
|
||||||
|
super(Case.Events.OS_ACCOUNT_ADDED.toString(), account);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java
Executable file
34
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.casemodule.events;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event published when an OsAccount is updated.
|
||||||
|
*/
|
||||||
|
public final class OsAccountChangedEvent extends OsAccountEvent {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public OsAccountChangedEvent(OsAccount account) {
|
||||||
|
super(Case.Events.OS_ACCOUNT_CHANGED.toString(), account);
|
||||||
|
}
|
||||||
|
}
|
64
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java
Executable file
64
Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.casemodule.events;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent class for specific OsAccount event classes.
|
||||||
|
*/
|
||||||
|
class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new OsAccountEvent.
|
||||||
|
*
|
||||||
|
* @param eventName The name of the event.
|
||||||
|
* @param account The OsAccount the event applies to.
|
||||||
|
*/
|
||||||
|
OsAccountEvent(String eventName, OsAccount account) {
|
||||||
|
super(eventName, Stream.of(account.getId()).collect(Collectors.toList()), Stream.of(account).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the OsAccount that changed.
|
||||||
|
*
|
||||||
|
* @return The OsAccount that was changed.
|
||||||
|
*/
|
||||||
|
public OsAccount getOsAccount() {
|
||||||
|
List<OsAccount> accounts = getNewValue();
|
||||||
|
return accounts.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
|
||||||
|
Long id = ids.get(0);
|
||||||
|
OsAccount account = caseDb.getOsAccountManager().getOsAccount(id);
|
||||||
|
List<OsAccount> accounts = new ArrayList<>();
|
||||||
|
accounts.add(account);
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
}
|
@ -75,8 +75,8 @@ public abstract class TskDataModelChangeEvent<T> extends AutopsyEvent {
|
|||||||
*
|
*
|
||||||
* @return The unique IDs.
|
* @return The unique IDs.
|
||||||
*/
|
*/
|
||||||
public final List<T> getDataModelObjectIds() {
|
public final List<Long> getDataModelObjectIds() {
|
||||||
return Collections.unmodifiableList(dataModelObjects);
|
return Collections.unmodifiableList(dataModelObjectIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
</file>
|
</file>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-CaseDetailsAction.instance"/>
|
<file name="org-sleuthkit-autopsy-casemodule-CaseDetailsAction.instance"/>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-datasourcesummary-DataSourceSummaryAction.instance"/>
|
<file name="org-sleuthkit-autopsy-casemodule-datasourcesummary-DataSourceSummaryAction.instance"/>
|
||||||
|
<file name="org-sleuthkit-autopsy-datamodel-hosts-OpenHostsAction.instance"/>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-CaseDeleteAction.instance"/>
|
<file name="org-sleuthkit-autopsy-casemodule-CaseDeleteAction.instance"/>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-UnpackagePortableCaseAction.instance"/>
|
<file name="org-sleuthkit-autopsy-casemodule-UnpackagePortableCaseAction.instance"/>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-CaseSaveAction.instance">
|
<file name="org-sleuthkit-autopsy-casemodule-CaseSaveAction.instance">
|
||||||
@ -164,7 +165,11 @@
|
|||||||
</file>
|
</file>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-AddImage-separatorBefore.instance">
|
<file name="org-sleuthkit-autopsy-casemodule-AddImage-separatorBefore.instance">
|
||||||
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
||||||
<attr name="position" intvalue="200"/>
|
<attr name="position" intvalue="150"/>
|
||||||
|
</file>
|
||||||
|
<file name="org-sleuthkit-autopsy-datamodel-hosts-OpenHostsAction.shadow">
|
||||||
|
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-datamodel-hosts-OpenHostsAction.instance"/>
|
||||||
|
<attr name="position" intvalue="151"/>
|
||||||
</file>
|
</file>
|
||||||
<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.shadow">
|
<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.shadow">
|
||||||
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-AddImageAction.instance"/>
|
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-AddImageAction.instance"/>
|
||||||
|
@ -18,9 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
@ -28,6 +33,8 @@ import org.openide.nodes.Node;
|
|||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.Host;
|
import org.sleuthkit.datamodel.Host;
|
||||||
import org.sleuthkit.datamodel.OsAccount;
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
@ -99,7 +106,24 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
|||||||
* The child node factory that creates the OsAccountNode children for a
|
* The child node factory that creates the OsAccountNode children for a
|
||||||
* OsAccountListNode.
|
* OsAccountListNode.
|
||||||
*/
|
*/
|
||||||
private final class OsAccountNodeFactory extends ChildFactory<OsAccount> {
|
private final class OsAccountNodeFactory extends ChildFactory.Detachable<OsAccount> {
|
||||||
|
|
||||||
|
private final PropertyChangeListener listener = new PropertyChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addNotify() {
|
||||||
|
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void removeNotify() {
|
||||||
|
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<OsAccount> list) {
|
protected boolean createKeys(List<OsAccount> list) {
|
||||||
@ -129,7 +153,18 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
|||||||
*/
|
*/
|
||||||
public static final class OsAccountNode extends DisplayableItemNode {
|
public static final class OsAccountNode extends DisplayableItemNode {
|
||||||
|
|
||||||
private final OsAccount account;
|
private OsAccount account;
|
||||||
|
|
||||||
|
private final PropertyChangeListener listener = new PropertyChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) {
|
||||||
|
// Update the account node to the new one
|
||||||
|
account = ((OsAccountChangedEvent)evt).getOsAccount();
|
||||||
|
updateSheet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new OsAccountNode.
|
* Constructs a new OsAccountNode.
|
||||||
@ -143,6 +178,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
|||||||
setName(account.getName());
|
setName(account.getName());
|
||||||
setDisplayName(account.getName());
|
setDisplayName(account.getName());
|
||||||
setIconBaseWithExtension(ICON_PATH);
|
setIconBaseWithExtension(ICON_PATH);
|
||||||
|
|
||||||
|
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -175,6 +212,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
|||||||
"OsAccounts_loginNameProperty_desc=Os Account login name"
|
"OsAccounts_loginNameProperty_desc=Os Account login name"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes this node's property sheet.
|
||||||
|
*/
|
||||||
|
void updateSheet() {
|
||||||
|
this.setSheet(createSheet());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Sheet createSheet() {
|
protected Sheet createSheet() {
|
||||||
Sheet sheet = super.createSheet();
|
Sheet sheet = super.createSheet();
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||||
|
<Properties>
|
||||||
|
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||||
|
</Properties>
|
||||||
|
<SyntheticProperties>
|
||||||
|
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||||
|
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||||
|
</SyntheticProperties>
|
||||||
|
<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="false"/>
|
||||||
|
<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="validationLabel" alignment="1" max="32767" attributes="0"/>
|
||||||
|
<Component id="inputTextField" alignment="0" max="32767" attributes="0"/>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<EmptySpace min="0" pref="288" max="32767" attributes="0"/>
|
||||||
|
<Component id="okButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</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"/>
|
||||||
|
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="inputTextField" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="validationLabel" min="-2" pref="18" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JTextField" name="inputTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="AddEditHostDialog.inputTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="nameLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="AddEditHostDialog.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="validationLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
|
<Connection code="Color.RED" type="code"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="verticalAlignment" type="int" value="1"/>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="okButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="AddEditHostDialog.okButton.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="okButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="cancelButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="AddEditHostDialog.cancelButton.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="cancelButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Form>
|
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.datamodel.hosts;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Dialog for adding or editing a host.
|
||||||
|
*/
|
||||||
|
class AddEditHostDialog extends javax.swing.JDialog {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private boolean changed = false;
|
||||||
|
|
||||||
|
// host names to upper and trimmed
|
||||||
|
private final Set<String> hostNamesSanitized;
|
||||||
|
private final Host initialHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param parent The parent frame for this dialog.
|
||||||
|
* @param currentHosts The current set of hosts in the case.
|
||||||
|
*/
|
||||||
|
AddEditHostDialog(java.awt.Frame parent, Collection<Host> currentHosts) {
|
||||||
|
this(parent, currentHosts, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param parent The parent frame for this dialog.
|
||||||
|
* @param currentHosts The current set of hosts (used for determining if
|
||||||
|
* name is unique).
|
||||||
|
* @param initialHost If adding a new host, this will be a null value.
|
||||||
|
* Otherwise, if editing, this will be the host being edited.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"AddEditHostDialog_addHost_title=Add Host",
|
||||||
|
"AddEditHostDialog_editHost_title=Edit Host"
|
||||||
|
})
|
||||||
|
AddEditHostDialog(java.awt.Frame parent, Collection<Host> currentHosts, Host initialHost) {
|
||||||
|
super(parent, true);
|
||||||
|
this.initialHost = initialHost;
|
||||||
|
setTitle(initialHost == null ? Bundle.AddEditHostDialog_addHost_title() : Bundle.AddEditHostDialog_editHost_title());
|
||||||
|
|
||||||
|
hostNamesSanitized = HostNameValidator.getSanitizedHostNames(currentHosts);
|
||||||
|
|
||||||
|
initComponents();
|
||||||
|
onNameUpdate(initialHost == null ? null : initialHost.getName());
|
||||||
|
|
||||||
|
// initially, don't show validation message (for empty strings or repeat),
|
||||||
|
// but do disable ok button if not valid.
|
||||||
|
validationLabel.setText("");
|
||||||
|
|
||||||
|
inputTextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
onNameUpdate(inputTextField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
onNameUpdate(inputTextField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
onNameUpdate(inputTextField.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The string value for the name in the input field if Ok pressed or
|
||||||
|
* null if not.
|
||||||
|
*/
|
||||||
|
String getValue() {
|
||||||
|
return inputTextField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether or not the value has been changed and the user pressed
|
||||||
|
* okay to save the new value.
|
||||||
|
*/
|
||||||
|
boolean isChanged() {
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the text field is updated, this method is called.
|
||||||
|
*
|
||||||
|
* @param newNameValue
|
||||||
|
*/
|
||||||
|
private void onNameUpdate(String newNameValue) {
|
||||||
|
String newNameValueOrEmpty = newNameValue == null ? "" : newNameValue;
|
||||||
|
// update input text field if it is not the same.
|
||||||
|
if (!newNameValueOrEmpty.equals(this.inputTextField.getText())) {
|
||||||
|
inputTextField.setText(newNameValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate text input against invariants setting validation
|
||||||
|
// message and whether or not okay button is enabled accordingly.
|
||||||
|
String validationMessage = HostNameValidator.getValidationMessage(
|
||||||
|
newNameValue, initialHost == null ? null : initialHost.getName(), hostNamesSanitized);
|
||||||
|
|
||||||
|
okButton.setEnabled(validationMessage == null);
|
||||||
|
validationLabel.setText(validationMessage == null ? "" : validationMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
|
||||||
|
inputTextField = new javax.swing.JTextField();
|
||||||
|
javax.swing.JLabel nameLabel = new javax.swing.JLabel();
|
||||||
|
validationLabel = new javax.swing.JLabel();
|
||||||
|
okButton = new javax.swing.JButton();
|
||||||
|
javax.swing.JButton cancelButton = new javax.swing.JButton();
|
||||||
|
|
||||||
|
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
|
inputTextField.setText(org.openide.util.NbBundle.getMessage(AddEditHostDialog.class, "AddEditHostDialog.inputTextField.text")); // NOI18N
|
||||||
|
|
||||||
|
nameLabel.setText(org.openide.util.NbBundle.getMessage(AddEditHostDialog.class, "AddEditHostDialog.nameLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
validationLabel.setForeground(Color.RED);
|
||||||
|
validationLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
|
||||||
|
|
||||||
|
okButton.setText(org.openide.util.NbBundle.getMessage(AddEditHostDialog.class, "AddEditHostDialog.okButton.text")); // NOI18N
|
||||||
|
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
okButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cancelButton.setText(org.openide.util.NbBundle.getMessage(AddEditHostDialog.class, "AddEditHostDialog.cancelButton.text")); // NOI18N
|
||||||
|
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
cancelButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||||
|
getContentPane().setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(validationLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(inputTextField)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(nameLabel)
|
||||||
|
.addGap(0, 0, Short.MAX_VALUE))
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addGap(0, 288, Short.MAX_VALUE)
|
||||||
|
.addComponent(okButton)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(cancelButton)))
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addComponent(nameLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(inputTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(validationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(cancelButton)
|
||||||
|
.addComponent(okButton))
|
||||||
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||||
|
this.changed = true;
|
||||||
|
dispose();
|
||||||
|
}//GEN-LAST:event_okButtonActionPerformed
|
||||||
|
|
||||||
|
|
||||||
|
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||||
|
this.changed = false;
|
||||||
|
dispose();
|
||||||
|
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||||
|
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JTextField inputTextField;
|
||||||
|
private javax.swing.JButton okButton;
|
||||||
|
private javax.swing.JLabel validationLabel;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
# To change this license header, choose License Headers in Project Properties.
|
||||||
|
# To change this template file, choose Tools | Templates
|
||||||
|
# and open the template in the editor.
|
||||||
|
ManageHostsDialog.hostDetailsLabel.text=Host Details
|
||||||
|
ManageHostsDialog.hostNameLabel.text=Host Name:
|
||||||
|
ManageHostsDialog.closeButton.text=Close
|
||||||
|
ManageHostsDialog.hostDescriptionTextArea.text=Hosts represent individual devices that may have multiple data sources.
|
||||||
|
ManageHostsDialog.hostListLabel.text=Hosts
|
||||||
|
ManageHostsDialog.newButton.text=New
|
||||||
|
ManageHostsDialog.editButton.text=Edit
|
||||||
|
ManageHostsDialog.deleteButton.text=Delete
|
||||||
|
AddEditHostDialog.nameLabel.text=Name:
|
||||||
|
AddEditHostDialog.okButton.text=OK
|
||||||
|
AddEditHostDialog.cancelButton.text=Cancel
|
||||||
|
AddEditHostDialog.inputTextField.text=jTextField1
|
@ -0,0 +1,23 @@
|
|||||||
|
AddEditHostDialog_addHost_title=Add Host
|
||||||
|
AddEditHostDialog_editHost_title=Edit Host
|
||||||
|
CTL_OpenHosts=Manage Hosts
|
||||||
|
HostNameValidator_getValidationMessage_onDuplicate=Another host already has the same name. Please choose a different name.
|
||||||
|
HostNameValidator_getValidationMessage_onEmpty=Please provide some text for the host name.
|
||||||
|
HostNameValidator_getValidationMessage_sameAsOriginal=Please provide a new name for this host.
|
||||||
|
# To change this license header, choose License Headers in Project Properties.
|
||||||
|
# To change this template file, choose Tools | Templates
|
||||||
|
# and open the template in the editor.
|
||||||
|
ManageHostsDialog.hostDetailsLabel.text=Host Details
|
||||||
|
ManageHostsDialog.hostNameLabel.text=Host Name:
|
||||||
|
ManageHostsDialog.closeButton.text=Close
|
||||||
|
ManageHostsDialog.hostDescriptionTextArea.text=Hosts represent individual devices that may have multiple data sources.
|
||||||
|
ManageHostsDialog.hostListLabel.text=Hosts
|
||||||
|
ManageHostsDialog.newButton.text=New
|
||||||
|
ManageHostsDialog.editButton.text=Edit
|
||||||
|
ManageHostsDialog.deleteButton.text=Delete
|
||||||
|
AddEditHostDialog.nameLabel.text=Name:
|
||||||
|
AddEditHostDialog.okButton.text=OK
|
||||||
|
AddEditHostDialog.cancelButton.text=Cancel
|
||||||
|
AddEditHostDialog.inputTextField.text=jTextField1
|
||||||
|
ManageHostsDialog_title_text=Manage Hosts
|
||||||
|
OpenHostsAction_displayName=Hosts
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.datamodel.hosts;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides methods for validating host names.
|
||||||
|
*/
|
||||||
|
public class HostNameValidator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the validation message based on the current text checked against the
|
||||||
|
* host names.
|
||||||
|
*
|
||||||
|
* @param curName The current name in the text field.
|
||||||
|
* @param initialName If editing a name, the initial name of the host.
|
||||||
|
* Otherwise, null can be provided for this parameter.
|
||||||
|
* @param currentHostsTrimmedUpper The current host names. This set should
|
||||||
|
* be sanitized to upper case and trimmed.
|
||||||
|
* @return The validation message if the name is not valid or null.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"HostNameValidator_getValidationMessage_onEmpty=Please provide some text for the host name.",
|
||||||
|
"HostNameValidator_getValidationMessage_sameAsOriginal=Please provide a new name for this host.",
|
||||||
|
"HostNameValidator_getValidationMessage_onDuplicate=Another host already has the same name. Please choose a different name.",})
|
||||||
|
public static String getValidationMessage(String curName, String initialName, Set<String> currentHostsTrimmedUpper) {
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(curName)) {
|
||||||
|
return Bundle.HostNameValidator_getValidationMessage_onEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.equalsIgnoreCase(initialName, curName)) {
|
||||||
|
return Bundle.HostNameValidator_getValidationMessage_sameAsOriginal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentHostsTrimmedUpper.contains(curName.trim().toUpperCase())) {
|
||||||
|
return Bundle.HostNameValidator_getValidationMessage_onDuplicate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a list of host names trimmed and to upper case that can be used
|
||||||
|
* with getValidationMessage.
|
||||||
|
*
|
||||||
|
* @param hosts The hosts.
|
||||||
|
* @return The set of host names trimmed and to upper case.
|
||||||
|
*/
|
||||||
|
public static Set<String> getSanitizedHostNames(Collection<Host> hosts) {
|
||||||
|
Stream<Host> hostsStream = hosts != null ? hosts.stream() : Stream.empty();
|
||||||
|
return hostsStream
|
||||||
|
.map(h -> h == null ? null : h.getName())
|
||||||
|
.filter(hName -> StringUtils.isNotBlank(hName))
|
||||||
|
.map(hName -> hName.trim().toUpperCase())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,359 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||||
|
<Properties>
|
||||||
|
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[600, 450]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<SyntheticProperties>
|
||||||
|
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||||
|
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||||
|
</SyntheticProperties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||||
|
<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="false"/>
|
||||||
|
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||||
|
<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 min="-2" pref="0" max="-2" attributes="0"/>
|
||||||
|
<Component id="manageHostsScrollPane" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||||
|
<Component id="manageHostsScrollPane" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Container class="javax.swing.JScrollPane" name="manageHostsScrollPane">
|
||||||
|
<Properties>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[600, 450]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[600, 450]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
|
<SubComponents>
|
||||||
|
<Container class="javax.swing.JPanel" name="manageHostsPanel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[527, 407]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<DimensionLayout dim="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="hostDescriptionScrollPane" alignment="0" min="-2" pref="225" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostListLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<Component id="newButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="editButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="deleteButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Component id="hostListScrollPane" alignment="0" min="-2" pref="224" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="jSeparator1" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace type="unrelated" max="32767" attributes="0"/>
|
||||||
|
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostNameLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostNameTextField" pref="79" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostDetailsLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="1" attributes="0">
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<Component id="hostDetailsLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="hostNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
|
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Component id="jSeparator1" alignment="1" max="32767" attributes="0"/>
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<Component id="hostDescriptionScrollPane" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostListLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hostListScrollPane" pref="325" max="32767" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="newButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="deleteButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="editButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Container class="javax.swing.JScrollPane" name="hostListScrollPane">
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JList" name="hostList">
|
||||||
|
<Properties>
|
||||||
|
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||||
|
<StringArray count="0"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="selectionMode" type="int" value="0"/>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<org.sleuthkit.autopsy.datamodel.hosts.ManageHostsDialog.HostListItem>"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
<Container class="javax.swing.JScrollPane" name="hostDescriptionScrollPane">
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JTextArea" name="hostDescriptionTextArea">
|
||||||
|
<Properties>
|
||||||
|
<Property name="editable" type="boolean" value="false"/>
|
||||||
|
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||||
|
<Color blue="f0" green="f0" red="f0" type="rgb"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="columns" type="int" value="20"/>
|
||||||
|
<Property name="lineWrap" type="boolean" value="true"/>
|
||||||
|
<Property name="rows" type="int" value="3"/>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.hostDescriptionTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
<Component class="javax.swing.JButton" name="newButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.newButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 6, 2, 6]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.newButton.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="deleteButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.deleteButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 6, 2, 6]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.deleteButton.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="closeButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.closeButton.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="hostListLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.hostListLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.hostListLabel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||||
|
<Properties>
|
||||||
|
<Property name="orientation" type="int" value="1"/>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="hostNameLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.hostNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.hostNameLabel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="hostNameTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="editable" type="boolean" value="false"/>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="editButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.editButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[70, 23]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.editButton.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="hostDetailsLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageHostsDialog.hostDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties" key="ManageHostsDialog.hostDetailsLabel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
</SubComponents>
|
||||||
|
</Form>
|
@ -0,0 +1,580 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.datamodel.hosts;
|
||||||
|
|
||||||
|
import java.awt.Dialog;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.ListModel;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for managing CRUD operations with hosts from the UI.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"ManageHostsDialog_title_text=Manage Hosts"
|
||||||
|
})
|
||||||
|
public class ManageHostsDialog extends javax.swing.JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List item to be used with jlist.
|
||||||
|
*/
|
||||||
|
private static class HostListItem {
|
||||||
|
|
||||||
|
private final Host host;
|
||||||
|
private final List<DataSource> dataSources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param host The host.
|
||||||
|
* @param dataSources The data sources that are children of this host.
|
||||||
|
*/
|
||||||
|
HostListItem(Host host, List<DataSource> dataSources) {
|
||||||
|
this.host = host;
|
||||||
|
this.dataSources = dataSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The host.
|
||||||
|
*/
|
||||||
|
Host getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The data sources associated with this host.
|
||||||
|
*/
|
||||||
|
List<DataSource> getDataSources() {
|
||||||
|
return dataSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return host == null ? "" : host.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 89 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final HostListItem other = (HostListItem) obj;
|
||||||
|
if (this.host == null || other.getHost() == null) {
|
||||||
|
return this.host == null && other.getHost() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.host.getId() == other.getHost().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ManageHostsDialog.class.getName());
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Map<Host, List<DataSource>> hostChildrenMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param parent The parent frame.
|
||||||
|
*/
|
||||||
|
public ManageHostsDialog(java.awt.Frame parent) {
|
||||||
|
super(parent, Bundle.ManageHostsDialog_title_text(), true);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param parent The parent dialog.
|
||||||
|
*/
|
||||||
|
public ManageHostsDialog(Dialog parent) {
|
||||||
|
super(parent, Bundle.ManageHostsDialog_title_text(), true);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes components, loads host data, and sets up list listener.
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
initComponents();
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// refreshes UI when selection changes including button enabled state and data.
|
||||||
|
this.hostList.addListSelectionListener((evt) -> refreshComponents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The currently selected host in the list or null if no host is
|
||||||
|
* selected.
|
||||||
|
*/
|
||||||
|
Host getSelectedHost() {
|
||||||
|
return (hostList.getSelectedValue() == null) ? null : hostList.getSelectedValue().getHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows add/edit dialog, and if a value is returned, creates a new Host.
|
||||||
|
*/
|
||||||
|
private void addHost() {
|
||||||
|
String newHostName = getAddEditDialogName(null);
|
||||||
|
if (newHostName != null) {
|
||||||
|
Long selectedId = null;
|
||||||
|
try {
|
||||||
|
Host newHost = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
|
||||||
|
selectedId = newHost == null ? null : newHost.getId();
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException e) {
|
||||||
|
logger.log(Level.WARNING, String.format("Unable to add new host '%s' at this time.", newHostName), e);
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
setSelectedHostById(selectedId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the selected host if possible.
|
||||||
|
*
|
||||||
|
* @param selectedHost
|
||||||
|
*/
|
||||||
|
private void deleteHost(Host selectedHost) {
|
||||||
|
if (selectedHost != null && selectedHost.getName() != null) {
|
||||||
|
try {
|
||||||
|
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().deleteHost(selectedHost.getName());
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException e) {
|
||||||
|
logger.log(Level.WARNING, String.format("Unable to delete host '%s' at this time.", selectedHost.getName()), e);
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the host with the given id. If no matching id found in list.
|
||||||
|
*
|
||||||
|
* @param selectedId The id of the host to select.
|
||||||
|
*/
|
||||||
|
private void setSelectedHostById(Long selectedId) {
|
||||||
|
ListModel<HostListItem> model = hostList.getModel();
|
||||||
|
|
||||||
|
if (selectedId == null) {
|
||||||
|
hostList.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < model.getSize(); i++) {
|
||||||
|
Object o = model.getElementAt(i);
|
||||||
|
if (!(o instanceof HostListItem)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Host host = ((HostListItem) o).getHost();
|
||||||
|
if (host == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.getId() == selectedId) {
|
||||||
|
hostList.setSelectedIndex(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hostList.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows add/edit dialog, and if a value is returned, creates a new Host.
|
||||||
|
*
|
||||||
|
* @param selectedHost The selected host.
|
||||||
|
*/
|
||||||
|
private void editHost(Host selectedHost) {
|
||||||
|
|
||||||
|
if (selectedHost != null) {
|
||||||
|
String newHostName = getAddEditDialogName(selectedHost);
|
||||||
|
if (newHostName != null) {
|
||||||
|
selectedHost.setName(newHostName);
|
||||||
|
try {
|
||||||
|
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().updateHost(selectedHost);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException e) {
|
||||||
|
logger.log(Level.WARNING, String.format("Unable to update host '%s' with id: %d at this time.", selectedHost.getName(), selectedHost.getId()), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostListItem selectedItem = hostList.getSelectedValue();
|
||||||
|
Long selectedId = selectedItem == null || selectedItem.getHost() == null ? null : selectedItem.getHost().getId();
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
setSelectedHostById(selectedId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the dialog to add or edit the name of a host.
|
||||||
|
*
|
||||||
|
* @param origValue The original values for the host or null if adding a
|
||||||
|
* host.
|
||||||
|
* @return The new name for the host or null if operation was cancelled.
|
||||||
|
*/
|
||||||
|
private String getAddEditDialogName(Host origValue) {
|
||||||
|
JFrame parent = (this.getRootPane() != null && this.getRootPane().getParent() instanceof JFrame)
|
||||||
|
? (JFrame) this.getRootPane().getParent()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
AddEditHostDialog addEditDialog = new AddEditHostDialog(parent, hostChildrenMap.keySet(), origValue);
|
||||||
|
addEditDialog.setResizable(false);
|
||||||
|
addEditDialog.setLocationRelativeTo(parent);
|
||||||
|
addEditDialog.setVisible(true);
|
||||||
|
addEditDialog.toFront();
|
||||||
|
|
||||||
|
if (addEditDialog.isChanged()) {
|
||||||
|
String newHostName = addEditDialog.getValue();
|
||||||
|
return newHostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the data and ui components for this dialog.
|
||||||
|
*/
|
||||||
|
private void refresh() {
|
||||||
|
refreshData();
|
||||||
|
refreshComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the data for this dialog and updates the host JList with the
|
||||||
|
* hosts.
|
||||||
|
*/
|
||||||
|
private void refreshData() {
|
||||||
|
|
||||||
|
hostChildrenMap = getHostListData();
|
||||||
|
|
||||||
|
Vector<HostListItem> jlistData = hostChildrenMap.entrySet().stream()
|
||||||
|
.sorted((a, b) -> getNameOrEmpty(a.getKey()).compareTo(getNameOrEmpty(b.getKey())))
|
||||||
|
.map(entry -> new HostListItem(entry.getKey(), entry.getValue()))
|
||||||
|
.collect(Collectors.toCollection(Vector::new));
|
||||||
|
|
||||||
|
hostList.setListData(jlistData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the host or an empty string if the host or name of
|
||||||
|
* host is null.
|
||||||
|
*
|
||||||
|
* @param h The host.
|
||||||
|
* @return The name of the host or empty string.
|
||||||
|
*/
|
||||||
|
private String getNameOrEmpty(Host h) {
|
||||||
|
return (h == null || h.getName() == null) ? "" : h.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current list of hosts for the case.
|
||||||
|
*
|
||||||
|
* @return The list of hosts to be displayed in the list (sorted
|
||||||
|
* alphabetically).
|
||||||
|
*/
|
||||||
|
private Map<Host, List<DataSource>> getHostListData() {
|
||||||
|
Map<Host, List<DataSource>> hostMapping = new HashMap<>();
|
||||||
|
try {
|
||||||
|
SleuthkitCase curCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||||
|
List<Host> hosts = curCase.getHostManager().getHosts();
|
||||||
|
List<DataSource> dataSources = curCase.getDataSources();
|
||||||
|
|
||||||
|
if (dataSources != null) {
|
||||||
|
for (DataSource ds : dataSources) {
|
||||||
|
List<DataSource> hostDataSources = hostMapping.computeIfAbsent(ds.getHost(), (d) -> new ArrayList<>());
|
||||||
|
hostDataSources.add(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hosts != null) {
|
||||||
|
for (Host host : hosts) {
|
||||||
|
hostMapping.putIfAbsent(host, Collections.emptyList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||||
|
logger.log(Level.WARNING, "There was an error while fetching hosts for current case.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes component's enabled state and displayed host data.
|
||||||
|
*/
|
||||||
|
private void refreshComponents() {
|
||||||
|
HostListItem selectedItem = hostList.getSelectedValue();
|
||||||
|
Host selectedHost = selectedItem == null ? null : selectedItem.getHost();
|
||||||
|
List<DataSource> dataSources = selectedItem == null ? null : selectedItem.getDataSources();
|
||||||
|
this.editButton.setEnabled(selectedHost != null);
|
||||||
|
this.deleteButton.setEnabled(selectedHost != null && CollectionUtils.isEmpty(dataSources));
|
||||||
|
String nameTextFieldStr = selectedHost != null && selectedHost.getName() != null ? selectedHost.getName() : "";
|
||||||
|
this.hostNameTextField.setText(nameTextFieldStr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
|
||||||
|
javax.swing.JScrollPane manageHostsScrollPane = new javax.swing.JScrollPane();
|
||||||
|
javax.swing.JPanel manageHostsPanel = new javax.swing.JPanel();
|
||||||
|
javax.swing.JScrollPane hostListScrollPane = new javax.swing.JScrollPane();
|
||||||
|
hostList = new javax.swing.JList<>();
|
||||||
|
javax.swing.JScrollPane hostDescriptionScrollPane = new javax.swing.JScrollPane();
|
||||||
|
hostDescriptionTextArea = new javax.swing.JTextArea();
|
||||||
|
newButton = new javax.swing.JButton();
|
||||||
|
deleteButton = new javax.swing.JButton();
|
||||||
|
closeButton = new javax.swing.JButton();
|
||||||
|
javax.swing.JLabel hostListLabel = new javax.swing.JLabel();
|
||||||
|
javax.swing.JSeparator jSeparator1 = new javax.swing.JSeparator();
|
||||||
|
javax.swing.JLabel hostNameLabel = new javax.swing.JLabel();
|
||||||
|
hostNameTextField = new javax.swing.JTextField();
|
||||||
|
editButton = new javax.swing.JButton();
|
||||||
|
javax.swing.JLabel hostDetailsLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
|
setMinimumSize(new java.awt.Dimension(600, 450));
|
||||||
|
|
||||||
|
manageHostsScrollPane.setMinimumSize(new java.awt.Dimension(600, 450));
|
||||||
|
manageHostsScrollPane.setPreferredSize(new java.awt.Dimension(600, 450));
|
||||||
|
|
||||||
|
manageHostsPanel.setPreferredSize(new java.awt.Dimension(527, 407));
|
||||||
|
|
||||||
|
hostList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
hostListScrollPane.setViewportView(hostList);
|
||||||
|
|
||||||
|
hostDescriptionTextArea.setEditable(false);
|
||||||
|
hostDescriptionTextArea.setBackground(new java.awt.Color(240, 240, 240));
|
||||||
|
hostDescriptionTextArea.setColumns(20);
|
||||||
|
hostDescriptionTextArea.setLineWrap(true);
|
||||||
|
hostDescriptionTextArea.setRows(3);
|
||||||
|
hostDescriptionTextArea.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.hostDescriptionTextArea.text")); // NOI18N
|
||||||
|
hostDescriptionTextArea.setWrapStyleWord(true);
|
||||||
|
hostDescriptionScrollPane.setViewportView(hostDescriptionTextArea);
|
||||||
|
|
||||||
|
newButton.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.newButton.text")); // NOI18N
|
||||||
|
newButton.setMargin(new java.awt.Insets(2, 6, 2, 6));
|
||||||
|
newButton.setMaximumSize(new java.awt.Dimension(70, 23));
|
||||||
|
newButton.setMinimumSize(new java.awt.Dimension(70, 23));
|
||||||
|
newButton.setPreferredSize(new java.awt.Dimension(70, 23));
|
||||||
|
newButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
newButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteButton.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.deleteButton.text")); // NOI18N
|
||||||
|
deleteButton.setMargin(new java.awt.Insets(2, 6, 2, 6));
|
||||||
|
deleteButton.setMaximumSize(new java.awt.Dimension(70, 23));
|
||||||
|
deleteButton.setMinimumSize(new java.awt.Dimension(70, 23));
|
||||||
|
deleteButton.setPreferredSize(new java.awt.Dimension(70, 23));
|
||||||
|
deleteButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
deleteButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
closeButton.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.closeButton.text")); // NOI18N
|
||||||
|
closeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
closeButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hostListLabel.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.hostListLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||||
|
|
||||||
|
hostNameLabel.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.hostNameLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
hostNameTextField.setEditable(false);
|
||||||
|
|
||||||
|
editButton.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.editButton.text")); // NOI18N
|
||||||
|
editButton.setMaximumSize(new java.awt.Dimension(70, 23));
|
||||||
|
editButton.setMinimumSize(new java.awt.Dimension(70, 23));
|
||||||
|
editButton.setPreferredSize(new java.awt.Dimension(70, 23));
|
||||||
|
editButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
editButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hostDetailsLabel.setText(org.openide.util.NbBundle.getMessage(ManageHostsDialog.class, "ManageHostsDialog.hostDetailsLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
javax.swing.GroupLayout manageHostsPanelLayout = new javax.swing.GroupLayout(manageHostsPanel);
|
||||||
|
manageHostsPanel.setLayout(manageHostsPanelLayout);
|
||||||
|
manageHostsPanelLayout.setHorizontalGroup(
|
||||||
|
manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(hostDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(hostListLabel)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addComponent(newButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(editButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(deleteButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addComponent(hostListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 224, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(closeButton))
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addGap(29, 29, 29)
|
||||||
|
.addComponent(hostNameLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(hostNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 79, Short.MAX_VALUE)))
|
||||||
|
.addContainerGap())
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(hostDetailsLabel)
|
||||||
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||||
|
);
|
||||||
|
manageHostsPanelLayout.setVerticalGroup(
|
||||||
|
manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addComponent(hostDetailsLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(hostNameLabel)
|
||||||
|
.addComponent(hostNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(closeButton))
|
||||||
|
.addComponent(jSeparator1)
|
||||||
|
.addGroup(manageHostsPanelLayout.createSequentialGroup()
|
||||||
|
.addComponent(hostDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(hostListLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(hostListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 325, Short.MAX_VALUE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(manageHostsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(newButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(deleteButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(editButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
|
||||||
|
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/sleuthkit/autopsy/datamodel/hosts/Bundle"); // NOI18N
|
||||||
|
newButton.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.newButton.text")); // NOI18N
|
||||||
|
deleteButton.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.deleteButton.text")); // NOI18N
|
||||||
|
closeButton.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.closeButton.text")); // NOI18N
|
||||||
|
hostListLabel.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.hostListLabel.text")); // NOI18N
|
||||||
|
hostNameLabel.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.hostNameLabel.text")); // NOI18N
|
||||||
|
editButton.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.editButton.text")); // NOI18N
|
||||||
|
hostDetailsLabel.getAccessibleContext().setAccessibleName(bundle.getString("ManageHostsDialog.hostDetailsLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
manageHostsScrollPane.setViewportView(manageHostsPanel);
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||||
|
getContentPane().setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addGap(0, 0, 0)
|
||||||
|
.addComponent(manageHostsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addGap(0, 0, 0)
|
||||||
|
.addComponent(manageHostsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
private void newButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newButtonActionPerformed
|
||||||
|
addHost();
|
||||||
|
}//GEN-LAST:event_newButtonActionPerformed
|
||||||
|
|
||||||
|
private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
|
||||||
|
HostListItem listItem = this.hostList.getSelectedValue();
|
||||||
|
if (listItem != null && listItem.getHost() != null) {
|
||||||
|
deleteHost(listItem.getHost());
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_deleteButtonActionPerformed
|
||||||
|
|
||||||
|
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
|
||||||
|
dispose();
|
||||||
|
}//GEN-LAST:event_closeButtonActionPerformed
|
||||||
|
|
||||||
|
private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editButtonActionPerformed
|
||||||
|
HostListItem listItem = this.hostList.getSelectedValue();
|
||||||
|
if (listItem != null && listItem.getHost() != null) {
|
||||||
|
editHost(listItem.getHost());
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_editButtonActionPerformed
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JButton closeButton;
|
||||||
|
private javax.swing.JButton deleteButton;
|
||||||
|
private javax.swing.JButton editButton;
|
||||||
|
private javax.swing.JTextArea hostDescriptionTextArea;
|
||||||
|
private javax.swing.JList<org.sleuthkit.autopsy.datamodel.hosts.ManageHostsDialog.HostListItem> hostList;
|
||||||
|
private javax.swing.JTextField hostNameTextField;
|
||||||
|
private javax.swing.JButton newButton;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.datamodel.hosts;
|
||||||
|
|
||||||
|
import java.awt.Frame;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import org.openide.awt.ActionID;
|
||||||
|
import org.openide.awt.ActionRegistration;
|
||||||
|
import org.openide.util.HelpCtx;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.openide.util.actions.CallableSystemAction;
|
||||||
|
import org.openide.windows.WindowManager;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Action that opens the Host Management window.
|
||||||
|
*/
|
||||||
|
@ActionID(category = "Case", id = "org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction")
|
||||||
|
@ActionRegistration(displayName = "#CTL_OpenHosts", lazy = false)
|
||||||
|
@Messages({
|
||||||
|
"CTL_OpenHosts=Manage Hosts",})
|
||||||
|
public final class OpenHostsAction extends CallableSystemAction {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*/
|
||||||
|
OpenHostsAction() {
|
||||||
|
putValue(Action.NAME, Bundle.CTL_OpenHosts());
|
||||||
|
this.setEnabled(false);
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
|
||||||
|
setEnabled(null != evt.getNewValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performAction() {
|
||||||
|
Frame parent = WindowManager.getDefault().getMainWindow();
|
||||||
|
ManageHostsDialog dialog = new ManageHostsDialog(parent);
|
||||||
|
dialog.setResizable(false);
|
||||||
|
dialog.setLocationRelativeTo(parent);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
dialog.toFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NbBundle.Messages("OpenHostsAction_displayName=Hosts")
|
||||||
|
public String getName() {
|
||||||
|
return Bundle.OpenHostsAction_displayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HelpCtx getHelpCtx() {
|
||||||
|
return HelpCtx.DEFAULT_HELP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean asynchronous() {
|
||||||
|
return false; // run on edt
|
||||||
|
}
|
||||||
|
}
|
@ -159,15 +159,6 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource
|
|||||||
@Override
|
@Override
|
||||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - use passed in value
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("RawDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
// It's not worth adding a logger for temporary code
|
|
||||||
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), host, progressMonitor, callback);
|
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), host, progressMonitor, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,15 +219,6 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa
|
|||||||
})
|
})
|
||||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
progressMonitor.setIndeterminate(true);
|
progressMonitor.setIndeterminate(true);
|
||||||
|
|
||||||
// HOSTTODO - use passed in value
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("XRYDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String selectedFilePath = configPanel.getSelectedFilePath();
|
String selectedFilePath = configPanel.getSelectedFilePath();
|
||||||
File selectedFile = new File(selectedFilePath);
|
File selectedFile = new File(selectedFilePath);
|
||||||
Path selectedPath = selectedFile.toPath();
|
Path selectedPath = selectedFile.toPath();
|
||||||
|
@ -162,8 +162,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
|
|||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
@Override
|
@Override
|
||||||
void clearList() {
|
void clearList() {
|
||||||
tableModel.setContents(new ArrayList<>());
|
addArtifacts(new ArrayList<>());
|
||||||
tableModel.fireTableDataChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,9 +92,9 @@ final class DomainDetailsPanel extends JPanel {
|
|||||||
if (selectedTabName == null || !selectedTabName.equals(newTabTitle)) {
|
if (selectedTabName == null || !selectedTabName.equals(newTabTitle)) {
|
||||||
selectedTabName = newTabTitle;
|
selectedTabName = newTabTitle;
|
||||||
Component selectedComponent = jTabbedPane1.getSelectedComponent();
|
Component selectedComponent = jTabbedPane1.getSelectedComponent();
|
||||||
if (selectedComponent instanceof DomainArtifactsTabPanel) {
|
if (!StringUtils.isBlank(domain) && selectedComponent instanceof DomainArtifactsTabPanel) {
|
||||||
runDomainWorker((DomainArtifactsTabPanel) selectedComponent, true);
|
runDomainWorker((DomainArtifactsTabPanel) selectedComponent, true);
|
||||||
} else if (selectedComponent instanceof MiniTimelinePanel) {
|
} else if (!StringUtils.isBlank(domain) && selectedComponent instanceof MiniTimelinePanel) {
|
||||||
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent, true);
|
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,13 +182,13 @@ final class DomainDetailsPanel extends JPanel {
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
|
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
if (StringUtils.isBlank(populateEvent.getDomain())) {
|
domain = populateEvent.getDomain();
|
||||||
|
if (StringUtils.isBlank(domain)) {
|
||||||
resetTabsStatus();
|
resetTabsStatus();
|
||||||
//send fade out event
|
//send fade out event
|
||||||
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
|
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
|
||||||
} else {
|
} else {
|
||||||
resetTabsStatus();
|
resetTabsStatus();
|
||||||
domain = populateEvent.getDomain();
|
|
||||||
Component selectedComponent = jTabbedPane1.getSelectedComponent();
|
Component selectedComponent = jTabbedPane1.getSelectedComponent();
|
||||||
if (selectedComponent instanceof DomainArtifactsTabPanel) {
|
if (selectedComponent instanceof DomainArtifactsTabPanel) {
|
||||||
runDomainWorker((DomainArtifactsTabPanel) selectedComponent, false);
|
runDomainWorker((DomainArtifactsTabPanel) selectedComponent, false);
|
||||||
|
@ -23,6 +23,11 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
/**
|
/**
|
||||||
* An adapter that provides a no-op implementation of the startUp() method for
|
* An adapter that provides a no-op implementation of the startUp() method for
|
||||||
* data source ingest modules.
|
* data source ingest modules.
|
||||||
|
*
|
||||||
|
* NOTE: As of Java 8, interfaces can have default methods.
|
||||||
|
* DataSourceIngestModule now provides default no-op versions of startUp() and
|
||||||
|
* shutDown(). This class is no longer needed and can be deprecated when
|
||||||
|
* convenient.
|
||||||
*/
|
*/
|
||||||
public abstract class DataSourceIngestModuleAdapter implements DataSourceIngestModule {
|
public abstract class DataSourceIngestModuleAdapter implements DataSourceIngestModule {
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2014-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,184 +18,86 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.ingest;
|
package org.sleuthkit.autopsy.ingest;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class manages a sequence of data source level ingest modules for an
|
* A pipeline of data source level ingest modules for performing data source
|
||||||
* ingestJobPipeline. It starts the modules, runs data sources through them, and
|
* level ingest tasks for an ingest job.
|
||||||
* shuts them down when data source level ingest is complete.
|
|
||||||
* <p>
|
|
||||||
* This class is thread-safe.
|
|
||||||
*/
|
*/
|
||||||
final class DataSourceIngestPipeline {
|
final class DataSourceIngestPipeline extends IngestTaskPipeline<DataSourceIngestTask> {
|
||||||
|
|
||||||
private static final IngestManager ingestManager = IngestManager.getInstance();
|
|
||||||
private static final Logger logger = Logger.getLogger(DataSourceIngestPipeline.class.getName());
|
private static final Logger logger = Logger.getLogger(DataSourceIngestPipeline.class.getName());
|
||||||
private final IngestJobPipeline ingestJobPipeline;
|
private static final IngestManager ingestManager = IngestManager.getInstance();
|
||||||
private final List<PipelineModule> modules = new ArrayList<>();
|
|
||||||
private volatile PipelineModule currentModule;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object that manages a sequence of data source level ingest
|
* Constructs a pipeline of data source level ingest modules for performing
|
||||||
* modules. It starts the modules, runs data sources through them, and shuts
|
* data source level ingest tasks for an ingest job.
|
||||||
* them down when data source level ingest is complete.
|
|
||||||
*
|
*
|
||||||
* @param ingestJobPipeline The ingestJobPipeline that owns this pipeline.
|
* @param ingestJobPipeline The ingest job pipeline that owns this pipeline.
|
||||||
* @param moduleTemplates Templates for the creating the ingest modules that
|
* @param moduleTemplates The ingest module templates that define this
|
||||||
* make up this pipeline.
|
* pipeline.
|
||||||
*/
|
*/
|
||||||
DataSourceIngestPipeline(IngestJobPipeline ingestJobPipeline, List<IngestModuleTemplate> moduleTemplates) {
|
DataSourceIngestPipeline(IngestJobPipeline ingestJobPipeline, List<IngestModuleTemplate> moduleTemplates) {
|
||||||
this.ingestJobPipeline = ingestJobPipeline;
|
super(ingestJobPipeline, moduleTemplates);
|
||||||
for (IngestModuleTemplate template : moduleTemplates) {
|
|
||||||
if (template.isDataSourceIngestModuleTemplate()) {
|
|
||||||
PipelineModule module = new PipelineModule(template.createDataSourceIngestModule(), template.getModuleName());
|
|
||||||
modules.add(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Indicates whether or not there are any ingest modules in this pipeline.
|
Optional<IngestTaskPipeline.PipelineModule<DataSourceIngestTask>> acceptModuleTemplate(IngestModuleTemplate template) {
|
||||||
*
|
Optional<IngestTaskPipeline.PipelineModule<DataSourceIngestTask>> module = Optional.empty();
|
||||||
* @return True or false.
|
if (template.isDataSourceIngestModuleTemplate()) {
|
||||||
*/
|
DataSourceIngestModule ingestModule = template.createDataSourceIngestModule();
|
||||||
boolean isEmpty() {
|
module = Optional.of(new DataSourcePipelineModule(ingestModule, template.getModuleName()));
|
||||||
return modules.isEmpty();
|
}
|
||||||
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Starts up the ingest modules in this pipeline.
|
void prepareTask(DataSourceIngestTask task) {
|
||||||
*
|
|
||||||
* @return A list of ingest module startup errors, possibly empty.
|
|
||||||
*/
|
|
||||||
synchronized List<IngestModuleError> startUp() {
|
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
|
||||||
for (PipelineModule module : modules) {
|
|
||||||
try {
|
|
||||||
module.startUp(new IngestJobContext(this.ingestJobPipeline));
|
|
||||||
} catch (Throwable ex) { // Catch-all exception firewall
|
|
||||||
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Runs a data source through the ingest modules in sequential order.
|
void completeTask(DataSourceIngestTask task) {
|
||||||
*
|
|
||||||
* @param task A data source level ingest task containing a data source to
|
|
||||||
* be processed.
|
|
||||||
*
|
|
||||||
* @return A list of processing errors, possible empty.
|
|
||||||
*/
|
|
||||||
synchronized List<IngestModuleError> process(DataSourceIngestTask task) {
|
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
|
||||||
if (!this.ingestJobPipeline.isCancelled()) {
|
|
||||||
Content dataSource = task.getDataSource();
|
|
||||||
for (PipelineModule module : modules) {
|
|
||||||
try {
|
|
||||||
this.currentModule = module;
|
|
||||||
String displayName = NbBundle.getMessage(this.getClass(),
|
|
||||||
"IngestJob.progress.dataSourceIngest.displayName",
|
|
||||||
module.getDisplayName(), dataSource.getName());
|
|
||||||
this.ingestJobPipeline.updateDataSourceIngestProgressBarDisplayName(displayName);
|
|
||||||
this.ingestJobPipeline.switchDataSourceIngestProgressBarToIndeterminate();
|
|
||||||
DataSourceIngestPipeline.ingestManager.setIngestTaskProgress(task, module.getDisplayName());
|
|
||||||
logger.log(Level.INFO, "{0} analysis of {1} (pipeline={2}) starting", new Object[]{module.getDisplayName(), ingestJobPipeline.getDataSource().getName(), ingestJobPipeline.getId()}); //NON-NLS
|
|
||||||
module.process(dataSource, new DataSourceIngestModuleProgress(this.ingestJobPipeline));
|
|
||||||
logger.log(Level.INFO, "{0} analysis of {1} (pipeline={2}) finished", new Object[]{module.getDisplayName(), ingestJobPipeline.getDataSource().getName(), ingestJobPipeline.getId()}); //NON-NLS
|
|
||||||
} catch (Throwable ex) { // Catch-all exception firewall
|
|
||||||
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
|
||||||
}
|
|
||||||
if (this.ingestJobPipeline.isCancelled()) {
|
|
||||||
break;
|
|
||||||
} else if (this.ingestJobPipeline.currentDataSourceIngestModuleIsCancelled()) {
|
|
||||||
this.ingestJobPipeline.currentDataSourceIngestModuleCancellationCompleted(currentModule.getDisplayName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.currentModule = null;
|
|
||||||
ingestManager.setIngestTaskProgressCompleted(task);
|
ingestManager.setIngestTaskProgressCompleted(task);
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the currently running module.
|
* A wrapper that adds ingest infrastructure operations to a data source
|
||||||
*
|
* level ingest module.
|
||||||
* @return The module, possibly null if no module is currently running.
|
|
||||||
*/
|
*/
|
||||||
PipelineModule getCurrentlyRunningModule() {
|
static final class DataSourcePipelineModule extends IngestTaskPipeline.PipelineModule<DataSourceIngestTask> {
|
||||||
return this.currentModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class decorates a data source level ingest module with a display
|
|
||||||
* name and a processing start time.
|
|
||||||
*/
|
|
||||||
static class PipelineModule implements DataSourceIngestModule {
|
|
||||||
|
|
||||||
private final DataSourceIngestModule module;
|
private final DataSourceIngestModule module;
|
||||||
private final String displayName;
|
|
||||||
private volatile Date processingStartTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object that decorates a data source level ingest module
|
* Constructs a wrapper that adds ingest infrastructure operations to a
|
||||||
* with a display name and a processing start time.
|
* data source level ingest module.
|
||||||
*
|
|
||||||
* @param module The data source level ingest module to be
|
|
||||||
* decorated.
|
|
||||||
* @param displayName The display name.
|
|
||||||
*/
|
*/
|
||||||
PipelineModule(DataSourceIngestModule module, String displayName) {
|
DataSourcePipelineModule(DataSourceIngestModule module, String displayName) {
|
||||||
|
super(module, displayName);
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.displayName = displayName;
|
|
||||||
this.processingStartTime = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the class name of the decorated ingest module.
|
|
||||||
*
|
|
||||||
* @return The class name.
|
|
||||||
*/
|
|
||||||
String getClassName() {
|
|
||||||
return this.module.getClass().getCanonicalName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the display of the decorated ingest module.
|
|
||||||
*
|
|
||||||
* @return The display name.
|
|
||||||
*/
|
|
||||||
String getDisplayName() {
|
|
||||||
return this.displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the time the decorated ingest module started processing the data
|
|
||||||
* source.
|
|
||||||
*
|
|
||||||
* @return The start time, will be null if the module has not started
|
|
||||||
* processing the data source yet.
|
|
||||||
*/
|
|
||||||
Date getProcessingStartTime() {
|
|
||||||
return this.processingStartTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
void performTask(IngestJobPipeline ingestJobPipeline, DataSourceIngestTask task) throws IngestModuleException {
|
||||||
this.module.startUp(context);
|
Content dataSource = task.getDataSource();
|
||||||
}
|
String progressBarDisplayName = NbBundle.getMessage(this.getClass(), "IngestJob.progress.dataSourceIngest.displayName", getDisplayName(), dataSource.getName());
|
||||||
|
ingestJobPipeline.updateDataSourceIngestProgressBarDisplayName(progressBarDisplayName);
|
||||||
@Override
|
ingestJobPipeline.switchDataSourceIngestProgressBarToIndeterminate();
|
||||||
public IngestModule.ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
|
ingestManager.setIngestTaskProgress(task, getDisplayName());
|
||||||
this.processingStartTime = new Date();
|
logger.log(Level.INFO, "{0} analysis of {1} starting", new Object[]{getDisplayName(), dataSource.getName()}); //NON-NLS
|
||||||
return this.module.process(dataSource, statusHelper);
|
ProcessResult result = module.process(dataSource, new DataSourceIngestModuleProgress(ingestJobPipeline));
|
||||||
|
logger.log(Level.INFO, "{0} analysis of {1} finished", new Object[]{getDisplayName(), dataSource.getName()}); //NON-NLS
|
||||||
|
if (!ingestJobPipeline.isCancelled() && ingestJobPipeline.currentDataSourceIngestModuleIsCancelled()) {
|
||||||
|
ingestJobPipeline.currentDataSourceIngestModuleCancellationCompleted(getDisplayName());
|
||||||
|
}
|
||||||
|
if (result == ProcessResult.ERROR) {
|
||||||
|
throw new IngestModuleException(String.format("%s experienced an error analyzing %s (data source objId = %d)", getDisplayName(), dataSource.getName(), dataSource.getId())); //NON-NLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2014 Basis Technology Corp.
|
* Copyright 2014-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -36,13 +36,4 @@ public interface FileIngestModule extends IngestModule {
|
|||||||
*/
|
*/
|
||||||
ProcessResult process(AbstractFile file);
|
ProcessResult process(AbstractFile file);
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked by Autopsy when an ingest job is completed (either because the
|
|
||||||
* data has been analyzed or because the job was canceled - check
|
|
||||||
* IngestJobContext.fileIngestIsCancelled()), before the ingest module
|
|
||||||
* instance is discarded. The module should respond by doing things like
|
|
||||||
* releasing private resources, submitting final results, and posting a
|
|
||||||
* final ingest message.
|
|
||||||
*/
|
|
||||||
void shutDown();
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,10 @@ import org.sleuthkit.datamodel.AbstractFile;
|
|||||||
/**
|
/**
|
||||||
* An adapter that provides no-op implementations of the startUp() and
|
* An adapter that provides no-op implementations of the startUp() and
|
||||||
* shutDown() methods for file ingest modules.
|
* shutDown() methods for file ingest modules.
|
||||||
|
*
|
||||||
|
* NOTE: As of Java 8, interfaces can have default methods. FileIngestModule now
|
||||||
|
* provides default no-op versions of startUp() and shutDown(). This class is no
|
||||||
|
* longer needed and can be deprecated when convenient.
|
||||||
*/
|
*/
|
||||||
public abstract class FileIngestModuleAdapter implements FileIngestModule {
|
public abstract class FileIngestModuleAdapter implements FileIngestModule {
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2014-2015 Basis Technology Corp.
|
* Copyright 2014-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,223 +18,109 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.ingest;
|
package org.sleuthkit.autopsy.ingest;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class manages a sequence of file level ingest modules for an
|
* A pipeline of file ingest modules for performing file ingest tasks for an
|
||||||
* ingest job pipeline. It starts the modules, runs files through them, and shuts them
|
* ingest job.
|
||||||
* down when file level ingest is complete.
|
|
||||||
* <p>
|
|
||||||
* This class is thread-safe.
|
|
||||||
*/
|
*/
|
||||||
final class FileIngestPipeline {
|
final class FileIngestPipeline extends IngestTaskPipeline<FileIngestTask> {
|
||||||
|
|
||||||
private static final IngestManager ingestManager = IngestManager.getInstance();
|
private static final IngestManager ingestManager = IngestManager.getInstance();
|
||||||
private final IngestJobPipeline ingestJobPipeline;
|
private final IngestJobPipeline ingestJobPipeline;
|
||||||
private final List<PipelineModule> modules = new ArrayList<>();
|
|
||||||
private Date startTime;
|
|
||||||
private volatile boolean running;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object that manages a sequence of file level ingest
|
* Constructs a pipeline of file ingest modules for performing file ingest
|
||||||
* modules. It starts the modules, runs files through them, and shuts them
|
* tasks for an ingest job.
|
||||||
* down when file level ingest is complete.
|
|
||||||
*
|
*
|
||||||
* @param ingestJobPipeline The ingestJobPipeline that owns the pipeline.
|
* @param ingestJobPipeline The ingest job pipeline that owns this pipeline.
|
||||||
* @param moduleTemplates The ingest module templates that define the
|
* @param moduleTemplates The ingest module templates that define this
|
||||||
* pipeline.
|
* pipeline.
|
||||||
*/
|
*/
|
||||||
FileIngestPipeline(IngestJobPipeline ingestJobPipeline, List<IngestModuleTemplate> moduleTemplates) {
|
FileIngestPipeline(IngestJobPipeline ingestJobPipeline, List<IngestModuleTemplate> moduleTemplates) {
|
||||||
|
super(ingestJobPipeline, moduleTemplates);
|
||||||
this.ingestJobPipeline = ingestJobPipeline;
|
this.ingestJobPipeline = ingestJobPipeline;
|
||||||
for (IngestModuleTemplate template : moduleTemplates) {
|
}
|
||||||
if (template.isFileIngestModuleTemplate()) {
|
|
||||||
PipelineModule module = new PipelineModule(template.createFileIngestModule(), template.getModuleName());
|
@Override
|
||||||
modules.add(module);
|
Optional<IngestTaskPipeline.PipelineModule<FileIngestTask>> acceptModuleTemplate(IngestModuleTemplate template) {
|
||||||
}
|
Optional<IngestTaskPipeline.PipelineModule<FileIngestTask>> module = Optional.empty();
|
||||||
|
if (template.isFileIngestModuleTemplate()) {
|
||||||
|
FileIngestModule ingestModule = template.createFileIngestModule();
|
||||||
|
module = Optional.of(new FileIngestPipelineModule(ingestModule, template.getModuleName()));
|
||||||
}
|
}
|
||||||
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Queries whether or not there are any ingest modules in this pipeline.
|
void prepareTask(FileIngestTask task) throws IngestTaskPipelineException {
|
||||||
*
|
|
||||||
* @return True or false.
|
|
||||||
*/
|
|
||||||
boolean isEmpty() {
|
|
||||||
return this.modules.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Queries whether or not this pipeline is running.
|
void completeTask(FileIngestTask task) throws IngestTaskPipelineException {
|
||||||
*
|
AbstractFile file = null;
|
||||||
* @return True or false.
|
try {
|
||||||
*/
|
file = task.getFile();
|
||||||
boolean isRunning() {
|
} catch (TskCoreException ex) {
|
||||||
return this.running;
|
throw new IngestTaskPipelineException(String.format("Failed to get file (file objId = %d)", task.getFileId()), ex); //NON-NLS
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the start up time of this pipeline.
|
|
||||||
*
|
|
||||||
* @return The file processing start time, may be null if this pipeline has
|
|
||||||
* not been started yet.
|
|
||||||
*/
|
|
||||||
Date getStartTime() {
|
|
||||||
return this.startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts up all of the ingest modules in the pipeline.
|
|
||||||
*
|
|
||||||
* @return List of start up errors, possibly empty.
|
|
||||||
*/
|
|
||||||
synchronized List<IngestModuleError> startUp() {
|
|
||||||
this.startTime = new Date();
|
|
||||||
this.running = true;
|
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
|
||||||
for (PipelineModule module : this.modules) {
|
|
||||||
try {
|
|
||||||
module.startUp(new IngestJobContext(this.ingestJobPipeline));
|
|
||||||
} catch (Throwable ex) { // Catch-all exception firewall
|
|
||||||
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return errors;
|
try {
|
||||||
}
|
if (!ingestJobPipeline.isCancelled()) {
|
||||||
|
/*
|
||||||
/**
|
* Save any updates from the ingest modules to the case
|
||||||
* Runs a file through the ingest modules in sequential order.
|
* database.
|
||||||
*
|
*/
|
||||||
* @param task A file level ingest task containing a file to be processed.
|
file.save();
|
||||||
*
|
|
||||||
* @return A list of processing errors, possible empty.
|
|
||||||
*/
|
|
||||||
synchronized List<IngestModuleError> process(FileIngestTask task) {
|
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
|
||||||
if (!this.ingestJobPipeline.isCancelled()) {
|
|
||||||
AbstractFile file;
|
|
||||||
try {
|
|
||||||
file = task.getFile();
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
// In practice, this task would never have been enqueued since the file
|
|
||||||
// lookup would have failed there.
|
|
||||||
errors.add(new IngestModuleError("File Ingest Pipeline", ex)); // NON-NLS
|
|
||||||
FileIngestPipeline.ingestManager.setIngestTaskProgressCompleted(task);
|
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
for (PipelineModule module : this.modules) {
|
} catch (TskCoreException ex) {
|
||||||
try {
|
throw new IngestTaskPipelineException(String.format("Failed to save updated data for file (file objId = %d)", task.getFileId()), ex); //NON-NLS
|
||||||
FileIngestPipeline.ingestManager.setIngestTaskProgress(task, module.getDisplayName());
|
} finally {
|
||||||
this.ingestJobPipeline.setCurrentFileIngestModule(module.getDisplayName(), task.getFile().getName());
|
if (!ingestJobPipeline.isCancelled()) {
|
||||||
module.process(file);
|
|
||||||
} catch (Throwable ex) { // Catch-all exception firewall
|
|
||||||
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
|
||||||
}
|
|
||||||
if (this.ingestJobPipeline.isCancelled()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.ingestJobPipeline.isCancelled()) {
|
|
||||||
// Save any properties that have not already been saved to the database
|
|
||||||
try{
|
|
||||||
file.save();
|
|
||||||
} catch (TskCoreException ex){
|
|
||||||
Logger.getLogger(FileIngestPipeline.class.getName()).log(Level.SEVERE, "Failed to save data for file " + file.getId(), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
IngestManager.getInstance().fireFileIngestDone(file);
|
IngestManager.getInstance().fireFileIngestDone(file);
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
ingestManager.setIngestTaskProgressCompleted(task);
|
||||||
}
|
}
|
||||||
FileIngestPipeline.ingestManager.setIngestTaskProgressCompleted(task);
|
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down all of the modules in the pipeline.
|
* A wrapper that adds ingest infrastructure operations to a file ingest
|
||||||
*
|
* module.
|
||||||
* @return A list of shut down errors, possibly empty.
|
|
||||||
*/
|
*/
|
||||||
synchronized List<IngestModuleError> shutDown() {
|
static final class FileIngestPipelineModule extends IngestTaskPipeline.PipelineModule<FileIngestTask> {
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
|
||||||
if (this.running == true) { // Don't shut down pipelines that never started
|
|
||||||
for (PipelineModule module : this.modules) {
|
|
||||||
try {
|
|
||||||
module.shutDown();
|
|
||||||
} catch (Throwable ex) { // Catch-all exception firewall
|
|
||||||
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
|
||||||
String msg = ex.getMessage();
|
|
||||||
// Jython run-time errors don't seem to have a message, but have details in toString.
|
|
||||||
if (msg == null) {
|
|
||||||
msg = ex.toString();
|
|
||||||
}
|
|
||||||
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "FileIngestPipeline.moduleError.title.text", module.getDisplayName()), msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.running = false;
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class decorates a file level ingest module with a display name.
|
|
||||||
*/
|
|
||||||
private static final class PipelineModule implements FileIngestModule {
|
|
||||||
|
|
||||||
private final FileIngestModule module;
|
private final FileIngestModule module;
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object that decorates a file level ingest module with a
|
* Constructs a wrapper that adds ingest infrastructure operations to a
|
||||||
* display name.
|
* file ingest module.
|
||||||
*
|
*
|
||||||
* @param module The file level ingest module to be decorated.
|
*
|
||||||
* @param displayName The display name.
|
* @param module The module.
|
||||||
|
* @param displayName The display name of the module.
|
||||||
*/
|
*/
|
||||||
PipelineModule(FileIngestModule module, String displayName) {
|
FileIngestPipelineModule(FileIngestModule module, String displayName) {
|
||||||
|
super(module, displayName);
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the class name of the decorated ingest module.
|
|
||||||
*
|
|
||||||
* @return The class name.
|
|
||||||
*/
|
|
||||||
String getClassName() {
|
|
||||||
return module.getClass().getCanonicalName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the display name of the decorated ingest module.
|
|
||||||
*
|
|
||||||
* @return The display name.
|
|
||||||
*/
|
|
||||||
String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
void performTask(IngestJobPipeline ingestJobPipeline, FileIngestTask task) throws IngestModuleException {
|
||||||
module.startUp(context);
|
AbstractFile file = null;
|
||||||
}
|
try {
|
||||||
|
file = task.getFile();
|
||||||
@Override
|
} catch (TskCoreException ex) {
|
||||||
public IngestModule.ProcessResult process(AbstractFile file) {
|
throw new IngestModuleException(String.format("Failed to get file (file objId = %d)", task.getFileId()), ex); //NON-NLS
|
||||||
return module.process(file);
|
}
|
||||||
}
|
ingestManager.setIngestTaskProgress(task, getDisplayName());
|
||||||
|
ingestJobPipeline.setCurrentFileIngestModule(getDisplayName(), file.getName());
|
||||||
@Override
|
ProcessResult result = module.process(file);
|
||||||
public void shutDown() {
|
if (result == ProcessResult.ERROR) {
|
||||||
module.shutDown();
|
throw new IngestModuleException(String.format("%s experienced an error analyzing %s (file objId = %d)", getDisplayName(), file.getName(), file.getId())); //NON-NLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -416,7 +416,7 @@ public final class IngestJob {
|
|||||||
Snapshot snapshot = pipeline.getSnapshot(getIngestTasksSnapshot);
|
Snapshot snapshot = pipeline.getSnapshot(getIngestTasksSnapshot);
|
||||||
dataSourceProcessingSnapshots.add(new DataSourceProcessingSnapshot(snapshot));
|
dataSourceProcessingSnapshots.add(new DataSourceProcessingSnapshot(snapshot));
|
||||||
if (null == dataSourceModule) {
|
if (null == dataSourceModule) {
|
||||||
DataSourceIngestPipeline.PipelineModule module = snapshot.getDataSourceLevelIngestModule();
|
DataSourceIngestPipeline.DataSourcePipelineModule module = snapshot.getDataSourceLevelIngestModule();
|
||||||
if (null != module) {
|
if (null != module) {
|
||||||
dataSourceModule = new DataSourceIngestModuleHandle(ingestJobPipelines.get(snapshot.getJobId()), module);
|
dataSourceModule = new DataSourceIngestModuleHandle(ingestJobPipelines.get(snapshot.getJobId()), module);
|
||||||
}
|
}
|
||||||
@ -500,7 +500,7 @@ public final class IngestJob {
|
|||||||
public static class DataSourceIngestModuleHandle {
|
public static class DataSourceIngestModuleHandle {
|
||||||
|
|
||||||
private final IngestJobPipeline ingestJobPipeline;
|
private final IngestJobPipeline ingestJobPipeline;
|
||||||
private final DataSourceIngestPipeline.PipelineModule module;
|
private final DataSourceIngestPipeline.DataSourcePipelineModule module;
|
||||||
private final boolean cancelled;
|
private final boolean cancelled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -511,7 +511,7 @@ public final class IngestJob {
|
|||||||
* @param ingestJobPipeline The ingestJobPipeline that owns the data source level ingest module.
|
* @param ingestJobPipeline The ingestJobPipeline that owns the data source level ingest module.
|
||||||
* @param module The data source level ingest module.
|
* @param module The data source level ingest module.
|
||||||
*/
|
*/
|
||||||
private DataSourceIngestModuleHandle(IngestJobPipeline ingestJobPipeline, DataSourceIngestPipeline.PipelineModule module) {
|
private DataSourceIngestModuleHandle(IngestJobPipeline ingestJobPipeline, DataSourceIngestPipeline.DataSourcePipelineModule module) {
|
||||||
this.ingestJobPipeline = ingestJobPipeline;
|
this.ingestJobPipeline = ingestJobPipeline;
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.cancelled = ingestJobPipeline.currentDataSourceIngestModuleIsCancelled();
|
this.cancelled = ingestJobPipeline.currentDataSourceIngestModuleIsCancelled();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2014-2019 Basis Technology Corp.
|
* Copyright 2014-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -553,7 +553,7 @@ final class IngestJobPipeline {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the data-source-level ingest pipelines were successfully started,
|
* If the data-source-level ingest pipelines were successfully started,
|
||||||
* start the Start the file-level ingest pipelines (one per file ingest
|
* start the file-level ingest pipelines (one per pipeline file ingest
|
||||||
* thread).
|
* thread).
|
||||||
*/
|
*/
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
@ -940,7 +940,7 @@ final class IngestJobPipeline {
|
|||||||
synchronized (this.dataSourceIngestPipelineLock) {
|
synchronized (this.dataSourceIngestPipelineLock) {
|
||||||
if (!this.isCancelled() && !this.currentDataSourceIngestPipeline.isEmpty()) {
|
if (!this.isCancelled() && !this.currentDataSourceIngestPipeline.isEmpty()) {
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
List<IngestModuleError> errors = new ArrayList<>();
|
||||||
errors.addAll(this.currentDataSourceIngestPipeline.process(task));
|
errors.addAll(this.currentDataSourceIngestPipeline.performTask(task));
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
logIngestModuleErrors(errors);
|
logIngestModuleErrors(errors);
|
||||||
}
|
}
|
||||||
@ -1014,7 +1014,7 @@ final class IngestJobPipeline {
|
|||||||
* Run the file through the pipeline.
|
* Run the file through the pipeline.
|
||||||
*/
|
*/
|
||||||
List<IngestModuleError> errors = new ArrayList<>();
|
List<IngestModuleError> errors = new ArrayList<>();
|
||||||
errors.addAll(pipeline.process(task));
|
errors.addAll(pipeline.performTask(task));
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
logIngestModuleErrors(errors, file);
|
logIngestModuleErrors(errors, file);
|
||||||
}
|
}
|
||||||
@ -1232,9 +1232,9 @@ final class IngestJobPipeline {
|
|||||||
*
|
*
|
||||||
* @return The currently running module, may be null.
|
* @return The currently running module, may be null.
|
||||||
*/
|
*/
|
||||||
DataSourceIngestPipeline.PipelineModule getCurrentDataSourceIngestModule() {
|
DataSourceIngestPipeline.DataSourcePipelineModule getCurrentDataSourceIngestModule() {
|
||||||
if (null != this.currentDataSourceIngestPipeline) {
|
if (null != currentDataSourceIngestPipeline) {
|
||||||
return this.currentDataSourceIngestPipeline.getCurrentlyRunningModule();
|
return (DataSourceIngestPipeline.DataSourcePipelineModule) currentDataSourceIngestPipeline.getCurrentlyRunningModule();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1358,7 +1358,7 @@ final class IngestJobPipeline {
|
|||||||
* Write ingest module errors to the log.
|
* Write ingest module errors to the log.
|
||||||
*
|
*
|
||||||
* @param errors The errors.
|
* @param errors The errors.
|
||||||
* @param file AbstractFile that caused the errors.
|
* @param file AbstractFile that caused the errors.
|
||||||
*/
|
*/
|
||||||
private void logIngestModuleErrors(List<IngestModuleError> errors, AbstractFile file) {
|
private void logIngestModuleErrors(List<IngestModuleError> errors, AbstractFile file) {
|
||||||
for (IngestModuleError error : errors) {
|
for (IngestModuleError error : errors) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2019 Basis Technology Corp.
|
* Copyright 2012-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2014 Basis Technology Corp.
|
* Copyright 2014-2021 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -25,9 +25,8 @@ package org.sleuthkit.autopsy.ingest;
|
|||||||
* ingest job it performs (one for each thread that it is using).
|
* ingest job it performs (one for each thread that it is using).
|
||||||
*
|
*
|
||||||
* Autopsy will call startUp() before any data is processed, will pass any data
|
* Autopsy will call startUp() before any data is processed, will pass any data
|
||||||
* to be analyzed into the process() method (FileIngestModule.process() or
|
* to be analyzed into the process() method, and call shutDown() after either
|
||||||
* DataSourceIngestModule.process()), and call shutDown() after either all data
|
* all data is analyzed or the user has canceled the job.
|
||||||
* is analyzed or the user has canceled the job.
|
|
||||||
*
|
*
|
||||||
* Autopsy may use multiple threads to complete an ingest job, but it is
|
* Autopsy may use multiple threads to complete an ingest job, but it is
|
||||||
* guaranteed that a module instance will always be called from a single thread.
|
* guaranteed that a module instance will always be called from a single thread.
|
||||||
@ -47,25 +46,53 @@ package org.sleuthkit.autopsy.ingest;
|
|||||||
public interface IngestModule {
|
public interface IngestModule {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A return code for derived class process() methods.
|
* Invoked by Autopsy to allow an ingest module instance to set up any
|
||||||
|
* internal data structures and acquire any private resources it will need
|
||||||
|
* during an ingest job. If the module depends on loading any resources, it
|
||||||
|
* should do so in this method so that it can throw an exception in the case
|
||||||
|
* of an error and alert the user. Exceptions that are thrown from startUp()
|
||||||
|
* are logged and stop processing of the data source.
|
||||||
|
*
|
||||||
|
* IMPORTANT: If the module instances must share resources, the modules are
|
||||||
|
* responsible for synchronizing access to the shared resources and doing
|
||||||
|
* reference counting as required to release those resources correctly.
|
||||||
|
* Also, more than one ingest job may be in progress at any given time. This
|
||||||
|
* must also be taken into consideration when sharing resources between
|
||||||
|
* module instances. See IngestModuleReferenceCounter.
|
||||||
|
*
|
||||||
|
* @param context Provides data and services specific to the ingest job and
|
||||||
|
* the ingest pipeline of which the module is a part.
|
||||||
|
*
|
||||||
|
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
|
||||||
*/
|
*/
|
||||||
public enum ProcessResult {
|
default void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
|
}
|
||||||
OK,
|
|
||||||
ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom exception for the use of ingest modules.
|
* Invoked by Autopsy when an ingest job is completed (either because the
|
||||||
|
* data has been analyzed or because the job was cancelled), before the
|
||||||
|
* ingest module instance is discarded. The module should respond by doing
|
||||||
|
* things like releasing private resources, submitting final results, and
|
||||||
|
* posting a final ingest message.
|
||||||
|
*
|
||||||
|
* IMPORTANT: If the module instances must share resources, the modules are
|
||||||
|
* responsible for synchronizing access to the shared resources and doing
|
||||||
|
* reference counting as required to release those resources correctly.
|
||||||
|
* Also, more than one ingest job may be in progress at any given time. This
|
||||||
|
* must also be taken into consideration when sharing resources between
|
||||||
|
* module instances. See IngestModuleReferenceCounter.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
default void shutDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception for the use of ingest modules.
|
||||||
*/
|
*/
|
||||||
public class IngestModuleException extends Exception {
|
public class IngestModuleException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public IngestModuleException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public IngestModuleException(String message) {
|
public IngestModuleException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
@ -73,26 +100,21 @@ public interface IngestModule {
|
|||||||
public IngestModuleException(String message, Throwable cause) {
|
public IngestModuleException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public IngestModuleException() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked by Autopsy to allow an ingest module instance to set up any
|
* A return code for subclass process() methods.
|
||||||
* internal data structures and acquire any private resources it will need
|
|
||||||
* during an ingest job. If the module depends on loading any resources, it
|
|
||||||
* should do so in this method so that it can throw an exception in the case
|
|
||||||
* of an error and alert the user. Exceptions that are thrown from process()
|
|
||||||
* and shutDown() are logged, but do not stop processing of the data source.
|
|
||||||
*
|
|
||||||
* @param context Provides data and services specific to the ingest job and
|
|
||||||
* the ingest pipeline of which the module is a part.
|
|
||||||
*
|
|
||||||
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
|
|
||||||
*/
|
*/
|
||||||
void startUp(IngestJobContext context) throws IngestModuleException;
|
public enum ProcessResult {
|
||||||
|
|
||||||
|
OK,
|
||||||
|
ERROR
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: The next time an API change is legal, add a cancel() method and
|
|
||||||
* remove the "ingest job is canceled" queries from the IngestJobContext
|
|
||||||
* class.
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
345
Core/src/org/sleuthkit/autopsy/ingest/IngestTaskPipeline.java
Executable file
345
Core/src/org/sleuthkit/autopsy/ingest/IngestTaskPipeline.java
Executable file
@ -0,0 +1,345 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.ingest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract superclass for pipelines of ingest modules for a given ingest
|
||||||
|
* task type. Some examples of ingest task types: data source level ingest
|
||||||
|
* tasks, file ingest tasks, data artifact ingest tasks, etc. Subclasses need to
|
||||||
|
* implement a specialization of the inner PipelineModule abstract superclass
|
||||||
|
* for the type of ingest modules that make up the pipeline.
|
||||||
|
*
|
||||||
|
* @param <T> The ingest task type.
|
||||||
|
*/
|
||||||
|
abstract class IngestTaskPipeline<T extends IngestTask> {
|
||||||
|
|
||||||
|
private static final IngestManager ingestManager = IngestManager.getInstance();
|
||||||
|
private final IngestJobPipeline ingestJobPipeline;
|
||||||
|
private final List<IngestModuleTemplate> moduleTemplates;
|
||||||
|
private final List<PipelineModule<T>> modules;
|
||||||
|
private volatile Date startTime;
|
||||||
|
private volatile boolean running;
|
||||||
|
private volatile PipelineModule<T> currentModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of an abstract superclass for pipelines of ingest
|
||||||
|
* modules for a given ingest task type. Some examples of ingest task types:
|
||||||
|
* data source level ingest tasks, file ingest tasks, data artifact ingest
|
||||||
|
* tasks, etc. Subclasses need to implement a specialization of the inner
|
||||||
|
* PipelineModule abstract superclass for the type of ingest modules that
|
||||||
|
* make up the pipeline.
|
||||||
|
*
|
||||||
|
* @param ingestJobPipeline The ingest job pipeline that owns this pipeline.
|
||||||
|
* @param moduleTemplates The ingest module templates that define this
|
||||||
|
* pipeline.
|
||||||
|
*/
|
||||||
|
IngestTaskPipeline(IngestJobPipeline ingestJobPipeline, List<IngestModuleTemplate> moduleTemplates) {
|
||||||
|
this.ingestJobPipeline = ingestJobPipeline;
|
||||||
|
this.moduleTemplates = moduleTemplates;
|
||||||
|
modules = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not there are any ingest modules in this pipeline.
|
||||||
|
*
|
||||||
|
* @return True or false.
|
||||||
|
*/
|
||||||
|
boolean isEmpty() {
|
||||||
|
return modules.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries whether or not this pipeline is running, i.e., started and not
|
||||||
|
* shut down.
|
||||||
|
*
|
||||||
|
* @return True or false.
|
||||||
|
*/
|
||||||
|
boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts up the ingest modules in this pipeline.
|
||||||
|
*
|
||||||
|
* @return A list of ingest module startup errors, possibly empty.
|
||||||
|
*/
|
||||||
|
List<IngestModuleError> startUp() {
|
||||||
|
createIngestModules(moduleTemplates);
|
||||||
|
return startUpIngestModules();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the ingest modules for this pipeline.
|
||||||
|
*
|
||||||
|
* @param moduleTemplates The ingest module templates avaialble to this
|
||||||
|
* pipeline.
|
||||||
|
*/
|
||||||
|
private void createIngestModules(List<IngestModuleTemplate> moduleTemplates) {
|
||||||
|
for (IngestModuleTemplate template : moduleTemplates) {
|
||||||
|
Optional<PipelineModule<T>> module = acceptModuleTemplate(template);
|
||||||
|
if (module.isPresent()) {
|
||||||
|
modules.add(module.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the type of ingest module that can be created from a given
|
||||||
|
* ingest module template should be added to this pipeline. If so, the
|
||||||
|
* ingest module is created and returned.
|
||||||
|
*
|
||||||
|
* @param ingestModuleTemplate The ingest module template to be used or
|
||||||
|
* ignored, as appropriate to the pipeline type.
|
||||||
|
*
|
||||||
|
* @return An Optional that is either empty or contains a newly created and
|
||||||
|
* wrapped ingest module.
|
||||||
|
*/
|
||||||
|
abstract Optional<PipelineModule<T>> acceptModuleTemplate(IngestModuleTemplate ingestModuleTemplate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts up the ingest modules in the pipeline.
|
||||||
|
*
|
||||||
|
* @return A list of ingest module startup errors, possibly empty.
|
||||||
|
*/
|
||||||
|
private List<IngestModuleError> startUpIngestModules() {
|
||||||
|
startTime = new Date();
|
||||||
|
running = true;
|
||||||
|
List<IngestModuleError> errors = new ArrayList<>();
|
||||||
|
for (PipelineModule<T> module : modules) {
|
||||||
|
try {
|
||||||
|
module.startUp(new IngestJobContext(ingestJobPipeline));
|
||||||
|
} catch (Throwable ex) { // Catch-all exception firewall
|
||||||
|
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start up time of this pipeline.
|
||||||
|
*
|
||||||
|
* @return The file processing start time, may be null if this pipeline has
|
||||||
|
* not been started yet.
|
||||||
|
*/
|
||||||
|
Date getStartTime() {
|
||||||
|
if (startTime == null) {
|
||||||
|
throw new IllegalStateException("startUp() was not called"); //NON-NLS
|
||||||
|
}
|
||||||
|
return new Date(startTime.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does any preparation required before performing a task.
|
||||||
|
*
|
||||||
|
* @param task The task.
|
||||||
|
*
|
||||||
|
* @throws IngestTaskPipelineException Thrown if there is an error preparing
|
||||||
|
* to perform the task.
|
||||||
|
*/
|
||||||
|
abstract void prepareTask(T task) throws IngestTaskPipelineException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an ingest task using the ingest modules in this pipeline.
|
||||||
|
*
|
||||||
|
* @param task The task.
|
||||||
|
*
|
||||||
|
* @return A list of ingest module task processing errors, possibly empty.
|
||||||
|
*/
|
||||||
|
List<IngestModuleError> performTask(T task) {
|
||||||
|
List<IngestModuleError> errors = new ArrayList<>();
|
||||||
|
if (!this.ingestJobPipeline.isCancelled()) {
|
||||||
|
try {
|
||||||
|
prepareTask(task);
|
||||||
|
} catch (IngestTaskPipelineException ex) {
|
||||||
|
errors.add(new IngestModuleError("Ingest Task Pipeline", ex)); //NON-NLS
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
for (PipelineModule<T> module : modules) {
|
||||||
|
try {
|
||||||
|
currentModule = module;
|
||||||
|
currentModule.setProcessingStartTime();
|
||||||
|
module.performTask(ingestJobPipeline, task);
|
||||||
|
} catch (Throwable ex) { // Catch-all exception firewall
|
||||||
|
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
||||||
|
}
|
||||||
|
if (ingestJobPipeline.isCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
completeTask(task);
|
||||||
|
} catch (IngestTaskPipelineException ex) {
|
||||||
|
errors.add(new IngestModuleError("Ingest Task Pipeline", ex)); //NON-NLS
|
||||||
|
}
|
||||||
|
currentModule = null;
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently running module.
|
||||||
|
*
|
||||||
|
* @return The module, possibly null if no module is currently running.
|
||||||
|
*/
|
||||||
|
PipelineModule<T> getCurrentlyRunningModule() {
|
||||||
|
return currentModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does any clean up required after performing a task.
|
||||||
|
*
|
||||||
|
* @param task The task.
|
||||||
|
*
|
||||||
|
* @throws IngestTaskPipelineException Thrown if there is an error cleaning
|
||||||
|
* up after performing the task.
|
||||||
|
*/
|
||||||
|
abstract void completeTask(T task) throws IngestTaskPipelineException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down all of the modules in the pipeline.
|
||||||
|
*
|
||||||
|
* @return A list of shut down errors, possibly empty.
|
||||||
|
*/
|
||||||
|
List<IngestModuleError> shutDown() {
|
||||||
|
List<IngestModuleError> errors = new ArrayList<>();
|
||||||
|
if (running == true) {
|
||||||
|
for (PipelineModule<T> module : modules) {
|
||||||
|
try {
|
||||||
|
module.shutDown();
|
||||||
|
} catch (Throwable ex) { // Catch-all exception firewall
|
||||||
|
errors.add(new IngestModuleError(module.getDisplayName(), ex));
|
||||||
|
String msg = ex.getMessage();
|
||||||
|
if (msg == null) {
|
||||||
|
/*
|
||||||
|
* Jython run-time errors don't seem to have a message,
|
||||||
|
* but have details in the string returned by
|
||||||
|
* toString().
|
||||||
|
*/
|
||||||
|
msg = ex.toString();
|
||||||
|
}
|
||||||
|
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "FileIngestPipeline.moduleError.title.text", module.getDisplayName()), msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
running = false;
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract superclass for a wrapper that adds ingest infrastructure
|
||||||
|
* operations to an ingest module.
|
||||||
|
*/
|
||||||
|
static abstract class PipelineModule<T extends IngestTask> implements IngestModule {
|
||||||
|
|
||||||
|
private final IngestModule module;
|
||||||
|
private final String displayName;
|
||||||
|
private volatile Date processingStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of an abstract superclass for a wrapper that
|
||||||
|
* adds ingest infrastructure operations to an ingest module.
|
||||||
|
*
|
||||||
|
* @param module The ingest module to be wrapped.
|
||||||
|
* @param displayName The display name for the module.
|
||||||
|
*/
|
||||||
|
PipelineModule(IngestModule module, String displayName) {
|
||||||
|
this.module = module;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.processingStartTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class name of the wrapped ingest module.
|
||||||
|
*
|
||||||
|
* @return The class name.
|
||||||
|
*/
|
||||||
|
String getClassName() {
|
||||||
|
return module.getClass().getCanonicalName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display name of the wrapped ingest module.
|
||||||
|
*
|
||||||
|
* @return The display name.
|
||||||
|
*/
|
||||||
|
String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the processing start time for the wrapped module to the system
|
||||||
|
* time when this method is called.
|
||||||
|
*/
|
||||||
|
void setProcessingStartTime() {
|
||||||
|
processingStartTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the the processing start time for the wrapped module.
|
||||||
|
*
|
||||||
|
* @return The start time, will be null if the module has not started
|
||||||
|
* processing the data source yet.
|
||||||
|
*/
|
||||||
|
Date getProcessingStartTime() {
|
||||||
|
return new Date(processingStartTime.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
|
module.startUp(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an ingest task.
|
||||||
|
*
|
||||||
|
* @param ingestJobPipeline The ingest job pipeline that owns the ingest
|
||||||
|
* module pipeline this module belongs to.
|
||||||
|
* @param task The task to process.
|
||||||
|
*
|
||||||
|
* @throws IngestModuleException Excepton thrown if there is an error
|
||||||
|
* performing the task.
|
||||||
|
*/
|
||||||
|
abstract void performTask(IngestJobPipeline ingestJobPipeline, T task) throws IngestModuleException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception for the use of ingest task pipelines.
|
||||||
|
*/
|
||||||
|
public static class IngestTaskPipelineException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public IngestTaskPipelineException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IngestTaskPipelineException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,7 +34,7 @@ public final class Snapshot implements Serializable {
|
|||||||
private final long jobId;
|
private final long jobId;
|
||||||
private final long jobStartTime;
|
private final long jobStartTime;
|
||||||
private final long snapShotTime;
|
private final long snapShotTime;
|
||||||
transient private final DataSourceIngestPipeline.PipelineModule dataSourceLevelIngestModule;
|
transient private final DataSourceIngestPipeline.DataSourcePipelineModule dataSourceLevelIngestModule;
|
||||||
private final boolean fileIngestRunning;
|
private final boolean fileIngestRunning;
|
||||||
private final Date fileIngestStartTime;
|
private final Date fileIngestStartTime;
|
||||||
private final long processedFiles;
|
private final long processedFiles;
|
||||||
@ -48,7 +48,7 @@ public final class Snapshot implements Serializable {
|
|||||||
* Constructs an object to store basic diagnostic statistics for a data
|
* Constructs an object to store basic diagnostic statistics for a data
|
||||||
* source ingest job.
|
* source ingest job.
|
||||||
*/
|
*/
|
||||||
Snapshot(String dataSourceName, long jobId, long jobStartTime, DataSourceIngestPipeline.PipelineModule dataSourceIngestModule,
|
Snapshot(String dataSourceName, long jobId, long jobStartTime, DataSourceIngestPipeline.DataSourcePipelineModule dataSourceIngestModule,
|
||||||
boolean fileIngestRunning, Date fileIngestStartTime,
|
boolean fileIngestRunning, Date fileIngestStartTime,
|
||||||
boolean jobCancelled, IngestJob.CancellationReason cancellationReason, List<String> cancelledModules,
|
boolean jobCancelled, IngestJob.CancellationReason cancellationReason, List<String> cancelledModules,
|
||||||
long processedFiles, long estimatedFilesToProcess,
|
long processedFiles, long estimatedFilesToProcess,
|
||||||
@ -110,7 +110,7 @@ public final class Snapshot implements Serializable {
|
|||||||
return jobStartTime;
|
return jobStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourceIngestPipeline.PipelineModule getDataSourceLevelIngestModule() {
|
DataSourceIngestPipeline.DataSourcePipelineModule getDataSourceLevelIngestModule() {
|
||||||
return this.dataSourceLevelIngestModule;
|
return this.dataSourceLevelIngestModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,16 +161,6 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - set to value from config panel
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("LogicalImagerDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
// It's not worth adding a logger for temporary code
|
|
||||||
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path imageDirPath = configPanel.getImageDirPath();
|
Path imageDirPath = configPanel.getImageDirPath();
|
||||||
List<String> errorList = new ArrayList<>();
|
List<String> errorList = new ArrayList<>();
|
||||||
List<Content> emptyDataSources = new ArrayList<>();
|
List<Content> emptyDataSources = new ArrayList<>();
|
||||||
|
@ -140,15 +140,6 @@ public class MemoryDSProcessor implements DataSourceProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
|
|
||||||
// HOSTTODO - replace with a call to configPanel().getHost()
|
|
||||||
try {
|
|
||||||
host = Case.getCurrentCase().getSleuthkitCase().getHostManager().getOrCreateHost("MemoryDSProcessor Host");
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
// It's not worth adding a logger for temporary code
|
|
||||||
//logger.log(Level.SEVERE, "Error creating/loading host", ex);
|
|
||||||
host = null;
|
|
||||||
}
|
|
||||||
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getProfile(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), host, progressMonitor, callback);
|
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getProfile(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), host, progressMonitor, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,9 @@ import java.util.Scanner;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import static java.util.Locale.US;
|
import static java.util.Locale.US;
|
||||||
|
import java.util.Optional;
|
||||||
import static java.util.TimeZone.getTimeZone;
|
import static java.util.TimeZone.getTimeZone;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
@ -93,10 +95,19 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAM
|
|||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HOME_DIR;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
import org.sleuthkit.datamodel.Host;
|
||||||
|
import org.sleuthkit.datamodel.HostManager;
|
||||||
|
import org.sleuthkit.datamodel.OsAccount;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountAttribute;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountManager;
|
||||||
|
import org.sleuthkit.datamodel.OsAccountRealm;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||||
import org.sleuthkit.datamodel.Report;
|
import org.sleuthkit.datamodel.Report;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.TskDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract windows registry data using regripper. Runs two versions of
|
* Extract windows registry data using regripper. Runs two versions of
|
||||||
@ -173,10 +184,16 @@ class ExtractRegistry extends Extract {
|
|||||||
private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS
|
private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS
|
||||||
private static final String SHELLBAG_ATTRIBUTE_KEY = "RA_SHELL_BAG_KEY"; //NON-NLS
|
private static final String SHELLBAG_ATTRIBUTE_KEY = "RA_SHELL_BAG_KEY"; //NON-NLS
|
||||||
|
|
||||||
|
private static final SimpleDateFormat REG_RIPPER_TIME_FORMAT = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
|
||||||
|
|
||||||
BlackboardArtifact.Type shellBagArtifactType = null;
|
BlackboardArtifact.Type shellBagArtifactType = null;
|
||||||
BlackboardAttribute.Type shellBagKeyAttributeType = null;
|
BlackboardAttribute.Type shellBagKeyAttributeType = null;
|
||||||
BlackboardAttribute.Type shellBagLastWriteAttributeType = null;
|
BlackboardAttribute.Type shellBagLastWriteAttributeType = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
REG_RIPPER_TIME_FORMAT.setTimeZone(getTimeZone("GMT"));
|
||||||
|
}
|
||||||
|
|
||||||
ExtractRegistry() throws IngestModuleException {
|
ExtractRegistry() throws IngestModuleException {
|
||||||
moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text");
|
moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text");
|
||||||
|
|
||||||
@ -849,56 +866,66 @@ class ExtractRegistry extends Extract {
|
|||||||
|
|
||||||
case "ProfileList": //NON-NLS
|
case "ProfileList": //NON-NLS
|
||||||
try {
|
try {
|
||||||
String homeDir = value;
|
String homeDir = value;
|
||||||
String sid = artnode.getAttribute("sid"); //NON-NLS
|
String sid = artnode.getAttribute("sid"); //NON-NLS
|
||||||
String username = artnode.getAttribute("username"); //NON-NLS
|
String username = artnode.getAttribute("username"); //NON-NLS
|
||||||
BlackboardArtifact bbart = null;
|
|
||||||
try {
|
// For now both an OsAccount and the
|
||||||
//check if any of the existing artifacts match this username
|
// TSK_OS_ACCOUNT artifact will be created.
|
||||||
ArrayList<BlackboardArtifact> existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
try{
|
||||||
for (BlackboardArtifact artifact : existingArtifacts) {
|
createOrUpdateOsAccount(regFile, sid, username, homeDir);
|
||||||
if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) {
|
|
||||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID));
|
} catch(TskCoreException | TskDataException ex) {
|
||||||
if (attribute != null && attribute.getValueString().equals(sid)) {
|
logger.log(Level.SEVERE, String.format("Failed to create OsAccount for file: %s, sid: %s", regFile.getId(), sid));
|
||||||
bbart = artifact;
|
}
|
||||||
break;
|
|
||||||
|
BlackboardArtifact bbart = null;
|
||||||
|
try {
|
||||||
|
//check if any of the existing artifacts match this username
|
||||||
|
ArrayList<BlackboardArtifact> existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||||
|
for (BlackboardArtifact artifact : existingArtifacts) {
|
||||||
|
if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) {
|
||||||
|
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID));
|
||||||
|
if (attribute != null && attribute.getValueString().equals(sid)) {
|
||||||
|
bbart = artifact;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error getting existing os account artifact", ex);
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
if (bbart == null) {
|
||||||
logger.log(Level.SEVERE, "Error getting existing os account artifact", ex);
|
//create new artifact
|
||||||
}
|
bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||||
if (bbart == null) {
|
|
||||||
//create new artifact
|
|
||||||
bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
|
||||||
parentModuleName, username));
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
|
||||||
parentModuleName, sid));
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
|
||||||
parentModuleName, homeDir));
|
|
||||||
|
|
||||||
newArtifacts.add(bbart);
|
|
||||||
} else {
|
|
||||||
//add attributes to existing artifact
|
|
||||||
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
|
|
||||||
|
|
||||||
if (bbattr == null) {
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||||
parentModuleName, username));
|
parentModuleName, username));
|
||||||
}
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
||||||
bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH));
|
parentModuleName, sid));
|
||||||
if (bbattr == null) {
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||||
parentModuleName, homeDir));
|
parentModuleName, homeDir));
|
||||||
}
|
|
||||||
}
|
|
||||||
bbart.addAttributes(bbattributes);
|
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
newArtifacts.add(bbart);
|
||||||
logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS
|
} else {
|
||||||
}
|
//add attributes to existing artifact
|
||||||
break;
|
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
|
||||||
|
|
||||||
|
if (bbattr == null) {
|
||||||
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||||
|
parentModuleName, username));
|
||||||
|
}
|
||||||
|
bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH));
|
||||||
|
if (bbattr == null) {
|
||||||
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||||
|
parentModuleName, homeDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bbart.addAttributes(bbattributes);
|
||||||
|
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "NtuserNetwork": // NON-NLS
|
case "NtuserNetwork": // NON-NLS
|
||||||
try {
|
try {
|
||||||
@ -1116,6 +1143,34 @@ class ExtractRegistry extends Extract {
|
|||||||
for (Map<String, String> userInfo : userSet) {
|
for (Map<String, String> userInfo : userSet) {
|
||||||
userInfoMap.put(userInfo.get(SID_KEY), userInfo);
|
userInfoMap.put(userInfo.get(SID_KEY), userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New OsAccount Code
|
||||||
|
OsAccountManager accountMgr = tskCase.getOsAccountManager();
|
||||||
|
HostManager hostMrg = tskCase.getHostManager();
|
||||||
|
Host host = hostMrg.getHost((DataSource)dataSource);
|
||||||
|
|
||||||
|
List<OsAccount> existingAccounts = accountMgr.getAccounts(host);
|
||||||
|
for(OsAccount osAccount: existingAccounts) {
|
||||||
|
Optional<String> optional = osAccount.getUniqueIdWithinRealm();
|
||||||
|
if(!optional.isPresent()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sid = optional.get();
|
||||||
|
Map<String, String> userInfo = userInfoMap.remove(sid);
|
||||||
|
if(userInfo != null) {
|
||||||
|
updateOsAccount(osAccount, userInfo, groupMap.get(sid), regAbstractFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add remaining userinfos as accounts;
|
||||||
|
for (Map<String, String> userInfo : userInfoMap.values()) {
|
||||||
|
OsAccount osAccount = accountMgr.createWindowsAccount(userInfo.get(SID_KEY), null, null, host, OsAccountRealm.RealmScope.UNKNOWN);
|
||||||
|
updateOsAccount(osAccount, userInfo, groupMap.get(userInfo.get(SID_KEY)), regAbstractFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing TSK_OS_ACCOUNT code.
|
||||||
|
|
||||||
//get all existing OS account artifacts
|
//get all existing OS account artifacts
|
||||||
List<BlackboardArtifact> existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
List<BlackboardArtifact> existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||||
for (BlackboardArtifact osAccount : existingOsAccounts) {
|
for (BlackboardArtifact osAccount : existingOsAccounts) {
|
||||||
@ -1157,7 +1212,7 @@ class ExtractRegistry extends Extract {
|
|||||||
logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS
|
logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException ex) {
|
||||||
logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS
|
logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskDataException | TskCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
|
logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
if (!context.dataSourceIngestIsCancelled()) {
|
if (!context.dataSourceIngestIsCancelled()) {
|
||||||
@ -2138,4 +2193,281 @@ class ExtractRegistry extends Extract {
|
|||||||
public String autopsyPlugins = "";
|
public String autopsyPlugins = "";
|
||||||
public String fullPlugins = "";
|
public String fullPlugins = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing or creates a new OsAccount with the given attributes.
|
||||||
|
*
|
||||||
|
* @param file Registry file
|
||||||
|
* @param sid Account sid
|
||||||
|
* @param userName Login name
|
||||||
|
* @param homeDir Account home Directory
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
* @throws TskDataException
|
||||||
|
*/
|
||||||
|
private void createOrUpdateOsAccount(AbstractFile file, String sid, String userName, String homeDir) throws TskCoreException, TskDataException {
|
||||||
|
OsAccountManager accountMgr = tskCase.getOsAccountManager();
|
||||||
|
HostManager hostMrg = tskCase.getHostManager();
|
||||||
|
Host host = hostMrg.getHost((DataSource)dataSource);
|
||||||
|
|
||||||
|
Optional<OsAccount> optional = accountMgr.getWindowsAccount(sid, null, null, host);
|
||||||
|
OsAccount osAccount;
|
||||||
|
if (!optional.isPresent()) {
|
||||||
|
osAccount = accountMgr.createWindowsAccount(sid, userName != null && userName.isEmpty() ? null : userName, null, host, OsAccountRealm.RealmScope.UNKNOWN);
|
||||||
|
} else {
|
||||||
|
osAccount = optional.get();
|
||||||
|
if (userName != null && !userName.isEmpty()) {
|
||||||
|
osAccount.setLoginName(userName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (homeDir != null && !homeDir.isEmpty()) {
|
||||||
|
List<OsAccountAttribute> attributes = new ArrayList<>();
|
||||||
|
attributes.add(createOsAccountAttribute(TSK_HOME_DIR, homeDir, osAccount, host, file));
|
||||||
|
osAccount.addAttributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
accountMgr.updateAccount(osAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an account for the found email address.
|
||||||
|
*
|
||||||
|
* @param regFile File the account was found in
|
||||||
|
* @param emailAddress The emailAddress
|
||||||
|
*/
|
||||||
|
private void addEmailAccount(AbstractFile regFile, String emailAddress) {
|
||||||
|
try {
|
||||||
|
currentCase.getSleuthkitCase()
|
||||||
|
.getCommunicationsManager()
|
||||||
|
.createAccountFileInstance(Account.Type.EMAIL,
|
||||||
|
emailAddress, getRAModuleName(), regFile);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE,
|
||||||
|
String.format("Error adding email account with value "
|
||||||
|
+ "%s, to the case database for file %s [objId=%d]",
|
||||||
|
emailAddress, regFile.getName(), regFile.getId()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the data time string found in the SAM file.
|
||||||
|
*
|
||||||
|
* @param value Date time string in the REG_RIPPER_TIME_FORMAT
|
||||||
|
*
|
||||||
|
* @return Java epoch time in seconds or null if the value could not be
|
||||||
|
* parsed.
|
||||||
|
*/
|
||||||
|
private Long parseRegRipTime(String value) {
|
||||||
|
try {
|
||||||
|
return REG_RIPPER_TIME_FORMAT.parse(value).getTime() / MS_IN_SEC;
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Failed to parse reg rip time: %s", value));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the data from the userInfo map created by parsing the SAM file.
|
||||||
|
*
|
||||||
|
* @param osAccount Account to update.
|
||||||
|
* @param userInfo userInfo map from SAM file parsing.
|
||||||
|
* @param groupList Group list from the SAM file parsing.
|
||||||
|
* @param regFile Source file.
|
||||||
|
*
|
||||||
|
* @throws TskDataException
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private void updateOsAccount(OsAccount osAccount, Map<String, String> userInfo, List<String> groupList, AbstractFile regFile) throws TskDataException, TskCoreException {
|
||||||
|
Host host = ((DataSource)dataSource).getHost();
|
||||||
|
|
||||||
|
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
|
||||||
|
regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
|
||||||
|
|
||||||
|
List<OsAccountAttribute> attributes = new ArrayList<>();
|
||||||
|
|
||||||
|
String value = userInfo.get(ACCOUNT_CREATED_KEY);
|
||||||
|
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||||
|
Long time = parseRegRipTime(value);
|
||||||
|
if (time != null) {
|
||||||
|
osAccount.setCreationTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(LAST_LOGIN_KEY);
|
||||||
|
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||||
|
Long time = parseRegRipTime(value);
|
||||||
|
if (time != null) {
|
||||||
|
attributes.add(createOsAccountAttribute(TSK_DATETIME_ACCESSED,
|
||||||
|
parseRegRipTime(value),
|
||||||
|
osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(LOGIN_COUNT_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
||||||
|
Integer.parseInt(value),
|
||||||
|
osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
// From regripper the possible values for this key are
|
||||||
|
// "Default Admin User", "Custom Limited Acct"
|
||||||
|
// and "Default Guest Acct"
|
||||||
|
value = userInfo.get(ACCOUNT_TYPE_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
osAccount.setIsAdmin(value.toLowerCase().contains("Admin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(USER_COMMENT_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION,
|
||||||
|
value, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(INTERNET_NAME_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
addEmailAccount(regFile, value);
|
||||||
|
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
||||||
|
value, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FULL_NAME_KEY and NAME_KEY appear to be the same value.
|
||||||
|
value = userInfo.get(FULL_NAME_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
osAccount.setFullName(value);
|
||||||
|
} else {
|
||||||
|
value = userInfo.get(NAME_KEY);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
osAccount.setFullName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(PWD_RESET_KEY);
|
||||||
|
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||||
|
Long time = parseRegRipTime(value);
|
||||||
|
if (time != null) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET,
|
||||||
|
time, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(PASSWORD_HINT);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT,
|
||||||
|
value, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
value = userInfo.get(PWD_FAILE_KEY);
|
||||||
|
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||||
|
Long time = parseRegRipTime(value);
|
||||||
|
if (time != null) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL,
|
||||||
|
time, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String settingString = getSettingsFromMap(ACCOUNT_SETTINGS_FLAGS, userInfo);
|
||||||
|
if (!settingString.isEmpty()) {
|
||||||
|
settingString = settingString.substring(0, settingString.length() - 2);
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS,
|
||||||
|
settingString, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
settingString = getSettingsFromMap(ACCOUNT_SETTINGS_FLAGS, userInfo);
|
||||||
|
if (!settingString.isEmpty()) {
|
||||||
|
settingString = settingString.substring(0, settingString.length() - 2);
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS,
|
||||||
|
settingString, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
settingString = getSettingsFromMap(ACCOUNT_TYPE_FLAGS, userInfo);
|
||||||
|
if (!settingString.isEmpty()) {
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_FLAG,
|
||||||
|
settingString, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupList != null && groupList.isEmpty()) {
|
||||||
|
String groups = groupList.stream()
|
||||||
|
.map(String::valueOf)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
attributes.add(createOsAccountAttribute(ATTRIBUTE_TYPE.TSK_GROUPS,
|
||||||
|
groups, osAccount, host, regFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
osAccount.addAttributes(attributes);
|
||||||
|
tskCase.getOsAccountManager().updateAccount(osAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create comma separated list from the set values for the given keys.
|
||||||
|
*
|
||||||
|
* @param keys List of map keys.
|
||||||
|
* @param map Data map.
|
||||||
|
*
|
||||||
|
* @return Comma separated String of values.
|
||||||
|
*/
|
||||||
|
private String getSettingsFromMap(String[] keys, Map<String, String> map) {
|
||||||
|
List<String> settingsList = new ArrayList<>();
|
||||||
|
for (String setting : keys) {
|
||||||
|
if (map.containsKey(setting)) {
|
||||||
|
settingsList.add(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settingsList.isEmpty()) {
|
||||||
|
return settingsList.stream()
|
||||||
|
.map(String::valueOf)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for constructing a new OsAccountAttribute
|
||||||
|
*
|
||||||
|
* @param type Attribute type
|
||||||
|
* @param value The value to store
|
||||||
|
* @param osAccount The OsAccount this attribute belongs to
|
||||||
|
* @param host The Host related to the OsAccount
|
||||||
|
* @param file The source where the attribute was found.
|
||||||
|
*
|
||||||
|
* @return Newly created OsACcountAttribute
|
||||||
|
*/
|
||||||
|
private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, String value, OsAccount osAccount, Host host, AbstractFile file) {
|
||||||
|
return new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for constructing a new OsAccountAttribute
|
||||||
|
*
|
||||||
|
* @param type Attribute type
|
||||||
|
* @param value The value to store
|
||||||
|
* @param osAccount The OsAccount this attribute belongs to
|
||||||
|
* @param host The Host related to the OsAccount
|
||||||
|
* @param file The source where the attribute was found.
|
||||||
|
*
|
||||||
|
* @return Newly created OsACcountAttribute
|
||||||
|
*/
|
||||||
|
private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, Long value, OsAccount osAccount, Host host, AbstractFile file) {
|
||||||
|
return new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for constructing a new OsAccountAttribute
|
||||||
|
*
|
||||||
|
* @param type Attribute type
|
||||||
|
* @param value The value to store
|
||||||
|
* @param osAccount The OsAccount this attribute belongs to
|
||||||
|
* @param host The Host related to the OsAccount
|
||||||
|
* @param file The source where the attribute was found.
|
||||||
|
*
|
||||||
|
* @return Newly created OsACcountAttribute
|
||||||
|
*/
|
||||||
|
private OsAccountAttribute createOsAccountAttribute(BlackboardAttribute.ATTRIBUTE_TYPE type, Integer value, OsAccount osAccount, Host host, AbstractFile file) {
|
||||||
|
return new OsAccountAttribute(new BlackboardAttribute.Type(type), value, osAccount, host, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,10 @@ public class AutopsyTestCases {
|
|||||||
while (!wo.btNext().isEnabled()) {
|
while (!wo.btNext().isEnabled()) {
|
||||||
new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
|
new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pass by host menu with auto-generate host (which should already be selected)
|
||||||
|
wo.btNext().clickMouse();
|
||||||
|
|
||||||
//select the toggle button for Disk Image or VM File it will be the first button created and proceed to next panel
|
//select the toggle button for Disk Image or VM File it will be the first button created and proceed to next panel
|
||||||
JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 0);
|
JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 0);
|
||||||
jtbo.clickMouse();
|
jtbo.clickMouse();
|
||||||
@ -173,6 +177,10 @@ public class AutopsyTestCases {
|
|||||||
while (!wo.btNext().isEnabled()) {
|
while (!wo.btNext().isEnabled()) {
|
||||||
new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
|
new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pass by host menu with auto-generate host (which should already be selected)
|
||||||
|
wo.btNext().clickMouse();
|
||||||
|
|
||||||
//select the toggle button for Logical Files it will be the third button created and proceed to next panel
|
//select the toggle button for Logical Files it will be the third button created and proceed to next panel
|
||||||
JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 2);
|
JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 2);
|
||||||
jtbo.clickMouse();
|
jtbo.clickMouse();
|
||||||
|
@ -348,9 +348,10 @@ class TestRunner(object):
|
|||||||
mu_su_str = "multi" if test_data.isMultiUser else "single"
|
mu_su_str = "multi" if test_data.isMultiUser else "single"
|
||||||
|
|
||||||
for file in glob.glob(test_data.output_path + "/*-Diff.txt"):
|
for file in glob.glob(test_data.output_path + "/*-Diff.txt"):
|
||||||
|
newPath = test_data.main_config.args.diff_files_output_folder + "/" + test_data.image + "-" + mu_su_str + "-" + os.path.basename(file)
|
||||||
# Eg. copies HTML-Report-Diff.txt to <Image-name>-HTML-Report-Diff.txt
|
# Eg. copies HTML-Report-Diff.txt to <Image-name>-HTML-Report-Diff.txt
|
||||||
shutil.copy(file, test_data.main_config.args.diff_files_output_folder +
|
print('copying ' + str(file) + ' to ' + newPath)
|
||||||
"/" + test_data.image + "-" + mus_su_str + "-" + os.path.basename(file))
|
shutil.copy(file, newPath)
|
||||||
copied = True
|
copied = True
|
||||||
if not copied:
|
if not copied:
|
||||||
print_report([], "NO DIFF FILES COPIED FROM " + test_data.output_path, "")
|
print_report([], "NO DIFF FILES COPIED FROM " + test_data.output_path, "")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user