diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index b9ea8f1ae0..6106914e5b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel; import org.sleuthkit.datamodel.Content; 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 @@ -115,7 +116,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { } }); } - + @Override public void setProgressMax(final int max) { // update the progress bar asynchronously @@ -289,7 +290,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { * * * @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) { getComponent().showErrors(errorString, critical); @@ -302,7 +303,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { private void startIngest() { if (!newContents.isEmpty() && readyToIngest && !ingested) { ingested = true; - if (dsProcessor != null && ! dsProcessor.supportsIngestStream()) { + if (dsProcessor != null && !dsProcessor.supportsIngestStream()) { IngestManager.getInstance().queueIngestJob(newContents, ingestJobSettings); } setStateFinished(); @@ -323,8 +324,13 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { /** * Starts the Data source processing by kicking off the selected * 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 final UUID dataSourceId = UUID.randomUUID(); newContents.clear(); @@ -349,7 +355,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { try { Case.getCurrentCaseThrows().notifyAddingDataSource(dataSourceId); } 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(); DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() { @@ -365,9 +371,9 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { if (dsProcessor.supportsIngestStream()) { // Set readyToIngest to false to prevent the wizard from starting ingest a second time. readyToIngest = false; - dsProcessor.runWithIngestStream(ingestJobSettings, getDSPProgressMonitorImpl(), cbObj); + dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj); } else { - dsProcessor.run(getDSPProgressMonitorImpl(), cbObj); + dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj); } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index cfcbdd015c..065a847760 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -43,7 +43,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel { - @Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules") + @Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest") private final IngestJobSettingsPanel ingestJobSettingsPanel; /** * The visual component that displays this panel. If you need to access the diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java index 045bf775b2..9ed66765d3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java @@ -29,6 +29,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.ingest.IngestProfiles; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.IngestProfileSelectionWizardPanel; 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 @@ -41,8 +42,10 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator getPanels() { if (panels == null) { panels = new ArrayList<>(); + hostPanel = new AddImageWizardSelectHostPanel(); + panels.add(hostPanel); + hostPanelIndex = panels.indexOf(hostPanel); AddImageWizardSelectDspPanel dspSelection = new AddImageWizardSelectDspPanel(); panels.add(dspSelection); AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(action); - AddImageWizardDataSourceSettingsPanel dsPanel = new AddImageWizardDataSourceSettingsPanel(); AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(progressPanel); panels.add(dsPanel); @@ -164,7 +169,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator 0); //Users should be able to back up to select a different DSP } /** @@ -180,8 +185,10 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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(); + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.form new file mode 100644 index 0000000000..bdb977932a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.form @@ -0,0 +1,163 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.java new file mode 100644 index 0000000000..9197b2fb42 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectHostVisual.java @@ -0,0 +1,374 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 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 hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts(); + sanitizedHostSet = HostNameValidator.getSanitizedHostNames(hosts); + + Vector 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") + // //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)) + ); + }// //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 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 +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 9019887a68..8206f18798 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -59,7 +59,7 @@ AddImageWizardChooseDataSourcePanel.moveFocusNext=Next > AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added. 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} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} 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.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User. SolrNotConfiguredDialog.messageLabel.text=Multi-User cases are enabled but Solr 8 server has not been configured.
\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n +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 based on based on data source name +AddImageWizardSelectHostVisual.validationMessage.text=\ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 30f782983e..2a22f05c7f 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -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 +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 Case.closeException.couldNotCloseCase=Error closing case: {0} Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources @@ -242,7 +245,7 @@ AddImageWizardChooseDataSourcePanel.moveFocusNext=Next > AddImageWizardChooseDataSourceVisual.getName.text=Select Data Source AddImageWizardIngestConfigPanel.dsProcDone.noErrs.text=*Data Source added. 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} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} 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 # {0} - case name 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 UnpackagePortableCaseDialog.title.text=Unpackage Portable Case 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.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User. SolrNotConfiguredDialog.messageLabel.text=Multi-User cases are enabled but Solr 8 server has not been configured.
\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n +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 based on based on data source name +AddImageWizardSelectHostVisual.validationMessage.text=\ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index c2fd9d822f..393d687807 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -104,6 +104,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.ThreadUtils; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.coreutils.Version; +import org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; @@ -1111,6 +1112,7 @@ public class Case { * Enable the case-specific actions. */ CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources()); + CallableSystemAction.get(OpenHostsAction.class).setEnabled(true); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); @@ -1166,6 +1168,7 @@ public class Case { * Disable the case-specific menu items. */ CallableSystemAction.get(AddImageAction.class).setEnabled(false); + CallableSystemAction.get(OpenHostsAction.class).setEnabled(false); CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java index bb7e6ac469..92335e75b1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java @@ -268,14 +268,6 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour // Read the settings from the wizard readConfigSettings(); 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 try { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java index b34a21ab1d..6cf15c54f7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java @@ -171,13 +171,6 @@ public class LocalDiskDSProcessor implements DataSourceProcessor { } 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; try { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 41be66f0da..dab8ea19ec 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -177,14 +177,6 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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(); if (configPanel.subTypeIsLogicalEvidencePanel()) { try { diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index 288c25ff3a..923e8f5c5e 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -49,6 +49,7 @@ + @@ -164,7 +165,11 @@ - + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.form b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.form new file mode 100644 index 0000000000..36ed76d8f5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.form @@ -0,0 +1,117 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.java new file mode 100644 index 0000000000..4a02edc69a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AddEditHostDialog.java @@ -0,0 +1,230 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 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 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 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") + // //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(); + }// //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 +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties new file mode 100644 index 0000000000..1c2607cb95 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties @@ -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 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties-MERGED new file mode 100644 index 0000000000..22dc1165b3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/Bundle.properties-MERGED @@ -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 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/HostNameValidator.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/HostNameValidator.java new file mode 100644 index 0000000000..a5790af6e7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/HostNameValidator.java @@ -0,0 +1,81 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 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 getSanitizedHostNames(Collection hosts) { + Stream 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()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.form b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.form new file mode 100644 index 0000000000..f01b0cbc8c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.form @@ -0,0 +1,359 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.java new file mode 100644 index 0000000000..6d27cd53a1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/ManageHostsDialog.java @@ -0,0 +1,580 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 dataSources; + + /** + * Main constructor. + * + * @param host The host. + * @param dataSources The data sources that are children of this host. + */ + HostListItem(Host host, List dataSources) { + this.host = host; + this.dataSources = dataSources; + } + + /** + * @return The host. + */ + Host getHost() { + return host; + } + + /** + * @return The data sources associated with this host. + */ + List 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> 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 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 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> getHostListData() { + Map> hostMapping = new HashMap<>(); + try { + SleuthkitCase curCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + List hosts = curCase.getHostManager().getHosts(); + List dataSources = curCase.getDataSources(); + + if (dataSources != null) { + for (DataSource ds : dataSources) { + List 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 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") + // //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(); + }// //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 hostList; + private javax.swing.JTextField hostNameTextField; + private javax.swing.JButton newButton; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/OpenHostsAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/OpenHostsAction.java new file mode 100644 index 0000000000..6c0139a908 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/OpenHostsAction.java @@ -0,0 +1,81 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java index 3faab8c2cd..7c38dedcf8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java @@ -159,15 +159,6 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource @Override public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java index dfea1da73c..d14d4e542a 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYDataSourceProcessor.java @@ -218,16 +218,7 @@ public class XRYDataSourceProcessor implements DataSourceProcessor, AutoIngestDa "XRYDataSourceProcessor.noCurrentCase=No case is open." }) public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - 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; - } - + progressMonitor.setIndeterminate(true); String selectedFilePath = configPanel.getSelectedFilePath(); File selectedFile = new File(selectedFilePath); Path selectedPath = selectedFile.toPath(); diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerDSProcessor.java index 67498064f0..9d9054cb80 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerDSProcessor.java @@ -160,17 +160,7 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor { }) @Override public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - 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; - } - + configPanel.storeSettings(); Path imageDirPath = configPanel.getImageDirPath(); List errorList = new ArrayList<>(); List emptyDataSources = new ArrayList<>(); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java index a0ca322fac..ea8a45fda9 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java @@ -140,15 +140,6 @@ public class MemoryDSProcessor implements DataSourceProcessor { @Override public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { 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); }