diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java index 227bfd0650..5ad9a606a6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java @@ -26,6 +26,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.imagewriter.ImageWriterService; +import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.SleuthkitJNI; @@ -41,6 +43,7 @@ class AddImageTask implements Runnable { private final String deviceId; private final String imagePath; private final String timeZone; + private final String imageWriterPath; private final boolean ignoreFatOrphanFiles; private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorCallback callback; @@ -74,15 +77,19 @@ class AddImageTask implements Runnable { * java.util.TimeZone.getID. * @param ignoreFatOrphanFiles Whether to parse orphans if the image has a * FAT filesystem. + * @param imageWriterPath Path that a copy of the image should be written to. + * Use empty string to disable image writing * @param progressMonitor Progress monitor to report progress during * processing. * @param callback Callback to call when processing is done. */ - AddImageTask(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + AddImageTask(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, String imageWriterPath, + DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { this.deviceId = deviceId; this.imagePath = imagePath; this.timeZone = timeZone; this.ignoreFatOrphanFiles = ignoreFatOrphanFiles; + this.imageWriterPath = imageWriterPath; this.callback = callback; this.progressMonitor = progressMonitor; tskAddImageProcessLock = new Object(); @@ -101,7 +108,7 @@ class AddImageTask implements Runnable { try { currentCase.getSleuthkitCase().acquireExclusiveLock(); synchronized (tskAddImageProcessLock) { - tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles); + tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath); } Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess)); progressUpdateThread.start(); @@ -201,6 +208,9 @@ class AddImageTask implements Runnable { if (!verificationError.isEmpty()) { errorMessages.add(verificationError); } + if(! imageWriterPath.isEmpty()){ + ImageWriterService.createImageWriter(imageId); + } newDataSources.add(newImage); } else { String errorMessage = String.format("Error commiting adding image %s to the case database, no object id returned", imagePath); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form deleted file mode 100644 index 928cb45a95..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form +++ /dev/null @@ -1,132 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java deleted file mode 100644 index adf61a8f32..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2014 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.casemodule; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.ListCellRenderer; -import javax.swing.event.DocumentEvent; -import org.openide.util.Lookup; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; - -/** - * visual component for the first panel of add image wizard. Allows the user to - * choose the data source type and then select the data source - * - */ -final class AddImageWizardChooseDataSourceVisual extends JPanel { - - static final Logger logger = Logger.getLogger(AddImageWizardChooseDataSourceVisual.class.getName()); - - private AddImageWizardChooseDataSourcePanel wizPanel; - - private JPanel currentPanel; - - private Map datasourceProcessorsMap = new HashMap<>(); - - List coreDSPTypes = new ArrayList<>(); - - /** - * Creates new form AddImageVisualPanel1 - * - * @param wizPanel corresponding WizardPanel to handle logic of wizard step - */ - AddImageWizardChooseDataSourceVisual(AddImageWizardChooseDataSourcePanel wizPanel) { - initComponents(); - this.wizPanel = wizPanel; - - customInit(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private void customInit() { - - typePanel.setLayout(new BorderLayout()); - - discoverDataSourceProcessors(); - - // set up the DSP type combobox - typeComboBox.removeAllItems(); - - Set dspTypes = datasourceProcessorsMap.keySet(); - - // make a list of core DSPs - // ensure that the core DSPs are at the top and in a fixed order - coreDSPTypes.add(ImageDSProcessor.getType()); - // Local disk processing is not allowed for multi-user cases - if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { - coreDSPTypes.add(LocalDiskDSProcessor.getType()); - } else { - // remove LocalDiskDSProcessor from list of DSPs - datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); - } - coreDSPTypes.add(LocalFilesDSProcessor.getType()); - coreDSPTypes.add(RawDSProcessor.getType()); - - for (String dspType : coreDSPTypes) { - typeComboBox.addItem(dspType); - } - - // now add any addtional DSPs that haven't already been added - for (String dspType : dspTypes) { - if (!coreDSPTypes.contains(dspType)) { - typeComboBox.addItem(dspType); - } - } - - typeComboBox.setRenderer(new ComboboxSeparatorRenderer(typeComboBox.getRenderer()) { - - @Override - protected boolean addSeparatorAfter(JList list, Object value, int index) { - return (index == coreDSPTypes.size() - 1); - } - }); - - //add actionlistner to listen for change - ActionListener cbActionListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dspSelectionChanged(); - } - }; - typeComboBox.addActionListener(cbActionListener); - typeComboBox.setSelectedIndex(0); - } - - private void discoverDataSourceProcessors() { - - for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { - - if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { - datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); - } else { - logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS - } - } - } - - private void dspSelectionChanged() { - // update the current panel to selection - currentPanel = getCurrentDSProcessor().getPanel(); - updateCurrentPanel(currentPanel); - } - - /** - * Changes the current panel to the given panel. - * - * @param panel instance of ImageTypePanel to change to - */ - @SuppressWarnings("deprecation") - private void updateCurrentPanel(JPanel panel) { - currentPanel = panel; - typePanel.removeAll(); - typePanel.add(currentPanel, BorderLayout.CENTER); - typePanel.validate(); - typePanel.repaint(); - currentPanel.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) { - updateUI(null); - } - if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) { - wizPanel.moveFocusToNext(); - } - } - }); - - updateUI(null); - } - - /** - * Returns the currently selected DS Processor - * - * @return DataSourceProcessor the DataSourceProcessor corresponding to the - * data source type selected in the combobox - */ - protected DataSourceProcessor getCurrentDSProcessor() { - // get the type of the currently selected panel and then look up - // the correspodning DS Handler in the map - String dsType = (String) typeComboBox.getSelectedItem(); - DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(dsType); - - return dsProcessor; - - } - - /** - * Returns the name of the this panel. This name will be shown on the left - * panel of the "Add Image" wizard panel. - * - * @return name the name of this panel - */ - @Override - public String getName() { - return NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourceVisual.getName.text"); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - private void initComponents() { - - buttonGroup1 = new javax.swing.ButtonGroup(); - jLabel2 = new javax.swing.JLabel(); - inputPanel = new javax.swing.JPanel(); - typeTabel = new javax.swing.JLabel(); - typePanel = new javax.swing.JPanel(); - typeComboBox = new javax.swing.JComboBox(); - - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.jLabel2.text")); // NOI18N - - setPreferredSize(new java.awt.Dimension(588, 328)); - - org.openide.awt.Mnemonics.setLocalizedText(typeTabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.typeTabel.text")); // NOI18N - - typePanel.setMinimumSize(new java.awt.Dimension(0, 65)); - typePanel.setPreferredSize(new java.awt.Dimension(521, 65)); - - javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel); - typePanel.setLayout(typePanelLayout); - typePanelLayout.setHorizontalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 548, Short.MAX_VALUE) - ); - typePanelLayout.setVerticalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 225, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout inputPanelLayout = new javax.swing.GroupLayout(inputPanel); - inputPanel.setLayout(inputPanelLayout); - inputPanelLayout.setHorizontalGroup( - inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addComponent(typeTabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 119, Short.MAX_VALUE)) - .addComponent(typePanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 548, Short.MAX_VALUE)) - .addContainerGap()) - ); - inputPanelLayout.setVerticalGroup( - inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(typeTabel) - .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(typePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - 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() - .addComponent(inputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(6, 6, 6) - .addComponent(inputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(44, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.ButtonGroup buttonGroup1; - private javax.swing.JPanel inputPanel; - private javax.swing.JLabel jLabel2; - private javax.swing.JComboBox typeComboBox; - private javax.swing.JPanel typePanel; - private javax.swing.JLabel typeTabel; - // End of variables declaration//GEN-END:variables - - /** - * The "listener" that updates the UI of this panel based on the changes of - * fields on this panel. This is also the method to check whether all the - * fields on this panel are correctly filled and decides whether to enable - * the "Next" button or not. - * - * @param e the document event - */ - public void updateUI(DocumentEvent e) { - // Enable the Next button if the current DSP panel is valid - this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); - } - - @SuppressWarnings("rawtypes") - public abstract class ComboboxSeparatorRenderer implements ListCellRenderer { - - private ListCellRenderer delegate; - - private JPanel separatorPanel = new JPanel(new BorderLayout()); - - private JSeparator separator = new JSeparator(); - - public ComboboxSeparatorRenderer(ListCellRenderer delegate) { - this.delegate = delegate; - } - - @SuppressWarnings("unchecked") - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (index != -1 && addSeparatorAfter(list, value, index)) { - separatorPanel.removeAll(); - separatorPanel.add(comp, BorderLayout.CENTER); - separatorPanel.add(separator, BorderLayout.SOUTH); - return separatorPanel; - } else { - return comp; - } - } - - protected abstract boolean addSeparatorAfter(JList list, Object value, int index); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java index 3d58017ecb..1643769be0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java @@ -39,23 +39,17 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript * The "Add Image" wizard panel1 handling the logic of selecting image file(s) * to add to Case, and pick the time zone. */ -class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { +class AddImageWizardDataSourceSettingsPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { /** * The visual component that displays this panel. If you need to access the * component from this class, just use getComponent(). */ - private final AddImageWizardAddingProgressPanel progressPanel; - private AddImageWizardChooseDataSourceVisual component; + private AddImageWizardDataSourceSettingsVisual component; private boolean isNextEnable = false; - private static final String PROP_LASTDATASOURCE_PATH = "LBL_LastDataSource_PATH"; //NON-NLS - private static final String PROP_LASTDATASOURCE_TYPE = "LBL_LastDataSource_TYPE"; //NON-NLS // paths to any set hash lookup databases (can be null) - private String NSRLPath, knownBadPath; - AddImageWizardChooseDataSourcePanel(AddImageWizardAddingProgressPanel proPanel) { - - this.progressPanel = proPanel; + AddImageWizardDataSourceSettingsPanel() { } @@ -68,10 +62,10 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel * @return component the UI component of this wizard panel */ @Override - public AddImageWizardChooseDataSourceVisual getComponent() { + public AddImageWizardDataSourceSettingsVisual getComponent() { if (component == null) { WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - component = new AddImageWizardChooseDataSourceVisual(this); + component = new AddImageWizardDataSourceSettingsVisual(this); } component.addPropertyChangeListener(this); return component; @@ -87,8 +81,7 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel public HelpCtx getHelp() { // Show no Help button for this panel: return HelpCtx.DEFAULT_HELP; - // If you have context help: - // return new HelpCtx(SampleWizardPanel1.class); + } /** @@ -164,10 +157,6 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel } } - // You can use a settings object to keep track of state. Normally the - // settings object will be the WizardDescriptor, so you can use - // WizardDescriptor.getProperty & putProperty to store information entered - // by the user. /** * Provides the wizard panel with the current data--either the default data * or already-modified settings, if the user used the previous and/or next @@ -178,26 +167,16 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel */ @Override public void readSettings(WizardDescriptor settings) { - - //reset settings if supports it - //getComponent().reset(); // Prepopulate the image directory from the properties file try { - - // Load hash database settings, enable or disable the checkbox - this.NSRLPath = null; - this.knownBadPath = null; - //JCheckBox lookupFilesCheckbox = component.getLookupFilesCheckbox(); - //lookupFilesCheckbox.setSelected(false); - //lookupFilesCheckbox.setEnabled(this.NSRLPath != null || this.knownBadPath != null); - + // If there is a process object in the settings, revert it and remove it from the settings AddImageAction.CleanupTask cleanupTask = (AddImageAction.CleanupTask) settings.getProperty(AddImageAction.IMAGECLEANUPTASK_PROP); if (cleanupTask != null) { try { cleanupTask.cleanup(); } catch (Exception ex) { - Logger logger = Logger.getLogger(AddImageWizardChooseDataSourcePanel.class.getName()); + Logger logger = Logger.getLogger(AddImageWizardDataSourceSettingsPanel.class.getName()); logger.log(Level.WARNING, "Error cleaning up image task", ex); //NON-NLS } finally { cleanupTask.disable(); @@ -205,7 +184,7 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel } } catch (Exception e) { } - + component.setDspSelection((String)settings.getProperty("SelectedDsp")); //NON-NLS magic string used SelectDataSourceProcessorPanel } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form new file mode 100644 index 0000000000..d8ab744f34 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form @@ -0,0 +1,58 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java new file mode 100644 index 0000000000..a5671ce0ee --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java @@ -0,0 +1,215 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 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.awt.BorderLayout; +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.ListCellRenderer; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * visual component for the first panel of add image wizard. Allows the user to + * choose the data source type and then select the data source + * + */ +final class AddImageWizardDataSourceSettingsVisual extends JPanel { + + private static final Logger logger = Logger.getLogger(AddImageWizardDataSourceSettingsVisual.class.getName()); + + private final AddImageWizardDataSourceSettingsPanel wizPanel; + + private JPanel currentPanel; + + private final Map datasourceProcessorsMap = new HashMap<>(); + + private String currentDsp; + + /** + * Creates new form AddImageVisualPanel1 + * + * @param wizPanel corresponding WizardPanel to handle logic of wizard step + */ + AddImageWizardDataSourceSettingsVisual(AddImageWizardDataSourceSettingsPanel wizPanel) { + initComponents(); + this.wizPanel = wizPanel; + typePanel.setLayout(new BorderLayout()); + discoverDataSourceProcessors(); + currentDsp = ImageDSProcessor.getType(); //default value to the ImageDSProcessor + } + + /** + * Populate the map of DataSourceProcessors which so they can be retrieved + * by name. + */ + private void discoverDataSourceProcessors() { + for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { + if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { + datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); + } else { + logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS + } + } + } + + /** + * Set the current DataSourceProcessor and update the panel to reflect that + * selection. + * + * @param dsType - the name of the DataSourceProcessor you wish to have this + * panel display settings for. + */ + void setDspSelection(String dsType) { + currentDsp = dsType; + currentPanel = datasourceProcessorsMap.get(dsType).getPanel(); + updateCurrentPanel(currentPanel); + } + + /** + * Changes the current panel to the given panel. + * + * @param panel instance of ImageTypePanel to change to + */ + @SuppressWarnings("deprecation") + private void updateCurrentPanel(JPanel panel) { + currentPanel = panel; + typePanel.removeAll(); + typePanel.add(currentPanel, BorderLayout.CENTER); + typePanel.validate(); + typePanel.repaint(); + currentPanel.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) { + wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); + } + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) { + wizPanel.moveFocusToNext(); + } + } + }); + this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); + } + + /** + * Returns the currently selected DS Processor + * + * @return DataSourceProcessor the DataSourceProcessor corresponding to the + * data source type selected in the combobox + */ + protected DataSourceProcessor getCurrentDSProcessor() { + // get the type of the currently selected panel and then look up + // the correspodning DS Handler in the map + DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(currentDsp); + return dsProcessor; + + } + + /** + * Returns the name of the this panel. This name will be shown on the left + * panel of the "Add Image" wizard panel. + * + * @return name the name of this panel + */ + @Override + public String getName() { + return NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourceVisual.getName.text"); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + typePanel = new javax.swing.JPanel(); + + setPreferredSize(new java.awt.Dimension(588, 328)); + + typePanel.setMinimumSize(new java.awt.Dimension(0, 65)); + typePanel.setPreferredSize(new java.awt.Dimension(521, 65)); + + javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel); + typePanel.setLayout(typePanelLayout); + typePanelLayout.setHorizontalGroup( + typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 588, Short.MAX_VALUE) + ); + typePanelLayout.setVerticalGroup( + typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 328, Short.MAX_VALUE) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 588, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel typePanel; + // End of variables declaration//GEN-END:variables + + @SuppressWarnings("rawtypes") + public abstract class ComboboxSeparatorRenderer implements ListCellRenderer { + + private final ListCellRenderer delegate; + + private final JPanel separatorPanel = new JPanel(new BorderLayout()); + + private final JSeparator separator = new JSeparator(); + + public ComboboxSeparatorRenderer(ListCellRenderer delegate) { + this.delegate = delegate; + } + + @SuppressWarnings("unchecked") + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (index != -1 && addSeparatorAfter(list, value, index)) { + separatorPanel.removeAll(); + separatorPanel.add(comp, BorderLayout.CENTER); + separatorPanel.add(separator, BorderLayout.SOUTH); + return separatorPanel; + } else { + return comp; + } + } + + protected abstract boolean addSeparatorAfter(JList list, Object value, int index); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index ce4aa13c89..1d7bbb8a13 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -69,12 +69,12 @@ class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel { private final AddImageAction addImageAction; private final AddImageWizardAddingProgressPanel progressPanel; - private final AddImageWizardChooseDataSourcePanel dataSourcePanel; + private final AddImageWizardDataSourceSettingsPanel dataSourcePanel; private DataSourceProcessor dsProcessor; private boolean cancelled; - AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { + AddImageWizardIngestConfigPanel(AddImageWizardDataSourceSettingsPanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { this.addImageAction = action; this.progressPanel = proPanel; this.dataSourcePanel = dsPanel; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java index 3dafa75479..3558ffe83b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java @@ -40,6 +40,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator panels; private final AddImageAction action; private int progressPanelIndex; + private int dsPanelIndex; private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS AddImageWizardIterator(AddImageAction action) { @@ -53,10 +54,11 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator getPanels() { if (panels == null) { panels = new ArrayList<>(); - + AddImageWizardSelectDspPanel dspSelection = new AddImageWizardSelectDspPanel(); + panels.add(dspSelection); AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(); - AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(progressPanel); + AddImageWizardDataSourceSettingsPanel dsPanel = new AddImageWizardDataSourceSettingsPanel(); AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, action, progressPanel); panels.add(dsPanel); List profiles = IngestProfiles.getIngestProfiles(); @@ -66,6 +68,7 @@ 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.awt.Cursor; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import javax.swing.event.ChangeListener; +import org.openide.WizardDescriptor; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel; + +/** + * Create a wizard panel which contains a panel allowing the selection of the + * DataSourceProcessor + */ +final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { + + @NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Type of Data") + private AddImageWizardSelectDspVisual component; + 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 Logger logger = Logger.getLogger(AddImageWizardSelectDspVisual.class.getName()); + + @Override + public Component getComponent() { + if (component == null) { + String lastDspUsed; + if (!(ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY) == null) + && !ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY).isEmpty()) { + lastDspUsed = ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY); + } else { + lastDspUsed = ImageDSProcessor.getType(); + logger.log(Level.WARNING, "There was no properties file containing the last DataSourceProcessor used, Disk Image or VM will be selected as default selection"); //NON-NLS + } + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + component = new AddImageWizardSelectDspVisual(lastDspUsed); + component.setName(Bundle.SelectDataSourceProcessorPanel_name_text()); + } + component.addPropertyChangeListener(this); + return component; + } + + @Override + public HelpCtx getHelp() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public void readSettings(WizardDescriptor data) { + } + + @Override + public void storeSettings(WizardDescriptor data) { + String lastDspUsed = component.getSelectedDsp(); + ModuleSettings.setConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY, lastDspUsed); + data.putProperty("SelectedDsp", lastDspUsed); //NON-NLS magic string necesary to AddImageWizardChooseDataSourcePanel + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public void addChangeListener(ChangeListener cl) { + } + + @Override + public void removeChangeListener(ChangeListener cl) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form new file mode 100644 index 0000000000..106c4925c5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form @@ -0,0 +1,73 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java new file mode 100644 index 0000000000..7a86e5dfb9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java @@ -0,0 +1,241 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 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.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.AbstractButton; +import javax.swing.Box.Filler; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JToggleButton; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Panel which displays the available DataSourceProcessors and allows selection of one + */ +final class AddImageWizardSelectDspVisual extends JPanel { + + private static final Logger logger = Logger.getLogger(AddImageWizardSelectDspVisual.class.getName()); + private String selectedDsp; + + /** + * Creates new form SelectDataSourceProcessorPanel + */ + AddImageWizardSelectDspVisual(String lastDspUsed) { + initComponents(); + selectedDsp = lastDspUsed; + createDataSourceProcessorButtons(); + + //add actionlistner to listen for change + } + + /** + * Find the DSP which is currently selected and save it as the selected + * DataSourceProcessor. + * + */ + private void updateSelectedDsp() { + Enumeration buttonGroup = buttonGroup1.getElements(); + while (buttonGroup.hasMoreElements()) { + AbstractButton dspButton = buttonGroup.nextElement(); + if (dspButton.isSelected()) { + selectedDsp = dspButton.getName(); + break; + } + } + } + + /** + * Get the DataSourceProcessor which is currently selected in this panel + * + * @return selectedDsp the DataSourceProcessor which is selected in this panel + */ + String getSelectedDsp() { + return selectedDsp; + } + + /** + * Create the a button for each DataSourceProcessor that should exist as an option. + */ + private void createDataSourceProcessorButtons() { + //Listener for button selection + ActionListener cbActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateSelectedDsp(); + } + }; + List dspList = getListOfDsps(); + //Set up the constraints for the panel layout + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weighty = 0; + constraints.anchor = GridBagConstraints.LINE_START; + Dimension spacerBlockDimension = new Dimension(6, 4); // Space between left edge and button, Space between rows + for (String dspType : dspList) { + constraints.weightx = 1; + //Add a spacer + Filler spacer = new Filler(spacerBlockDimension, spacerBlockDimension, spacerBlockDimension); + gridBagLayout.setConstraints(spacer, constraints); + jPanel1.add(spacer); + constraints.gridx++; + constraints.gridy++; + //Add the button + JToggleButton dspButton = createDspButton(dspType); + dspButton.addActionListener(cbActionListener); + jPanel1.add(dspButton); + buttonGroup1.add(dspButton); + gridBagLayout.setConstraints(dspButton, constraints); + constraints.gridx++; + //Add the text area serving as a label to the right of the button + JTextArea myLabel = new JTextArea(dspType); + myLabel.setBackground(new Color(240, 240, 240));//matches background of panel + myLabel.setEditable(false); + myLabel.setWrapStyleWord(true); + myLabel.setLineWrap(true); + jPanel1.add(myLabel); + gridBagLayout.setConstraints(myLabel, constraints); + constraints.weightx = 0; + constraints.gridy++; + constraints.gridx = 0; + } + Component vertGlue = javax.swing.Box.createVerticalGlue(); + jPanel1.add(vertGlue); + constraints.gridy++; + constraints.gridx = 0; + constraints.weighty = 1; + gridBagLayout.setConstraints(vertGlue, constraints); + jPanel1.setLayout(gridBagLayout); + } + + /** + * Create a list of the DataSourceProcessors which should exist as options on this panel. + * The default Autopsy DataSourceProcessors will appear + * at the beggining of the list in the same order. + * + * @return dspList a list of DataSourceProcessors which can be chose in this panel + */ + private List getListOfDsps() { + List dspList = new ArrayList<>(); + final Map datasourceProcessorsMap = new HashMap<>(); + for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { + if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { + datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); + } else { + logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS + } + } + dspList.add(ImageDSProcessor.getType()); + if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { + dspList.add(LocalDiskDSProcessor.getType()); + } else { + // remove LocalDiskDSProcessor from list of DSPs + datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); + } + dspList.add(LocalFilesDSProcessor.getType()); + dspList.add(RawDSProcessor.getType()); + // now add any addtional DSPs that haven't already been added + for (String dspType : datasourceProcessorsMap.keySet()) { + if (!dspList.contains(dspType)) { + dspList.add(dspType); + } + } + return dspList; + } + + /** + * Create a single button for a DataSourceProcessor + * + * @param dspType - the name of the DataSourceProcessor + * + * @return dspButton a JToggleButton for the specified dspType + */ + private JToggleButton createDspButton(String dspType) { + JToggleButton dspButton = new JToggleButton(); + dspButton.setMaximumSize(new java.awt.Dimension(48, 48)); + dspButton.setMinimumSize(new java.awt.Dimension(48, 48)); + dspButton.setPreferredSize(new java.awt.Dimension(48, 48)); + dspButton.setName(dspType); + dspButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/fileextmismatch/options-icon.png"))); + dspButton.setSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/checkbox32.png"))); + dspButton.setFocusable(false); + if (dspType.equals(selectedDsp)) { + dspButton.setSelected(true); + } else { + dspButton.setSelected(false); + } + return dspButton; + } + + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(6, 8), new java.awt.Dimension(6, 8), new java.awt.Dimension(6, 8)); + + jPanel1.setLayout(new java.awt.GridBagLayout()); + jPanel1.add(filler1, new java.awt.GridBagConstraints()); + + jScrollPane1.setViewportView(jPanel1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 588, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 588, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 328, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.Box.Filler filler1; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + // 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 c4a51ac904..f22ada5664 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -55,8 +55,6 @@ LocalFilesPanel.localFileChooser.approveButtonText=Select LocalFilesPanel.localFileChooser.approveButtonToolTipText= LocalFilesPanel.selectButton.actionCommand=Add AddImageWizardAddingProgressVisual.statusLabel.text=Data source has been added to the local database. Files are being analyzed. -AddImageWizardChooseDataSourceVisual.typeTabel.text=Select data source type: -AddImageWizardChooseDataSourceVisual.jLabel2.text=jLabel2 AddImageWizardAddingProgressVisual.progressLabel.text= AddImageWizardAddingProgressVisual.viewLogButton.text=View Log AddImageWizardAddingProgressVisual.subTitle1Label.text=Processing data source and adding it to a local database. File analysis will start when this finishes. @@ -68,6 +66,10 @@ LocalDiskPanel.timeZoneLabel.text=Please select the input timezone: LocalDiskPanel.noFatOrphansCheckbox.toolTipText= LocalDiskPanel.noFatOrphansCheckbox.text=Ignore orphan files in FAT file systems LocalDiskPanel.descLabel.text=(faster results, although some data will not be searched) +LocalDiskPanel.imageWriterDirError.text=Error - directory does not exist +LocalDiskPanel.imageWriterEmptyPathError.text=Error - enter path for VHD +LocalDiskPanel.imageWriterIsDirError.text=Error - VHD path is a directory +LocalDiskPanel.imageWriterFileExistsError.text=Error - VHD path already exists MissingImageDialog.browseButton.text=Browse MissingImageDialog.pathNameTextField.text= AddImageWizardAddingProgressVisual.progressTextArea.border.title=Status @@ -224,6 +226,11 @@ LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default IngestJobInfoPanel.jLabel1.text=Ingest Modules IngestJobInfoPanel.jLabel2.text=Ingest Jobs CaseInformationPanel.closeButton.text=Close +LocalDiskPanel.copyImageCheckbox.text=Make a VHD image of the drive while it is being analyzed +LocalDiskPanel.imageWriterErrorLabel.text=Error Label +LocalDiskPanel.jLabel1.text=Note that at least one ingest module must be run to create a complete copy +LocalDiskPanel.pathTextField.text= +LocalDiskPanel.browseButton.text=Browse CasePropertiesPanel.updateCaseNameButton.text=Update Name CasePropertiesPanel.caseNameTextField.text= CasePropertiesPanel.caseDirLabel.text=Case Directory: diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index fdb39ad133..82efd7df67 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -42,8 +42,6 @@ LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u30 LocalFilesPanel.localFileChooser.approveButtonText=\u9078\u629e LocalFilesPanel.selectButton.actionCommand=\u8ffd\u52a0 AddImageWizardAddingProgressVisual.statusLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d\u3067\u3059\u3002 -AddImageWizardChooseDataSourceVisual.typeTabel.text=\u8ffd\u52a0\u3059\u308b\u30bd\u30fc\u30b9\u30bf\u30a4\u30d7\u3092\u9078\u629e\uff1a -AddImageWizardChooseDataSourceVisual.jLabel2.text=jLabel2 AddImageWizardAddingProgressVisual.progressLabel.text=\uff1c\u30d7\u30ed\u30b0\u30ec\u30b9\uff1e AddImageWizardAddingProgressVisual.viewLogButton.text=\u30ed\u30b0\u3092\u8868\u793a AddImageWizardAddingProgressVisual.subTitle1Label.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u60c5\u5831\u3092\u8ffd\u52a0\u4e2d\u3067\u3059\u3002\u3053\u3061\u3089\u304c\u5b8c\u4e86\u6b21\u7b2c\u3001\u30d5\u30a1\u30a4\u30eb\u89e3\u6790\u304c\u59cb\u307e\u308a\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index ad21349a82..e3ab78bf64 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -72,9 +72,9 @@ import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; @@ -673,7 +673,7 @@ public class Case { * cannot be deleted if another node has it open. */ progressIndicator.start(Bundle.Case_progressMessage_acquiringLocks()); - try (CoordinationService.Lock dirLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory())) { + try (CoordinationService.Lock dirLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) { assert (null != dirLock); /* @@ -945,7 +945,7 @@ public class Case { @Messages({"Case.creationException.couldNotAcquireNameLock=Failed to get lock on case name"}) private static CoordinationService.Lock acquireExclusiveCaseNameLock(String caseName) throws CaseActionException { try { - Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseName, NAME_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, caseName, NAME_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == lock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireNameLock()); } @@ -971,7 +971,7 @@ public class Case { private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseName) throws CaseActionException { try { String resourcesNodeName = caseName + "_resources"; - Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, resourcesNodeName, RESOURCE_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCE_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == lock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -1604,7 +1604,7 @@ public class Case { "Case.progressIndicatorTitle.creatingCase=Creating Case", "Case.progressIndicatorCancelButton.label=Cancel", "Case.progressMessage.preparing=Preparing...", - "Case.progressMessage.acquiringLocks=Acquiring locks..." + "Case.progressMessage.acquiringLocks=Preparing to open case resources.
This may take time if another user is upgrading the case." }) private void open(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException { /* @@ -2337,7 +2337,7 @@ public class Case { @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."}) private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException { try { - caseDirLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, caseDir, SHARED_DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == caseDirLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java index e7b4e98c99..aaf65e2f7e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java @@ -455,13 +455,6 @@ public final class CaseMetadata { this.caseDatabaseName = caseDirectoryPath.relativize(possibleAbsoluteCaseDbPath).toString(); } - /* - * Update the file to the current schema, if necessary. - */ - if (!schemaVersion.equals(CURRENT_SCHEMA_VERSION)) { - writeToFile(); - } - } catch (ParserConfigurationException | SAXException | IOException ex) { throw new CaseMetadataException(String.format("Error reading from case metadata file %s", metadataFilePath), ex); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java index a5064606f2..26a7fb9c26 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java @@ -115,7 +115,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action protected void done() { try { get(); - } catch (InterruptedException | ExecutionException ex) { + } catch (InterruptedException ex) { logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", path), ex); //NON-NLS WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); JOptionPane.showMessageDialog( @@ -124,6 +124,15 @@ public final class CaseOpenAction extends CallableSystemAction implements Action NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.title"), //NON-NLS JOptionPane.ERROR_MESSAGE); StartupWindowProvider.getInstance().open(); + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", path), ex); //NON-NLS + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + JOptionPane.showMessageDialog( + WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), //get the message of the wrapped exception + NbBundle.getMessage(this.getClass(), "CaseOpenAction.msgDlg.cantOpenCase.title"), //NON-NLS + JOptionPane.ERROR_MESSAGE); + StartupWindowProvider.getInstance().open(); } WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java index f76f620413..5d976c0baa 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java @@ -185,7 +185,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour * @param callback Callback to call when processing is done. */ public void run(String deviceId, String imagePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addImageTask = new AddImageTask(deviceId, imagePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callback); + addImageTask = new AddImageTask(deviceId, imagePath, timeZone, ignoreFatOrphanFiles, "", progressMonitor, callback); new Thread(addImageTask).start(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java index 305bb3dd90..d5b926d197 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskDSProcessor.java @@ -54,6 +54,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor, AutoIngestData private String deviceId; private String drivePath; private String timeZone; + private String imageWriterPath = ""; private boolean ignoreFatOrphanFiles; private boolean setDataSourceOptionsCalled; @@ -137,8 +138,11 @@ public class LocalDiskDSProcessor implements DataSourceProcessor, AutoIngestData drivePath = configPanel.getContentPaths(); timeZone = configPanel.getTimeZone(); ignoreFatOrphanFiles = configPanel.getNoFatOrphans(); + if(configPanel.getImageWriterEnabled()){ + imageWriterPath = configPanel.getImageWriterPath(); + } } - addDiskTask = new AddImageTask(deviceId, drivePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callback); + addDiskTask = new AddImageTask(deviceId, drivePath, timeZone, ignoreFatOrphanFiles, imageWriterPath, progressMonitor, callback); new Thread(addDiskTask).start(); } @@ -164,7 +168,7 @@ public class LocalDiskDSProcessor implements DataSourceProcessor, AutoIngestData * @param callback Callback to call when processing is done. */ public void run(String deviceId, String drivePath, String timeZone, boolean ignoreFatOrphanFiles, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addDiskTask = new AddImageTask(deviceId, drivePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callback); + addDiskTask = new AddImageTask(deviceId, drivePath, timeZone, ignoreFatOrphanFiles, imageWriterPath, progressMonitor, callback); new Thread(addDiskTask).start(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form index b1409546e6..89835246a9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.form @@ -3,10 +3,10 @@
- + - + @@ -26,21 +26,38 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + - + @@ -61,7 +78,18 @@ - + + + + + + + + + + + + @@ -165,5 +193,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java index 0d485b3392..2572c0c61d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java @@ -22,6 +22,8 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Font; +import java.io.File; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -30,6 +32,7 @@ import java.util.TimeZone; import java.util.concurrent.CancellationException; import java.util.logging.Level; import javax.swing.ComboBoxModel; +import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; @@ -57,6 +60,7 @@ final class LocalDiskPanel extends JPanel { private List disks; private LocalDiskModel model; private boolean enableNext = false; + private final JFileChooser fc = new JFileChooser(); /** * Creates new form LocalDiskPanel @@ -88,6 +92,10 @@ final class LocalDiskPanel extends JPanel { errorLabel.setVisible(false); errorLabel.setText(""); diskComboBox.setEnabled(false); + imageWriterErrorLabel.setOpaque(true); + imageWriterErrorLabel.setText(""); + pathTextField.setEnabled(copyImageCheckbox.isSelected()); + browseButton.setEnabled(copyImageCheckbox.isSelected()); } /** @@ -106,9 +114,14 @@ final class LocalDiskPanel extends JPanel { timeZoneComboBox = new javax.swing.JComboBox<>(); noFatOrphansCheckbox = new javax.swing.JCheckBox(); descLabel = new javax.swing.JLabel(); + copyImageCheckbox = new javax.swing.JCheckBox(); + imageWriterErrorLabel = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + pathTextField = new javax.swing.JTextField(); + browseButton = new javax.swing.JButton(); - setMinimumSize(new java.awt.Dimension(0, 65)); - setPreferredSize(new java.awt.Dimension(485, 65)); + setMinimumSize(new java.awt.Dimension(0, 420)); + setPreferredSize(new java.awt.Dimension(485, 410)); diskLabel.setFont(diskLabel.getFont().deriveFont(diskLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); org.openide.awt.Mnemonics.setLocalizedText(diskLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.diskLabel.text")); // NOI18N @@ -132,24 +145,66 @@ final class LocalDiskPanel extends JPanel { descLabel.setFont(descLabel.getFont().deriveFont(descLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); org.openide.awt.Mnemonics.setLocalizedText(descLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.descLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(copyImageCheckbox, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.copyImageCheckbox.text")); // NOI18N + copyImageCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + copyImageCheckboxActionPerformed(evt); + } + }); + + imageWriterErrorLabel.setFont(imageWriterErrorLabel.getFont().deriveFont(imageWriterErrorLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + imageWriterErrorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(imageWriterErrorLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.imageWriterErrorLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.jLabel1.text")); // NOI18N + + pathTextField.setText(org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.pathTextField.text")); // NOI18N + pathTextField.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyReleased(java.awt.event.KeyEvent evt) { + pathTextFieldKeyReleased(evt); + } + public void keyTyped(java.awt.event.KeyEvent evt) { + pathTextFieldKeyTyped(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.browseButton.text")); // NOI18N + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(diskLabel) - .addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(errorLabel) .addGroup(layout.createSequentialGroup() - .addComponent(timeZoneLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(noFatOrphansCheckbox) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 362, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(diskLabel) + .addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(errorLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(timeZoneLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(noFatOrphansCheckbox) + .addComponent(copyImageCheckbox) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(descLabel)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseButton)) .addGroup(layout.createSequentialGroup() .addGap(21, 21, 21) - .addComponent(descLabel))) - .addGap(0, 102, Short.MAX_VALUE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 423, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(imageWriterErrorLabel)))) + .addGap(0, 29, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -167,19 +222,76 @@ final class LocalDiskPanel extends JPanel { .addComponent(noFatOrphansCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(descLabel) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(copyImageCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(imageWriterErrorLabel) + .addContainerGap(170, Short.MAX_VALUE)) ); }// //GEN-END:initComponents + + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + String oldText = pathTextField.getText(); + // set the current directory of the FileChooser if the ImagePath Field is valid + File currentFile = new File(oldText); + if ((currentFile.getParentFile() != null) && (currentFile.getParentFile().exists())) { + fc.setCurrentDirectory(currentFile.getParentFile()); + } + + int retval = fc.showOpenDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + pathTextField.setText(path); + } + fireUpdateEvent(); + }//GEN-LAST:event_browseButtonActionPerformed + + private void copyImageCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyImageCheckboxActionPerformed + pathTextField.setEnabled(copyImageCheckbox.isSelected()); + browseButton.setEnabled(copyImageCheckbox.isSelected()); + fireUpdateEvent(); + }//GEN-LAST:event_copyImageCheckboxActionPerformed + + private void pathTextFieldKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_pathTextFieldKeyTyped + + }//GEN-LAST:event_pathTextFieldKeyTyped + + private void pathTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_pathTextFieldKeyReleased + fireUpdateEvent(); + }//GEN-LAST:event_pathTextFieldKeyReleased + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton browseButton; + private javax.swing.JCheckBox copyImageCheckbox; private javax.swing.JLabel descLabel; private javax.swing.JComboBox diskComboBox; private javax.swing.JLabel diskLabel; private javax.swing.JLabel errorLabel; + private javax.swing.JLabel imageWriterErrorLabel; + private javax.swing.JLabel jLabel1; private javax.swing.JCheckBox noFatOrphansCheckbox; + private javax.swing.JTextField pathTextField; private javax.swing.JComboBox timeZoneComboBox; private javax.swing.JLabel timeZoneLabel; // End of variables declaration//GEN-END:variables + private void fireUpdateEvent(){ + try { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } catch (Exception e) { + logger.log(Level.SEVERE, "LocalDiskPanel listener threw exception", e); //NON-NLS + MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr"), + NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr.msg"), + MessageNotifyUtil.MessageType.ERROR); + } + } + /** * Return the currently selected disk path. * @@ -214,14 +326,69 @@ final class LocalDiskPanel extends JPanel { boolean getNoFatOrphans() { return noFatOrphansCheckbox.isSelected(); } + + private static String getDefaultImageWriterFolder(){ + return Paths.get(Case.getCurrentCase().getModuleDirectory(), "Image Writer").toString(); + } + + private void setPotentialImageWriterPath(LocalDisk disk){ + + File subDirectory = Paths.get(getDefaultImageWriterFolder()).toFile(); + if (!subDirectory.exists()) { + subDirectory.mkdirs(); + } + + String path = disk.getName().replaceAll("[:]", ""); + path += " " + System.currentTimeMillis(); + path += ".vhd"; + pathTextField.setText(Paths.get(getDefaultImageWriterFolder(), path).toString()); + } + + private boolean imageWriterPathIsValid(){ + if(pathTextField.getText().isEmpty()){ + imageWriterErrorLabel.setText(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.imageWriterEmptyPathError.text")); + return false; + } + + File f = new File(pathTextField.getText()); + if(((f.getParentFile() != null) && (! f.getParentFile().exists())) || + (f.getParentFile() == null)) { + imageWriterErrorLabel.setText(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.imageWriterDirError.text")); + return false; + } + if(f.isDirectory()){ + imageWriterErrorLabel.setText(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.imageWriterIsDirError.text")); + return false; + } + if(f.exists()){ + imageWriterErrorLabel.setText(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.imageWriterFileExistsError.text")); + return false; + } + + imageWriterErrorLabel.setText(""); + return true; + } + + boolean getImageWriterEnabled(){ + return copyImageCheckbox.isSelected(); + } + + String getImageWriterPath(){ + return pathTextField.getText(); + } /** - * Should we enable the wizard's next button? Always return true because we - * control the possible selections. + * Should we enable the wizard's next button? We control all the possible + * selections except for Image Writer. * - * @return true + * @return true if panel is valid */ public boolean validatePanel() { + if(copyImageCheckbox.isSelected() && + ! imageWriterPathIsValid()){ + return false; + } + return enableNext; } @@ -318,15 +485,8 @@ final class LocalDiskPanel extends JPanel { if (ready) { selected = (LocalDisk) anItem; enableNext = true; - - try { - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); - } catch (Exception e) { - logger.log(Level.SEVERE, "LocalDiskPanel listener threw exception", e); //NON-NLS - MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr"), - NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr.msg"), - MessageNotifyUtil.MessageType.ERROR); - } + setPotentialImageWriterPath((LocalDisk) selected); + fireUpdateEvent(); } } diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java index e546e4d260..36b6dc37e3 100644 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java +++ b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java @@ -19,9 +19,13 @@ package org.sleuthkit.autopsy.coordinationservice; import java.io.IOException; -import java.util.HashMap; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; @@ -34,25 +38,28 @@ import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; +import org.openide.util.Lookup; import org.sleuthkit.autopsy.core.UserPreferences; /** * A coordination service for maintaining configuration information and * providing distributed synchronization using a shared hierarchical namespace * of nodes. - * - * TODO (JIRA 2205): Simple refactoring for general use. */ +@ThreadSafe public final class CoordinationService { - private static CuratorFramework curator = null; - private static final Map rootNodesToServices = new HashMap<>(); private static final int SESSION_TIMEOUT_MILLISECONDS = 300000; private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000; private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000; private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000; private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000 - private final Map categoryNodeToPath = new HashMap<>(); + private static final String DEFAULT_NAMESPACE_ROOT = "autopsy"; + @GuardedBy("CoordinationService.class") + private static CoordinationService instance; + private final CuratorFramework curator; + @GuardedBy("categoryNodeToPath") + private final Map categoryNodeToPath; /** * Determines if ZooKeeper is accessible with the current settings. Closes @@ -86,44 +93,31 @@ public final class CoordinationService { } /** - * Gets a coordination service for a specific namespace. + * Gets the coordination service for maintaining configuration information + * and providing distributed synchronization using a shared hierarchical + * namespace of nodes. * - * @param rootNode The name of the root node that defines the namespace. + * @return The corrdination service. * - * @return The coordination service. - * - * @throws CoordinationServiceException If an instance of the coordination - * service cannot be created. + * @throws CoordinationServiceException */ - public static synchronized CoordinationService getServiceForNamespace(String rootNode) throws CoordinationServiceException { - /* - * Connect to ZooKeeper via Curator. - */ - if (null == curator) { - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET; - String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort; - curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy); - curator.start(); - } - - /* - * Get or create a coordination service for the namespace defined by the - * specified root node. - */ - if (rootNodesToServices.containsKey(rootNode)) { - return rootNodesToServices.get(rootNode); - } else { - CoordinationService service; + public synchronized static CoordinationService getInstance() throws CoordinationServiceException { + if (null == instance) { + String rootNode; + Collection providers = Lookup.getDefault().lookupAll(CoordinationServiceNamespace.class); + Iterator it = providers.iterator(); + if (it.hasNext()) { + rootNode = it.next().getNamespaceRoot(); + } else { + rootNode = DEFAULT_NAMESPACE_ROOT; + } try { - service = new CoordinationService(rootNode); + instance = new CoordinationService(rootNode); } catch (IOException | InterruptedException | KeeperException | CoordinationServiceException ex) { - curator = null; throw new CoordinationServiceException("Failed to create coordination service", ex); } - rootNodesToServices.put(rootNode, service); - return service; } + return instance; } /** @@ -141,11 +135,23 @@ public final class CoordinationService { throw new CoordinationServiceException("Unable to access ZooKeeper"); } + /* + * Connect to ZooKeeper via Curator. + */ + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET; + String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort; + curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy); + curator.start(); + + /* + * Create the top-level root and category nodes. + */ String rootNode = rootNodeName; if (!rootNode.startsWith("/")) { rootNode = "/" + rootNode; } - + categoryNodeToPath = new ConcurrentHashMap<>(); for (CategoryNode node : CategoryNode.values()) { String nodePath = rootNode + "/" + node.getDisplayName(); try { @@ -422,8 +428,7 @@ public final class CoordinationService { CASES("cases"), MANIFESTS("manifests"), - CONFIG("config"), - RESOURCE("resource"); + CONFIG("config"); private final String displayName; diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java index 567dd38bc6..3ffe0b78fd 100644 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java +++ b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016-2017 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,15 +19,18 @@ package org.sleuthkit.autopsy.coordinationservice; /** - * Root node for Autopsy coordination service namespace. + * An interface that allows the root node of the coordination service namespace + * for the application to be specified at runtime. An application built on the + * Autopsy platform should provide at most one implementation of this interface + * (additional implementations are ignored). */ -public final class CoordinationServiceNamespace { - private static final String ROOT = "autopsy"; +public interface CoordinationServiceNamespace { - public static String getRoot() { - return ROOT; - } + /** + * Gets the name of the root node of the coordination service namespace. + * + * @return The name of the root node. + */ + public String getNamespaceRoot(); - private CoordinationServiceNamespace() { - } } diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java index 0507e55cd6..753ccd598a 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-2016 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. @@ -163,12 +163,22 @@ public final class ExecUtil { process.waitFor(timeOut, units); if (process.isAlive() && terminator.shouldTerminateProcess()) { killProcess(process); + try { + process.waitFor(); //waiting to help ensure process is shutdown before calling interrupt() or returning + } catch (InterruptedException exx) { + Logger.getLogger(ExecUtil.class.getName()).log(Level.INFO, String.format("Wait for process termination following killProcess was interrupted for command %s", processBuilder.command().get(0))); + } } } while (process.isAlive()); } catch (InterruptedException ex) { if (process.isAlive()) { killProcess(process); } + try { + process.waitFor(); //waiting to help ensure process is shutdown before calling interrupt() or returning + } catch (InterruptedException exx) { + Logger.getLogger(ExecUtil.class.getName()).log(Level.INFO, String.format("Wait for process termination following killProcess was interrupted for command %s", processBuilder.command().get(0))); + } Logger.getLogger(ExecUtil.class.getName()).log(Level.INFO, "Thread interrupted while running {0}", processBuilder.command().get(0)); // NON-NLS Thread.currentThread().interrupt(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 3559bb79d7..eb736069a7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -51,7 +51,9 @@ abstract class AbstractContentChildren extends Keys { * Uses lazy Content.Keys */ AbstractContentChildren() { - super(true); // use lazy behavior + /*This was turned off because we were getting out of memory errors when the + filter nodes were hiding nodes. Turning this off seemed to help */ + super(false); //don't use lazy behavior } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 7c28e09529..304febdf74 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -61,7 +61,7 @@ public class ArtifactStringContent implements StringContent { * @return The HTML representation of the artifact as a string. */ @Messages({ - "ArtifactStringContent.attrsTableHeader.attribute=Attribute", + "ArtifactStringContent.attrsTableHeader.type=Type", "ArtifactStringContent.attrsTableHeader.value=Value", "ArtifactStringContent.attrsTableHeader.sources=Source(s)", "ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database", @@ -90,15 +90,15 @@ public class ArtifactStringContent implements StringContent { */ buffer.append(""); //NON-NLS buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS try { Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 5377e430c2..d178b73469 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -171,8 +171,6 @@ KeywordHits.createNodeForKey.chgTime.desc=Change Time KeywordHits.createNodeForKey.chgTime.name=ChangeTime KeywordHits.createNodeForKey.accessTime.name=AccessTime KeywordHits.createNodeForKey.modTime.name=ModifiedTime -KnownFileFilterNode.selectionContext.dataSources=Data Sources -KnownFileFilterNode.selectionContext.views=Views LayoutFileNode.propertyType.parts=Parts LayoutFileNode.createSheet.name.name=Name LayoutFileNode.createSheet.name.displayName=Name @@ -221,8 +219,6 @@ ReportNode.reportNameProperty.name=Report Name ReportNode.reportNameProperty.displayName=Report Name ReportNode.reportNameProperty.desc=Name of the report ReportsListNode.displayName=Reports -SlackFileFilterNode.selectionContext.dataSources=Data Sources -SlackFileFilterNode.selectionContext.views=Views SlackFileNode.getActions.viewInNewWin.text=View in New Window SlackFileNode.getActions.viewFileInDir.text=View File in Directory TagNameNode.namePlusTags.text={0} Tags diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KnownFileFilterNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/KnownFileFilterNode.java deleted file mode 100755 index 8a6dc86f3f..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KnownFileFilterNode.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013-2014 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datamodel; - -import java.util.prefs.PreferenceChangeEvent; -import java.util.prefs.PreferenceChangeListener; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskData; - -/** - * A Filter Node responsible for conditionally filtering out Nodes that - * represent known files. - * - * Filters known files IF the option to Filter Known files for the given - * SelectionContext is set. Otherwise, does nothing. - * - * @author jwallace - */ -public class KnownFileFilterNode extends FilterNode { - - private static boolean filterFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); - private static boolean filterFromViews = UserPreferences.hideKnownFilesInViewsTree(); - - static { - UserPreferences.addChangeListener(new PreferenceChangeListener() { - @Override - public void preferenceChange(PreferenceChangeEvent evt) { - switch (evt.getKey()) { - case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE: - filterFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); - break; - case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: - filterFromViews = UserPreferences.hideKnownFilesInViewsTree(); - break; - } - } - }); - } - - public enum SelectionContext { - - DATA_SOURCES(NbBundle.getMessage(KnownFileFilterNode.class, "KnownFileFilterNode.selectionContext.dataSources")), - VIEWS(NbBundle.getMessage(KnownFileFilterNode.class, "KnownFileFilterNode.selectionContext.views")), - OTHER(""); // Subnode of another node. - - private final String displayName; - - SelectionContext(String displayName) { - this.displayName = displayName; - } - - public static SelectionContext getContextFromName(String name) { - if (name.equals(DATA_SOURCES.getName())) { - return DATA_SOURCES; - } else if (name.equals(VIEWS.getName())) { - return VIEWS; - } else { - return OTHER; - } - } - - private String getName() { - return displayName; - } - } - - /** - * Create a KnownFileFilterNode from the given Node. Note that the Node - * should be from the directory tree. - * - * @param arg - * @param context - */ - public KnownFileFilterNode(Node arg, SelectionContext context) { - super(arg, new KnownFileFilterChildren(arg, context)); - } - - private KnownFileFilterNode(Node arg, boolean filter) { - super(arg, new KnownFileFilterChildren(arg, filter)); - } - - /** - * Get the selection context of a Node in the DirectoryTree. - * - * @param n - * - * @return - */ - public static SelectionContext getSelectionContext(Node n) { - if (n == null || n.getParentNode() == null) { - // Parent of root node or root node. Occurs during case open / close. - return SelectionContext.OTHER; - } else if (n.getParentNode().getParentNode() == null) { - // One level below root node. Should be one of DataSources, Views, or Results - return SelectionContext.getContextFromName(n.getDisplayName()); - } else { - return getSelectionContext(n.getParentNode()); - } - } - - /** - * Complementary class to KnownFileFilterNode. - * - * Filters out children Nodes that represent known files. Otherwise, returns - * the original node wrapped in another instance of the KnownFileFilterNode. - * - * @author jwallace - */ - private static class KnownFileFilterChildren extends FilterNode.Children { - - /** - * True if this KnownFileFilterChildren should filter out known files. - */ - private boolean filter; - - /** - * Constructor used when the context has already been determined. - * - * @param arg - * @param filter - */ - private KnownFileFilterChildren(Node arg, boolean filter) { - super(arg); - this.filter = filter; - } - - /** - * Constructor used when the context has not been determined. - * - * @param arg - * @param context - */ - private KnownFileFilterChildren(Node arg, KnownFileFilterNode.SelectionContext context) { - super(arg); - - switch (context) { - case DATA_SOURCES: - filter = filterFromDataSources; - break; - case VIEWS: - filter = filterFromViews; - break; - default: - filter = false; - break; - } - } - - @Override - protected Node[] createNodes(Node arg) { - if (filter) { - // Filter out child nodes that represent known files - AbstractFile file = arg.getLookup().lookup(AbstractFile.class); - if (file != null && (file.getKnown() == TskData.FileKnown.KNOWN)) { - return new Node[]{}; - } - } - return new Node[]{new KnownFileFilterNode(arg, filter)}; - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileFilterNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileFilterNode.java deleted file mode 100644 index af43970e8c..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileFilterNode.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013-2014 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datamodel; - -import java.util.prefs.PreferenceChangeEvent; -import java.util.prefs.PreferenceChangeListener; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskData; - -/** - * A Filter Node responsible for conditionally filtering out Nodes that - * represent slack files. - * - * Filters known files IF the option to Filter Slack files for the given - * SelectionContext is set. Otherwise, does nothing. - * - */ -public class SlackFileFilterNode extends FilterNode { - - private static boolean filterFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); - private static boolean filterFromViews = UserPreferences.hideSlackFilesInViewsTree(); - - static { - UserPreferences.addChangeListener(new PreferenceChangeListener() { - @Override - public void preferenceChange(PreferenceChangeEvent evt) { - switch (evt.getKey()) { - case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE: - filterFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); - break; - case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: - filterFromViews = UserPreferences.hideSlackFilesInViewsTree(); - break; - } - } - }); - } - - public enum SelectionContext { - - DATA_SOURCES(NbBundle.getMessage(SlackFileFilterNode.class, "SlackFileFilterNode.selectionContext.dataSources")), - VIEWS(NbBundle.getMessage(SlackFileFilterNode.class, "SlackFileFilterNode.selectionContext.views")), - OTHER(""); // Subnode of another node. - - private final String displayName; - - SelectionContext(String displayName) { - this.displayName = displayName; - } - - public static SelectionContext getContextFromName(String name) { - if (name.equals(DATA_SOURCES.getName())) { - return DATA_SOURCES; - } else if (name.equals(VIEWS.getName())) { - return VIEWS; - } else { - return OTHER; - } - } - - private String getName() { - return displayName; - } - } - - /** - * Create a SlackFileFilterNode from the given Node. Note that the Node - * should be from the directory tree. - * - * @param arg - * @param context - */ - public SlackFileFilterNode(Node arg, SelectionContext context) { - super(arg, new SlackFileFilterChildren(arg, context)); - } - - private SlackFileFilterNode(Node arg, boolean filter) { - super(arg, new SlackFileFilterChildren(arg, filter)); - } - - /** - * Get the selection context of a Node in the DirectoryTree. - * - * @param n - * - * @return - */ - public static SelectionContext getSelectionContext(Node n) { - if (n == null || n.getParentNode() == null) { - // Parent of root node or root node. Occurs during case open / close. - return SelectionContext.OTHER; - } else if (n.getParentNode().getParentNode() == null) { - // One level below root node. Should be one of DataSources, Views, or Results - return SelectionContext.getContextFromName(n.getDisplayName()); - } else { - return getSelectionContext(n.getParentNode()); - } - } - - /** - * Complementary class to SlackFileFilterNode. - * - * Filters out children Nodes that represent slack files. Otherwise, returns - * the original node wrapped in another instance of the SlackFileFilterNode. - * - * @author jwallace - */ - private static class SlackFileFilterChildren extends FilterNode.Children { - - /** - * True if this SlackFileFilterChildren should filter out slack files. - */ - private boolean filter; - - /** - * Constructor used when the context has already been determined. - * - * @param arg - * @param filter - */ - private SlackFileFilterChildren(Node arg, boolean filter) { - super(arg); - this.filter = filter; - } - - /** - * Constructor used when the context has not been determined. - * - * @param arg - * @param context - */ - private SlackFileFilterChildren(Node arg, SlackFileFilterNode.SelectionContext context) { - super(arg); - - switch (context) { - case DATA_SOURCES: - filter = filterFromDataSources; - break; - case VIEWS: - filter = filterFromViews; - break; - default: - filter = false; - break; - } - } - - @Override - protected Node[] createNodes(Node arg) { - if (filter) { - // Filter out child nodes that represent slack files - AbstractFile file = arg.getLookup().lookup(AbstractFile.class); - if ((file != null) && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { - return new Node[]{}; - } - } - return new Node[]{new SlackFileFilterNode(arg, filter)}; - } - } -} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties index 4a8de9a48b..ad62c83c2f 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties @@ -10,3 +10,4 @@ RawDSInputPanel.jBreakFileUpLabel.text=Break image up into: RawDSInputPanel.jNoBreakupRadioButton.text=Do not break up RawDSInputPanel.j2GBBreakupRadioButton.text=2GB chunks RawDSInputPanel.timeZoneLabel.text=Please select the input timezone: + diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterChildren.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterChildren.java deleted file mode 100644 index 3acab048f5..0000000000 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterChildren.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.directorytree; - -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; - -/** - * This class is used for the creation of all the children for the - * DataResultFilterNode that created in the DataResultFilterNode.java. - * - */ -class DataResultFilterChildren extends FilterNode.Children { - - ExplorerManager sourceEm; - - /** - * the constructor - */ - public DataResultFilterChildren(Node arg, ExplorerManager sourceEm) { - super(arg); - this.sourceEm = sourceEm; - } - - @Override - protected Node copyNode(Node arg0) { - return new DataResultFilterNode(arg0, sourceEm); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index de20df20e7..81820218ec 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,8 @@ import java.beans.PropertyVetoException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import org.openide.explorer.ExplorerManager; @@ -33,6 +35,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; @@ -58,6 +61,7 @@ import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.SlackFile; +import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.VirtualDirectory; @@ -67,11 +71,37 @@ import org.sleuthkit.datamodel.VirtualDirectory; */ public class DataResultFilterNode extends FilterNode { - private ExplorerManager sourceEm; + private static boolean filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); + private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); + private static boolean filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); + private static boolean filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); - private final DisplayableItemNodeVisitor> getActionsDIV; + static { + UserPreferences.addChangeListener(new PreferenceChangeListener() { + @Override + public void preferenceChange(PreferenceChangeEvent evt) { + switch (evt.getKey()) { + case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE: + filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); + break; + case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: + filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); + break; + case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE: + filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); + break; + case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: + filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); + break; + } + } + }); + } - private final DisplayableItemNodeVisitor getPreferredActionsDIV; + static private final DisplayableItemNodeVisitor> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); + private final DisplayableItemNodeVisitor getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); + + private final ExplorerManager sourceEm; /** * @@ -81,8 +111,17 @@ public class DataResultFilterNode extends FilterNode { public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); this.sourceEm = em; - getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); - getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); + + } + + /** + * + * @param node Root node to be passed to DataResult viewers + * @param em ExplorerManager for component that is creating the node + */ + private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { + super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); + this.sourceEm = em; } /** @@ -150,6 +189,64 @@ public class DataResultFilterNode extends FilterNode { return propertySets; } + /** + * This class is used for the creation of all the children for the + * DataResultFilterNode that created in the DataResultFilterNode.java. + * + */ + private static class DataResultFilterChildren extends FilterNode.Children { + + private final ExplorerManager sourceEm; + + private boolean filterKnown; + private boolean filterSlack; + + /** + * the constructor + */ + private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) { + super(arg); + switch (SelectionContext.getSelectionContext(arg)) { + case DATA_SOURCES: + filterSlack = filterSlackFromDataSources; + filterKnown = filterKnownFromDataSources; + break; + case VIEWS: + filterSlack = filterSlackFromViews; + filterKnown = filterKnownFromViews; + break; + default: + filterSlack = false; + filterKnown = false; + break; + } + this.sourceEm = sourceEm; + } + + private DataResultFilterChildren(Node arg, ExplorerManager sourceEm, boolean filterKnown, boolean filterSlack) { + super(arg); + this.filterKnown = filterKnown; + this.filterSlack = filterSlack; + this.sourceEm = sourceEm; + } + + @Override + protected Node[] createNodes(Node key) { + AbstractFile file = key.getLookup().lookup(AbstractFile.class); + if (file != null) { + if (filterKnown && (file.getKnown() == TskData.FileKnown.KNOWN)) { + // Filter out child nodes that represent known files + return new Node[]{}; + } + if (filterSlack && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { + // Filter out child nodes that represent slack files + return new Node[]{}; + } + } + return new Node[]{new DataResultFilterNode(key, sourceEm, filterKnown, filterSlack)}; + } + } + /** * Uses the default nodes actions per node, adds some custom ones and * returns them per visited node type @@ -236,10 +333,10 @@ public class DataResultFilterNode extends FilterNode { // The base class Action is "Collapse All", inappropriate. return null; } - + @Override public List visit(FileTypesNode fileTypes) { - return defaultVisit(fileTypes); + return defaultVisit(fileTypes); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 51b4bf7cb1..da788f4363 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -65,12 +65,10 @@ import org.sleuthkit.autopsy.datamodel.EmptyNode; import org.sleuthkit.autopsy.datamodel.ExtractedContent; import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType; import org.sleuthkit.autopsy.datamodel.KeywordHits; -import org.sleuthkit.autopsy.datamodel.KnownFileFilterNode; import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.Results; import org.sleuthkit.autopsy.datamodel.ResultsNode; import org.sleuthkit.autopsy.datamodel.RootContentChildren; -import org.sleuthkit.autopsy.datamodel.SlackFileFilterNode; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.autopsy.datamodel.Views; import org.sleuthkit.autopsy.datamodel.ViewsNode; @@ -656,22 +654,17 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat //set node, wrap in filter node first to filter out children Node drfn = new DataResultFilterNode(originNode, DirectoryTreeTopComponent.this.em); - Node kffn = new KnownFileFilterNode(drfn, KnownFileFilterNode.getSelectionContext(originNode)); - Node sffn = new SlackFileFilterNode(kffn, SlackFileFilterNode.getSelectionContext(originNode)); // Create a TableFilterNode with knowledge of the node's type to allow for column order settings - //Special case for when File Type Identification has not yet been run and - //there are no mime types to populate Files by Mime Type Tree if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) { + //Special case for when File Type Identification has not yet been run and + //there are no mime types to populate Files by Mime Type Tree EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text()); - Node emptyDrfn = new DataResultFilterNode(emptyNode, DirectoryTreeTopComponent.this.em); - Node emptyKffn = new KnownFileFilterNode(emptyDrfn, KnownFileFilterNode.getSelectionContext(emptyNode)); - Node emptySffn = new SlackFileFilterNode(emptyKffn, SlackFileFilterNode.getSelectionContext(originNode)); - dataResult.setNode(new TableFilterNode(emptySffn, true, "This Node Is Empty")); //NON-NLS + dataResult.setNode(new TableFilterNode(emptyNode, true, "This Node Is Empty")); //NON-NLS } else if (originNode instanceof DisplayableItemNode) { - dataResult.setNode(new TableFilterNode(sffn, true, ((DisplayableItemNode) originNode).getItemType())); + dataResult.setNode(new TableFilterNode(drfn, true, ((DisplayableItemNode) originNode).getItemType())); } else { - dataResult.setNode(new TableFilterNode(sffn, true)); + dataResult.setNode(new TableFilterNode(drfn, true)); } String displayName = ""; diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/SelectionContext.java b/Core/src/org/sleuthkit/autopsy/directorytree/SelectionContext.java new file mode 100644 index 0000000000..c204be3b00 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/SelectionContext.java @@ -0,0 +1,71 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 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.directorytree; + +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import static org.sleuthkit.autopsy.directorytree.Bundle.*; + +@NbBundle.Messages({"SelectionContext.dataSources=Data Sources", + "SelectionContext.views=Views"}) +enum SelectionContext { + DATA_SOURCES(SelectionContext_dataSources()), + VIEWS(SelectionContext_views()), + OTHER(""); // Subnode of another node. + + private final String displayName; + + private SelectionContext(String displayName) { + this.displayName = displayName; + } + + public static SelectionContext getContextFromName(String name) { + if (name.equals(DATA_SOURCES.getName())) { + return DATA_SOURCES; + } else if (name.equals(VIEWS.getName())) { + return VIEWS; + } else { + return OTHER; + } + } + + private String getName() { + return displayName; + } + + /** + * Get the selection context of a Node in the DirectoryTree. + * + * @param n + * + * @return + */ + public static SelectionContext getSelectionContext(Node n) { + if (n == null || n.getParentNode() == null) { + // Parent of root node or root node. Occurs during case open / close. + return SelectionContext.OTHER; + } else if (n.getParentNode().getParentNode() == null) { + // One level below root node. Should be one of DataSources, Views, or Results + return SelectionContext.getContextFromName(n.getDisplayName()); + } else { + return getSelectionContext(n.getParentNode()); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/images/export16.png b/Core/src/org/sleuthkit/autopsy/images/export16.png new file mode 100644 index 0000000000..aa51e8f870 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/export16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/import16.png b/Core/src/org/sleuthkit/autopsy/images/import16.png new file mode 100644 index 0000000000..18b35db444 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/import16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/new16.png b/Core/src/org/sleuthkit/autopsy/images/new16.png new file mode 100644 index 0000000000..f286d2b6c0 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/new16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle.properties b/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle.properties new file mode 100644 index 0000000000..67a92df30f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/imagewriter/Bundle.properties @@ -0,0 +1,8 @@ +# 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. + +ImageWriterService.serviceName=Image Writer +ImageWriterService.waitingForVHDs=Waiting for VHD(s) to complete +ImageWriterService.shouldWait=Wait for VHD(s) in progress to complete? +ImageWriterService.localDisk=Local disk image copy diff --git a/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java new file mode 100644 index 0000000000..55bfa23326 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java @@ -0,0 +1,288 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.imagewriter; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import org.netbeans.api.progress.ProgressHandle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.RuntimeProperties; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; +import org.sleuthkit.datamodel.SleuthkitJNI; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * The ImageWriter class is used to complete VHD copies created from local disks + * after the ingest process completes. The AddImageTask for this data source must have included + * a non-empty imageWriterPath parameter to enable Image Writer. + * + * Most of the cancellation/cleanup is handled through ImageWriterService + */ +class ImageWriter implements PropertyChangeListener{ + + private final Logger logger = Logger.getLogger(ImageWriter.class.getName()); + + private final Long dataSourceId; + + private Long imageHandle = null; + private Future finishTask = null; + private ProgressHandle progressHandle = null; + private ScheduledFuture progressUpdateTask = null; + private boolean isCancelled = false; + private boolean isStarted = false; + private final Object currentTasksLock = new Object(); // Get this lock before accessing imageHandle, finishTask, progressHandle, progressUpdateTask, + // isCancelled, isStarted, or isFinished + + private ScheduledThreadPoolExecutor periodicTasksExecutor = null; + private final boolean doUI; + + /** + * Create the Image Writer object. + * After creation, startListeners() should be called. + * @param dataSourceId + */ + ImageWriter(Long dataSourceId){ + this.dataSourceId = dataSourceId; + doUI = RuntimeProperties.runningWithGUI(); + } + + /** + * Add this ImageWriter object as a listener to the necessary events + */ + void subscribeToEvents(){ + IngestManager.getInstance().addIngestJobEventListener(this); + } + + /** + * Deregister this object from the events. This is ok to call multiple times. + */ + void unsubscribeFromEvents(){ + IngestManager.getInstance().removeIngestJobEventListener(this); + } + + /** + * Handle the events: + * DATA_SOURCE_ANALYSIS_COMPLETED - start the finish image process and clean up after it is complete + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + if(evt instanceof DataSourceAnalysisCompletedEvent){ + + DataSourceAnalysisCompletedEvent event = (DataSourceAnalysisCompletedEvent)evt; + + if(event.getDataSource() != null){ + long imageId = event.getDataSource().getId(); + String name = event.getDataSource().getName(); + + // Check that the event corresponds to this datasource + if(imageId != dataSourceId){ + return; + } + new Thread(() -> { + startFinishImage(name); + }).start(); + + } else { + logger.log(Level.SEVERE, "DataSourceAnalysisCompletedEvent did not contain a dataSource object"); //NON-NLS + } + } + } + + private void startFinishImage(String dataSourceName){ + + synchronized(currentTasksLock){ + if(isCancelled){ + return; + } + + // If we've already started the finish process for this datasource, return. + // Multiple DataSourceAnalysisCompletedEvent events can come from + // the same image if more ingest modules are run later + if(isStarted){ + return; + } + + Image image; + try{ + image = Case.getCurrentCase().getSleuthkitCase().getImageById(dataSourceId); + imageHandle = image.getImageHandle(); + } catch (IllegalStateException ex){ + // This exception means that getCurrentCase() failed because no case was open. + // This can happen when the user closes the case while ingest is ongoing - canceling + // ingest fires off the DataSourceAnalysisCompletedEvent while the case is in the + // process of closing. + logger.log(Level.WARNING, String.format("Case closed before ImageWriter could start the finishing process for %s", + dataSourceName)); + return; + } catch (TskCoreException ex){ + logger.log(Level.SEVERE, "Error loading image", ex); + return; + } + + logger.log(Level.INFO, String.format("Finishing VHD image for %s", + dataSourceName)); //NON-NLS + + if(doUI){ + periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("image-writer-progress-update-%d").build()); //NON-NLS + progressHandle = ProgressHandle.createHandle("Image writer - " + dataSourceName); + progressHandle.start(100); + progressUpdateTask = periodicTasksExecutor.scheduleAtFixedRate( + new ProgressUpdateTask(progressHandle, imageHandle), 0, 250, TimeUnit.MILLISECONDS); + } + + // The added complexity here with the Future is because we absolutely need to make sure + // the call to finishImageWriter returns before allowing the TSK data structures to be freed + // during case close. + finishTask = Executors.newSingleThreadExecutor().submit(() -> { + try{ + SleuthkitJNI.finishImageWriter(imageHandle); + } catch (TskCoreException ex){ + logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS + } + }); + + // Setting this means that finishTask and all the UI updaters are initialized (if running UI) + isStarted = true; + } + + // Wait for finishImageWriter to complete + try{ + // The call to get() can happen multiple times if the user closes the case, which is ok + finishTask.get(); + } catch (InterruptedException | ExecutionException ex){ + logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS + } + + synchronized(currentTasksLock){ + if(doUI){ + // Some of these may be called twice if the user closes the case + progressUpdateTask.cancel(true); + progressHandle.finish(); + periodicTasksExecutor.shutdown(); + } + } + + logger.log(Level.INFO, String.format("Finished writing VHD image for %s", dataSourceName)); //NON-NLS + } + + /** + * If a task hasn't been started yet, set the cancel flag so it can no longer + * start. + * This is intended to be used in case close so a job doesn't suddenly start + * up during cleanup. + */ + void cancelIfNotStarted(){ + synchronized(currentTasksLock){ + if(! isStarted){ + isCancelled = true; + } + } + } + + /** + * Check if the finishTask process is running. + * @return true if the finish task is still going on, false if it is finished or + * never started + */ + boolean jobIsInProgress(){ + synchronized(currentTasksLock){ + return((isStarted) && (! finishTask.isDone())); + } + } + + /** + * Cancels a single job. + * Does not wait for the job to complete. Safe to call with Image Writer in any state. + */ + void cancelJob(){ + synchronized(currentTasksLock){ + // All of the following is redundant but safe to call on a complete job + isCancelled = true; + + if(isStarted){ + SleuthkitJNI.cancelFinishImage(imageHandle); + + // Stop the progress bar update task. + // The thread from startFinishImage will also stop it + // once the task completes, but we don't have a guarantee on + // when that happens. + // Since we've stopped the update task, we'll stop the associated progress + // bar now, too. + if(doUI){ + progressUpdateTask.cancel(true); + progressHandle.finish(); + } + } + } + } + + /** + * Blocks while all finishImage tasks complete. + * Also makes sure the progressUpdateTask is canceled. + */ + void waitForJobToFinish(){ + synchronized(currentTasksLock){ + // Wait for the finish task to end + if(isStarted){ + try{ + finishTask.get(); + } catch (InterruptedException | ExecutionException ex){ + Logger.getLogger(ImageWriter.class.getName()).log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS + } + if(doUI){ + progressUpdateTask.cancel(true); + } + } + } + } + + /** + * Task to query the Sleuthkit processing to get the percentage done. + */ + private final class ProgressUpdateTask implements Runnable { + final long imageHandle; + final ProgressHandle progressHandle; + + ProgressUpdateTask(ProgressHandle progressHandle, long imageHandle){ + this.imageHandle = imageHandle; + this.progressHandle = progressHandle; + } + + @Override + public void run() { + try { + int progress = SleuthkitJNI.getFinishImageProgress(imageHandle); + progressHandle.progress(progress); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Unexpected exception in ProgressUpdateTask", ex); //NON-NLS + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriterService.java b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriterService.java new file mode 100644 index 0000000000..8f3791d53c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriterService.java @@ -0,0 +1,123 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.imagewriter; + +import java.util.HashSet; +import java.util.Set; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.openide.util.lookup.ServiceProviders; +import org.sleuthkit.autopsy.framework.AutopsyService; + +@ServiceProviders(value = {@ServiceProvider(service = AutopsyService.class)}) + +/** + * Creates and handles closing of ImageWriter objects. + * Currently, ImageWriter is only enabled for local disks, and local disks can + * not be processed in multi user mode. If ImageWriter is ever enabled for multi user + * cases this code will need to be revised. + */ + +public class ImageWriterService implements AutopsyService { + + private static final Set imageWriters = new HashSet<>(); // Contains all Image Writer objects + private static final Object imageWritersLock = new Object(); // Get this lock before accessing currentImageWriters + + /** + * Create an image writer object for the given data source ID. + * @param imageId ID for the image + */ + public static void createImageWriter(Long imageId){ + + // ImageWriter objects are created during the addImageTask. They can not arrive while + // we're closing case resources so we don't need to worry about one showing up while + // doing our close/cleanup. + synchronized(imageWritersLock){ + ImageWriter writer = new ImageWriter(imageId); + writer.subscribeToEvents(); + imageWriters.add(writer); + } + } + + @Override + public String getServiceName() { + return NbBundle.getMessage(this.getClass(), "ImageWriterService.serviceName"); + } + + @Override + public void closeCaseResources(CaseContext context) throws AutopsyServiceException { + context.getProgressIndicator().progress(NbBundle.getMessage(this.getClass(), "ImageWriterService.waitingForVHDs")); + + synchronized(imageWritersLock){ + // If any of our ImageWriter objects haven't started the finish task, set the cancel flag + // to make sure they don't start now. The reason they haven't started is that + // ingest was not complete, and the user already confirmed that they want to exit + // even though ingest is not complete so we will take that to mean that they + // also don't want to wait for Image Writer. + for(ImageWriter writer: imageWriters){ + writer.cancelIfNotStarted(); + } + + // Test whether any finishImage tasks are in progress + boolean jobsAreInProgress = false; + for(ImageWriter writer: imageWriters){ + if(writer.jobIsInProgress()){ + jobsAreInProgress = true; + break; + } + } + + if(jobsAreInProgress){ + // If jobs are in progress, ask the user if they want to wait for them to complete + NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation( + NbBundle.getMessage(this.getClass(), "ImageWriterService.shouldWait"), + NbBundle.getMessage(this.getClass(), "ImageWriterService.localDisk"), + NotifyDescriptor.YES_NO_OPTION, + NotifyDescriptor.WARNING_MESSAGE); + descriptor.setValue(NotifyDescriptor.NO_OPTION); + Object response = DialogDisplayer.getDefault().notify(descriptor); + + if(response == DialogDescriptor.NO_OPTION){ + // Cancel all the jobs + for(ImageWriter writer: imageWriters){ + writer.cancelJob(); + } + } + + // Wait for all finishImage jobs to complete. If the jobs got cancelled + // this will be very fast. + for(ImageWriter writer: imageWriters){ + writer.waitForJobToFinish(); + } + + } + + // Stop listening for events + for(ImageWriter writer: imageWriters){ + writer.unsubscribeFromEvents(); + } + + // Clear out the list of Image Writers + imageWriters.clear(); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java index a154fcb6db..8b1c81a19a 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java @@ -25,6 +25,7 @@ import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -413,19 +414,34 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { FilesSetDefsPanel fileIngestFilterPanel; fileIngestFilterPanel = new FilesSetDefsPanel(FilesSetDefsPanel.PANEL_TYPE.FILE_INGEST_FILTERS); fileIngestFilterPanel.load(); + //save the filters that exist before any are created + final ArrayList oldFilterList = new ArrayList<>(Arrays.asList(getComboBoxContents())); dialog.addApplyButtonListener( (ActionEvent e) -> { fileIngestFilterPanel.store(); + ArrayList newFilterList = new ArrayList<>(); + try { + newFilterList.addAll(FilesSetsManager.getInstance().getCustomFileIngestFilters().values()); + } catch (FilesSetsManager.FilesSetsManagerException ex) { + logger.log(Level.SEVERE, "Failed to get user created file ingest filters, only default available for selection", ex); //NON-NLS + } + for (FilesSet filter : newFilterList) { //getting one of the recently created filters + if (!oldFilterList.contains(filter.getName())) { + //set newly created filter to selected filter + settings.setFileIngestFilter(filter); + break; + } + } fileIngestFilterComboBox.setModel(new DefaultComboBoxModel<>(getComboBoxContents())); + //set the selected filter after the comboBox Contents were updated to include it + fileIngestFilterComboBox.setSelectedItem(settings.getFileIngestFilter().getName()); dialog.close(); } ); dialog.display(fileIngestFilterPanel); - + //return to saved selection in case they cancel out of filter creation fileIngestFilterComboBox.setSelectedItem(settings.getFileIngestFilter().getName()); - } else if (evt.getActionCommand().equals("comboBoxChanged")) { - try { Map fileIngestFilters = FilesSetsManager.getInstance() .getCustomFileIngestFilters(); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java index 28bf7a623e..d10b987873 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java @@ -47,6 +47,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen private final static int INDEX_OF_SETTINGS_PANEL = 2; private ProfileSettingsPanel profilePanel; private final static int INDEX_OF_PROFILE_PANEL = 1; + private int indexOfPreviousTab; /** * This panel implements a property change listener that listens to ingest * job events so it can disable the buttons on the panel if ingest is @@ -58,6 +59,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen public IngestOptionsPanel() { initComponents(); customizeComponents(); + indexOfPreviousTab = tabbedPane.getSelectedIndex(); } private void customizeComponents() { @@ -78,15 +80,16 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen @Override public void stateChanged(ChangeEvent e) { if (e.getSource() instanceof JTabbedPane) { - //because we can have two filterPanels open at the same time - //we need to save the settings when we change tabs otherwise - //they could be overwritten with out of date - if (tabbedPane.getSelectedIndex() == INDEX_OF_FILTER_PANEL) { - filterPanel.load(); - } - else { - filterPanel.saveSettings(); - } + //If we are switching to a filter panel we should load + //load the filter panel to ensure it is up to date + //incase a filter was addded through the Profile->new->create new filter manner + if (tabbedPane.getSelectedIndex() == INDEX_OF_FILTER_PANEL && tabbedPane.getSelectedIndex() != indexOfPreviousTab) { + filterPanel.load(); + } + //save the contents of whichever Tab we just switched from + saveTabByIndex(indexOfPreviousTab); + //save the index of the current tab for the next time we switch + indexOfPreviousTab = tabbedPane.getSelectedIndex(); } } }); @@ -160,8 +163,30 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen */ @Override public void saveSettings() { - filterPanel.store(); - settingsPanel.store(); + saveTabByIndex(tabbedPane.getSelectedIndex()); + } + + /** + * Save the panel which is in the tab corresponding to the specified index. + * + * @param index - the index of the tab you wish to save the contents of + */ + private void saveTabByIndex(int index) { + //Because we can create filters in two seperate windows here we need + //to be careful not to save an out of date list over the current list + switch (index) { + case (INDEX_OF_FILTER_PANEL): + filterPanel.saveSettings(); + break; + case (INDEX_OF_PROFILE_PANEL): + profilePanel.saveSettings(); + break; + case (INDEX_OF_SETTINGS_PANEL): + settingsPanel.saveSettings(); + break; + default: + //don't save anything if it wasn't a tab index that should exist + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form index 6ced55aa7e..5b7b56cdf8 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form @@ -27,22 +27,28 @@ - + - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -60,7 +66,7 @@ - + @@ -97,13 +103,10 @@ - - - - - - + + + @@ -115,9 +118,16 @@ - + + + + + + + + + - @@ -128,7 +138,7 @@ - + @@ -355,5 +365,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java index c057f08477..94fd0626e5 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.ingest; +import java.awt.Cursor; import java.util.Map; import java.util.TreeMap; import javax.swing.DefaultListModel; @@ -44,12 +45,15 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op "ProfileSettingsPanel.deleteProfileButton.text=Delete Profile", "ProfileSettingsPanel.messages.filterLoadFailed=Failed to load file ingest filter", "# {0} - profile name", - "ProfileSettingsPanel.doFileSetsDialog.duplicateProfile.text=Profile with name {0} already exists." + "ProfileSettingsPanel.doFileSetsDialog.duplicateProfile.text=Profile with name {0} already exists.", + "ProfileSettingsPanel.infoTextArea.text=An Ingest Profile runs a preconfigured set of ingest modules" + + " on some or all of the files in a data source. Create a profile if you frequently run the same set of modules on a subset of the files." }) private final DefaultListModel profilesListModel; private Map profiles; private ProfilePanel panel; + private boolean canBeEnabled; //if something can be enabled ingest is not running /** * Creates new form ProfileOptionsPanel @@ -60,8 +64,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op this.profileList.setModel(profilesListModel); this.profileList.addListSelectionListener(new ProfileSettingsPanel.ProfileListSelectionListener()); ingestWarningLabel.setVisible(false); - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); + canBeEnabled = !IngestManager.getInstance().isIngestRunning(); + refreshButtons(); } /** @@ -91,6 +95,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op selectedModulesLabel = new javax.swing.JLabel(); ingestWarningLabel = new javax.swing.JLabel(); jSeparator2 = new javax.swing.JSeparator(); + jScrollPane2 = new javax.swing.JScrollPane(); + infoTextArea = new javax.swing.JTextArea(); setBorder(javax.swing.BorderFactory.createEtchedBorder()); setPreferredSize(new java.awt.Dimension(800, 488)); @@ -175,6 +181,18 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL); + jScrollPane2.setFont(jScrollPane2.getFont().deriveFont(jScrollPane2.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + + infoTextArea.setEditable(false); + infoTextArea.setBackground(new java.awt.Color(240, 240, 240)); + infoTextArea.setColumns(20); + infoTextArea.setFont(infoTextArea.getFont().deriveFont(infoTextArea.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + infoTextArea.setLineWrap(true); + infoTextArea.setRows(3); + infoTextArea.setText(org.openide.util.NbBundle.getMessage(ProfileSettingsPanel.class, "ProfileSettingsPanel.infoTextArea.text")); // NOI18N + infoTextArea.setWrapStyleWord(true); + jScrollPane2.setViewportView(infoTextArea); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -182,17 +200,21 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(profileListLabel) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(editProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(profileListLabel)) + .addGap(6, 6, 6)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(profileListPane, javax.swing.GroupLayout.PREFERRED_SIZE, 339, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(editProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(6, 6, 6))) + .addComponent(profileListPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -208,7 +230,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(ingestWarningLabel) - .addGap(0, 0, Short.MAX_VALUE)) + .addGap(0, 69, Short.MAX_VALUE)) .addComponent(profileDescPane, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(selectedModulesPane, javax.swing.GroupLayout.Alignment.TRAILING))) .addGroup(layout.createSequentialGroup() @@ -229,18 +251,18 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteProfileButton, editProfileButton, newProfileButton}); + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jScrollPane2, profileListPane}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(profileListLabel) - .addComponent(profileDescLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addComponent(profileDescLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(profileDescPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -251,8 +273,14 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(selectedModulesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(selectedModulesPane)) - .addComponent(profileListPane, javax.swing.GroupLayout.DEFAULT_SIZE, 415, Short.MAX_VALUE)) + .addComponent(selectedModulesPane, javax.swing.GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(profileListLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(profileListPane, javax.swing.GroupLayout.DEFAULT_SIZE, 346, Short.MAX_VALUE) + .addGap(9, 9, 9))) .addGap(4, 4, 4) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -260,7 +288,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(ingestWarningLabel)) .addContainerGap()) - .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING))) + .addComponent(jSeparator2))) ); }// //GEN-END:initComponents @@ -288,10 +316,9 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op * @param isEnabled */ void enableButtons(boolean isEnabled) { - newProfileButton.setEnabled(isEnabled); - editProfileButton.setEnabled(isEnabled); - deleteProfileButton.setEnabled(isEnabled); - ingestWarningLabel.setVisible(!isEnabled); + canBeEnabled = isEnabled; //update value of canBeEnabled to be used by refresh + refreshButtons(); + ingestWarningLabel.setVisible(!canBeEnabled); } /** @@ -307,23 +334,20 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op this.filterNameText.setText(""); this.selectedModulesArea.setText(""); } - refreshEditDeleteButtons(); + refreshButtons(); } /** - * When Ingest is not running this will changed enabled status of the edit - * and delete buttons to reflect their current availability. + * When Ingest is not running this will change the enabled status of the + * edit and delete buttons to reflect their current availability. */ - private void refreshEditDeleteButtons() { - if (newProfileButton.isEnabled()) { - if (profilesListModel.isEmpty()) { - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); - } else { - editProfileButton.setEnabled(true); - deleteProfileButton.setEnabled(true); - } - } + private void refreshButtons() { + IngestProfile selectedProfile = ProfileSettingsPanel.this.profileList.getSelectedValue(); + boolean profileIsSelected = (selectedProfile != null); + newProfileButton.setEnabled(canBeEnabled); + editProfileButton.setEnabled(canBeEnabled && profileIsSelected); + deleteProfileButton.setEnabled(canBeEnabled && profileIsSelected); + } @@ -340,6 +364,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op */ private void doProfileDialog(IngestProfile selectedProfile) { // Create a files set defintion panel. + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + //start wait cursor for ingest job settings construction if (selectedProfile != null) { // Editing an existing set definition. panel = new ProfilePanel(selectedProfile); @@ -347,6 +373,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op // Creating a new set definition. panel = new ProfilePanel(); } + //end wait Cursor for ingest job settings construction + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); // Do a dialog box with the profilePanel till the user enters a name or chooses cancel int option = JOptionPane.OK_OPTION; do { @@ -366,7 +394,6 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op panel.saveSettings(); load(); } - } @Override @@ -392,7 +419,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op if (currentIndex < 0 || currentIndex >= profilesListModel.getSize()) { currentIndex = 0; } - refreshEditDeleteButtons(); + refreshButtons(); this.profileList.setSelectedIndex(currentIndex); } @@ -406,11 +433,10 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op // Get the selected interesting files set and populate the set // components. IngestProfile selectedProfile = ProfileSettingsPanel.this.profileList.getSelectedValue(); + refreshButtons(); if (selectedProfile != null) { profileDescArea.setText(selectedProfile.getDescription()); filterNameText.setText(selectedProfile.getFileIngestFilter()); - editProfileButton.setEnabled(true); - deleteProfileButton.setEnabled(true); try { Map fileIngestFilters = FilesSetsManager.getInstance().getCustomFileIngestFilters(); for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { @@ -424,10 +450,6 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op for (String moduleName : IngestJobSettings.getEnabledModules(selectedProfile.getName())) { selectedModulesArea.append(moduleName + "\n"); } - - } else { - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); } } } @@ -439,7 +461,9 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op private javax.swing.JScrollPane filterDescPane; private javax.swing.JLabel filterNameLabel; private javax.swing.JLabel filterNameText; + private javax.swing.JTextArea infoTextArea; private javax.swing.JLabel ingestWarningLabel; + private javax.swing.JScrollPane jScrollPane2; private javax.swing.JSeparator jSeparator2; private javax.swing.JButton newProfileButton; private javax.swing.JTextArea profileDescArea; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form index f8e456c728..f25b144088 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form @@ -19,39 +19,40 @@ - + - - - - - - - - - - - - - - - - + + + + - + + + + + + + + + + - + + + + + @@ -65,7 +66,9 @@ - + + + @@ -207,5 +210,12 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java index 15c6dfcaeb..3f8cbe85f8 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-2016 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. @@ -29,6 +29,7 @@ import org.openide.util.NbBundle.Messages; import static org.sleuthkit.autopsy.modules.filetypeid.AddFileTypePanel.EVENT.SIG_LIST_CHANGED; import org.sleuthkit.autopsy.modules.filetypeid.AddFileTypeSignatureDialog.BUTTON_PRESSED; import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; +@Messages("AddFileTypePanel.mimeFormatLabel.text=Form of MIME type should be: media type/media subtype") /** * Panel for adding or editing file types. @@ -39,7 +40,7 @@ class AddFileTypePanel extends javax.swing.JPanel { private AddFileTypeSignatureDialog addSigDialog; private DefaultListModel signaturesListModel; - + /** * Creates a panel for a new file type. */ @@ -50,7 +51,7 @@ class AddFileTypePanel extends javax.swing.JPanel { this.addTypeListSelectionListener(); this.enableButtons(); } - + enum EVENT { SIG_LIST_CHANGED } @@ -87,7 +88,10 @@ class AddFileTypePanel extends javax.swing.JPanel { @Messages({"AddMimeTypePanel.emptySigList.message=Must have at least one signature.", "AddMimeTypePanel.emptySigList.title=Invalid Signature List", "AddMimeTypePanel.emptySetName.message=Interesting files set name is required if alert is requested.", - "AddMimeTypePanel.emptySetName.title=Missing Interesting Files Set Name"}) + "AddMimeTypePanel.emptySetName.title=Missing Interesting Files Set Name", + "AddFileTypePanel.nonStandardMIMEType.message=" + + "Files of this MIME type will not appear under the Views, File Types, By MIME Type tree because it is not in the format of: media type/media subtype", + "AddFileTypePanel.nonStandardMIMEType.title=Non-standard MIME Type"}) FileType getFileType() { String typeName = mimeTypeTextField.getText(); if (typeName.isEmpty()) { @@ -97,7 +101,12 @@ class AddFileTypePanel extends javax.swing.JPanel { JOptionPane.ERROR_MESSAGE); return null; } - + if (typeName.split("/").length != 2) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "AddFileTypePanel.nonStandardMIMEType.message"), + NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "AddFileTypePanel.nonStandardMIMEType.title"), + JOptionPane.WARNING_MESSAGE); + } if (this.signaturesListModel.isEmpty()) { JOptionPane.showMessageDialog(null, Bundle.AddMimeTypePanel_emptySigList_message(), @@ -155,7 +164,7 @@ class AddFileTypePanel extends javax.swing.JPanel { deleteSigButton.setEnabled(true); } } - + boolean hasSignature() { return !this.signaturesListModel.isEmpty(); } @@ -180,6 +189,7 @@ class AddFileTypePanel extends javax.swing.JPanel { postHitCheckBox = new javax.swing.JCheckBox(); setNameLabel = new javax.swing.JLabel(); setNameTextField = new javax.swing.JTextField(); + mimeFormatLabel = new javax.swing.JLabel(); editSigButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(editSigButton, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.editSigButton.text")); // NOI18N @@ -233,6 +243,8 @@ class AddFileTypePanel extends javax.swing.JPanel { setNameTextField.setText(org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.setNameTextField.text")); // NOI18N setNameTextField.setEnabled(postHitCheckBox.isSelected()); + org.openide.awt.Mnemonics.setLocalizedText(mimeFormatLabel, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.mimeFormatLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -244,27 +256,28 @@ class AddFileTypePanel extends javax.swing.JPanel { .addComponent(mimeTypeLabel) .addGap(18, 18, 18) .addComponent(mimeTypeTextField)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(addSigButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(editSigButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(deleteSigButton)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 393, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addGap(28, 28, 28) - .addComponent(setNameLabel) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(addSigButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addComponent(editSigButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(deleteSigButton)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 393, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(28, 28, 28) + .addComponent(setNameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel1) .addComponent(postHitCheckBox)) - .addGap(0, 0, Short.MAX_VALUE))) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGap(71, 71, 71) + .addComponent(mimeFormatLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( @@ -274,7 +287,9 @@ class AddFileTypePanel extends javax.swing.JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(mimeTypeLabel) .addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(3, 3, 3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(mimeFormatLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1) .addGap(1, 1, 1) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -338,6 +353,7 @@ class AddFileTypePanel extends javax.swing.JPanel { private javax.swing.JButton editSigButton; private javax.swing.JLabel jLabel1; private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel mimeFormatLabel; private javax.swing.JLabel mimeTypeLabel; private javax.swing.JTextField mimeTypeTextField; private javax.swing.JCheckBox postHitCheckBox; diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties index a432a9efa2..e5ae1eb726 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties @@ -80,4 +80,4 @@ FilesSetRulePanel.allRadioButton.text=All FilesSetDefsPanel.ingoreUnallocCheckbox.text=Ignore Unallocated Space FilesSetDefsPanel.ingoreUnallocCheckbox.toolTipText=Ignores unallocated space, such as deleted files. May run faster but produce less complete results. FilesSetDefsPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes. -FilesSetDefsPanel.allRadioButton.text=All \ No newline at end of file +FilesSetDefsPanel.allRadioButton.text=All diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form index d777ba80c0..51b66658ed 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form @@ -29,15 +29,12 @@ - - - - + - + @@ -68,114 +65,135 @@ - - - - - + + + + + + + + + + + + - + + + + - + + + - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -185,94 +203,94 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -868,6 +886,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java index b4743bd360..c42aac3b10 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java @@ -19,7 +19,9 @@ package org.sleuthkit.autopsy.modules.interestingitems; import java.awt.EventQueue; +import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -29,11 +31,14 @@ import java.util.TreeMap; import java.util.logging.Level; import javax.swing.DefaultListModel; import javax.swing.JButton; +import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import javax.swing.filechooser.FileNameExtensionFilter; import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -55,7 +60,10 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp "FilesSetDefsPanel.megaBytes=Megabytes", "FilesSetDefsPanel.gigaBytes=Gigabytes", "FilesSetDefsPanel.loadError=Error loading interesting files sets from file.", - "FilesSetDefsPanel.saveError=Error saving interesting files sets to file." + "FilesSetDefsPanel.saveError=Error saving interesting files sets to file.", + "FilesSetDefsPanel.interesting.copySetButton.text=Copy Set", + "FilesSetDefsPanel.interesting.importSetButton.text=Import Set", + "FilesSetDefsPanel.interesting.exportSetButton.text=Export Set" }) public static enum PANEL_TYPE { FILE_INGEST_FILTERS, @@ -93,12 +101,14 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp this.rulesList.addListSelectionListener(new FilesSetDefsPanel.RulesListSelectionListener()); this.ingestWarningLabel.setVisible(false); if (panelType == PANEL_TYPE.FILE_INGEST_FILTERS) { //Hide the mimetype settings when this is displaying FileSet rules instead of interesting item rules + this.copySetButton.setVisible(false); + this.importSetButton.setVisible(false); + this.exportSetButton.setVisible(false); this.mimeTypeComboBox.setVisible(false); this.jLabel7.setVisible(false); this.fileSizeUnitComboBox.setVisible(false); this.fileSizeSpinner.setVisible(false); this.ruleDialogTitle = "FilesSetPanel.ingest.title"; - this.jLabel8.setVisible(false); this.equalitySignComboBox.setVisible(false); this.ignoreKnownFilesCheckbox.setVisible(false); @@ -173,17 +183,23 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp } catch (FilesSetsManager.FilesSetsManagerException ex) { MessageNotifyUtil.Message.error(Bundle.FilesSetDefsPanel_saveError()); + logger.log(Level.WARNING, Bundle.FilesSetDefsPanel_saveError(), ex); } } public void enableButtons(boolean isEnabled) { + boolean setSelected = (FilesSetDefsPanel.this.setsList.getSelectedValue() != null); + boolean ruleSelected = (FilesSetDefsPanel.this.rulesList.getSelectedValue() != null); canBeEnabled = isEnabled; newRuleButton.setEnabled(isEnabled); + copySetButton.setEnabled(isEnabled && setSelected); newSetButton.setEnabled(isEnabled); - editRuleButton.setEnabled(isEnabled); - editSetButton.setEnabled(isEnabled); - deleteRuleButton.setEnabled(isEnabled); - deleteSetButton.setEnabled(isEnabled); + editRuleButton.setEnabled(isEnabled && ruleSelected); + editSetButton.setEnabled(isEnabled && setSelected); + exportSetButton.setEnabled(setSelected); + importSetButton.setEnabled(isEnabled); + deleteRuleButton.setEnabled(isEnabled && ruleSelected); + deleteSetButton.setEnabled(isEnabled && setSelected); ingestWarningLabel.setVisible(!isEnabled); } @@ -213,6 +229,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp } catch (FilesSetsManager.FilesSetsManagerException ex) { MessageNotifyUtil.Message.error(Bundle.FilesSetDefsPanel_loadError()); + logger.log(Level.WARNING, Bundle.FilesSetDefsPanel_loadError(), ex); this.filesSets = new TreeMap<>(); } @@ -242,6 +259,9 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp this.ingoreUnallocCheckbox.setSelected(true); this.newSetButton.setEnabled(true && canBeEnabled); this.editSetButton.setEnabled(false); + this.copySetButton.setEnabled(false); + this.exportSetButton.setEnabled(false); + this.importSetButton.setEnabled(true && canBeEnabled); this.deleteSetButton.setEnabled(false); } @@ -275,30 +295,31 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp if (e.getValueIsAdjusting()) { return; } - FilesSetDefsPanel.this.rulesListModel.clear(); FilesSetDefsPanel.this.resetRuleComponents(); - + //enable the new button + FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled); + FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled); // Get the selected interesting files set and populate the set - // components. + // components. FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue(); + if (selectedSet != null) { // Populate the components that display the properties of the // selected files set. FilesSetDefsPanel.this.setDescriptionTextArea.setText(selectedSet.getDescription()); FilesSetDefsPanel.this.ignoreKnownFilesCheckbox.setSelected(selectedSet.ignoresKnownFiles()); FilesSetDefsPanel.this.ingoreUnallocCheckbox.setSelected(selectedSet.ingoresUnallocatedSpace()); - // Enable the new, edit and delete set buttons. - FilesSetDefsPanel.this.newSetButton.setEnabled(true && canBeEnabled); - FilesSetDefsPanel.this.editSetButton.setEnabled(true && canBeEnabled); - FilesSetDefsPanel.this.deleteSetButton.setEnabled(true && canBeEnabled); - + // Enable the copy, export, edit and delete set buttons. + FilesSetDefsPanel.this.editSetButton.setEnabled(canBeEnabled); + FilesSetDefsPanel.this.deleteSetButton.setEnabled(canBeEnabled); + FilesSetDefsPanel.this.copySetButton.setEnabled(canBeEnabled); + FilesSetDefsPanel.this.exportSetButton.setEnabled(true); // Populate the rule definitions list, sorted by name. TreeMap rules = new TreeMap<>(selectedSet.getRules()); for (FilesSet.Rule rule : rules.values()) { FilesSetDefsPanel.this.rulesListModel.addElement(rule); } - // Select the first rule by default. if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) { FilesSetDefsPanel.this.rulesList.setSelectedIndex(0); @@ -391,15 +412,18 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp * Display an interesting files set definition panel in a dialog box and * respond to user interactions with the dialog. * - * @param selectedSet The currently selected files set, may be null to - * indicate a new interesting files set definition is to - * be created. + * @param selectedSet The currently selected files set, may be null to + * indicate a new interesting files set definition is + * to be created. + * @param shouldCreateNew Wether this should be creating a new set or + * replacing the selectedSet. False for edit, true + * for copy or new. */ - private void doFileSetsDialog(FilesSet selectedSet) { + private void doFileSetsDialog(FilesSet selectedSet, boolean shouldCreateNew) { // Create a files set defintion panle. FilesSetPanel panel; if (selectedSet != null) { - // Editing an existing set definition. + // Editing an existing set definition. panel = new FilesSetPanel(selectedSet, panelType); } else { // Creating a new set definition. @@ -416,7 +440,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp // While adding new ruleset(selectedSet == null), if rule set with same name already exists, do not add to the filesSets hashMap. // In case of editing an existing ruleset(selectedSet != null), following check is not performed. - if (this.filesSets.containsKey(panel.getFilesSetName()) && selectedSet == null) { + if (this.filesSets.containsKey(panel.getFilesSetName()) && shouldCreateNew) { MessageNotifyUtil.Message.error(NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text", panel.getFilesSetName())); @@ -431,7 +455,11 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp // Preserve the existing rules from the set being edited. rules.putAll(selectedSet.getRules()); } - this.replaceFilesSet(selectedSet, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules); + if (shouldCreateNew) { + this.replaceFilesSet(null, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules); + } else { + this.replaceFilesSet(selectedSet, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules); + } } } @@ -583,6 +611,9 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp fileSizeUnitComboBox = new javax.swing.JComboBox(); ingoreUnallocCheckbox = new javax.swing.JCheckBox(); ingestWarningLabel = new javax.swing.JLabel(); + copySetButton = new javax.swing.JButton(); + importSetButton = new javax.swing.JButton(); + exportSetButton = new javax.swing.JButton(); setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11)); @@ -785,6 +816,32 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp ingestWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/hashdatabase/warning16.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(ingestWarningLabel, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.ingestWarningLabel.text")); // NOI18N + copySetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/new16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(copySetButton, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.interesting.copySetButton.text")); // NOI18N + copySetButton.setEnabled(false); + copySetButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + copySetButtonActionPerformed(evt); + } + }); + + importSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/import16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(importSetButton, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.interesting.importSetButton.text")); // NOI18N + importSetButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + importSetButtonActionPerformed(evt); + } + }); + + exportSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/export16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(exportSetButton, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.interesting.exportSetButton.text")); // NOI18N + exportSetButton.setEnabled(false); + exportSetButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exportSetButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -792,92 +849,105 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(setsListLabel) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 314, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(newSetButton) - .addGap(9, 9, 9) - .addComponent(editSetButton) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup() + .addComponent(copySetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(importSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup() + .addComponent(newSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 97, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(editSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteSetButton)) - .addComponent(setsListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 344, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(exportSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(deleteSetButton, javax.swing.GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE))) + .addComponent(setsListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(setsListLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(101, 101, 101) - .addComponent(filesRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(dirsRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(allRadioButton)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(105, 105, 105) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(fileNameRadioButton) - .addGap(4, 4, 4) - .addComponent(fileNameExtensionRadioButton) + .addGap(101, 101, 101) + .addComponent(filesRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(fileNameRegexCheckbox)) - .addComponent(rulePathConditionRegexCheckBox))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(rulesListLabel) - .addComponent(jLabel1) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(newRuleButton) - .addGap(18, 18, 18) - .addComponent(editRuleButton) - .addGap(18, 18, 18) - .addComponent(deleteRuleButton)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(ignoreKnownFilesCheckbox) + .addComponent(dirsRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(ingoreUnallocCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 158, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(allRadioButton)) .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(105, 105, 105) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel5) - .addComponent(jLabel6)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(fileNameRadioButton) + .addGap(4, 4, 4) + .addComponent(fileNameExtensionRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(fileNameRegexCheckbox)) + .addComponent(rulePathConditionRegexCheckBox))) + .addGroup(jPanel1Layout.createSequentialGroup() .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(ingestWarningLabel)))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(rulesListLabel) + .addComponent(jLabel1) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(newRuleButton) + .addGap(18, 18, 18) + .addComponent(editRuleButton) + .addGap(18, 18, 18) + .addComponent(deleteRuleButton)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(ignoreKnownFilesCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(ingoreUnallocCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 158, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel5) + .addComponent(jLabel6)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(ingestWarningLabel))))) + .addGap(24, 28, Short.MAX_VALUE)) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(29, 29, 29) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(22, 22, 22) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel7) - .addComponent(jLabel8)) - .addGap(18, 18, 18)) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel7) + .addComponent(jLabel8)) + .addGap(18, 18, 18)) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel3) - .addComponent(jLabel2)) - .addGap(6, 6, 6)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addComponent(jLabel4) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)))) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(mimeTypeComboBox, 0, 313, Short.MAX_VALUE) - .addComponent(rulePathConditionTextField) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) + .addComponent(jLabel2)) + .addGap(6, 6, 6)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addComponent(jLabel4) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fileSizeSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(rulePathConditionTextField) + .addComponent(fileNameTextField, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(mimeTypeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(fileSizeSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(fileNameTextField))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(setDescScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(rulesListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE)))) - .addGap(18, 18, 18)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(rulesListScrollPane, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(setDescScrollPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGap(8, 8, 8)))) ); - jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteSetButton, editSetButton, newSetButton}); + jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copySetButton, deleteSetButton, editSetButton, newSetButton}); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -888,75 +958,77 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel6) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(setsListLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(setsListScrollPane) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newSetButton) - .addComponent(editSetButton) - .addComponent(deleteSetButton))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jLabel6) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel5) - .addGap(1, 1, 1)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addComponent(ingestWarningLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))) - .addComponent(setDescScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(6, 6, 6) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(ignoreKnownFilesCheckbox) - .addComponent(ingoreUnallocCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(rulesListLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(rulesListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newRuleButton) - .addComponent(editRuleButton) - .addComponent(deleteRuleButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1) - .addGap(8, 8, 8) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel2) - .addComponent(filesRadioButton) - .addComponent(dirsRadioButton) - .addComponent(allRadioButton)) - .addGap(16, 16, 16) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel3) - .addComponent(fileNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(fileNameRadioButton) - .addComponent(fileNameExtensionRadioButton) - .addComponent(fileNameRegexCheckbox)) - .addGap(16, 16, 16) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel4) - .addComponent(rulePathConditionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(7, 7, 7) - .addComponent(rulePathConditionRegexCheckBox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel7) - .addComponent(mimeTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel8) - .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(fileSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(5, 5, 5))) - .addGap(5, 5, 5)))) + .addComponent(jLabel5) + .addGap(1, 1, 1)) + .addComponent(ingestWarningLabel, javax.swing.GroupLayout.Alignment.TRAILING)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(setDescScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(6, 6, 6) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(ignoreKnownFilesCheckbox) + .addComponent(ingoreUnallocCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(rulesListLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(rulesListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 82, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(newRuleButton) + .addComponent(editRuleButton) + .addComponent(deleteRuleButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1) + .addGap(8, 8, 8) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(filesRadioButton) + .addComponent(dirsRadioButton) + .addComponent(allRadioButton)) + .addGap(8, 8, 8) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(fileNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(fileNameRadioButton) + .addComponent(fileNameExtensionRadioButton) + .addComponent(fileNameRegexCheckbox)) + .addGap(8, 8, 8) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel4) + .addComponent(rulePathConditionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(6, 6, 6) + .addComponent(rulePathConditionRegexCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel7) + .addComponent(mimeTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel8) + .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(5, 5, 5)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(setsListLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(setsListScrollPane) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(newSetButton) + .addComponent(editSetButton) + .addComponent(deleteSetButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(copySetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(importSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(exportSetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(6, 6, 6)))) ); jPanel1Layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteRuleButton, deleteSetButton, editRuleButton, editSetButton, newRuleButton, newSetButton}); @@ -967,9 +1039,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jScrollPane1) - .addGap(0, 0, 0)) + .addComponent(jScrollPane1) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -978,7 +1048,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp }// //GEN-END:initComponents private void newSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSetButtonActionPerformed - this.doFileSetsDialog(null); + this.doFileSetsDialog(null, true); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_newSetButtonActionPerformed @@ -1026,7 +1096,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp }//GEN-LAST:event_deleteSetButtonActionPerformed private void editSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editSetButtonActionPerformed - this.doFileSetsDialog(this.setsList.getSelectedValue()); + this.doFileSetsDialog(this.setsList.getSelectedValue(), false); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_editSetButtonActionPerformed @@ -1040,14 +1110,166 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_newRuleButtonActionPerformed + private void copySetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copySetButtonActionPerformed + this.doFileSetsDialog(this.setsList.getSelectedValue(), true); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_copySetButtonActionPerformed + @NbBundle.Messages({ + "FilesSetDefsPanel.yesOwMsg=Yes, overwrite", + "FilesSetDefsPanel.noSkipMsg=No, skip", + "FilesSetDefsPanel.cancelImportMsg=Cancel import", + "# {0} - FilesSet name", + "FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set <{0}> already exists locally, overwrite?", + "FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict", + "FilesSetDefsPanel.interesting.failImportMsg=Interesting files set not imported", + "FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)", + "FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import" + }) + private void importSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importSetButtonActionPerformed + //save currently selected value as default value to select + FilesSet selectedSet = this.setsList.getSelectedValue(); + JFileChooser chooser = new JFileChooser(); + final String EXTENSION = "xml"; //NON-NLS + FileNameExtensionFilter autopsyFilter = new FileNameExtensionFilter( + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.fileExtensionFilterLbl"), EXTENSION); + chooser.addChoosableFileFilter(autopsyFilter); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + int returnVal = chooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File selFile = chooser.getSelectedFile(); + if (selFile == null) { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.failImportMsg"), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.importButtonAction.featureName"), + JOptionPane.WARNING_MESSAGE); + logger.warning("Selected file was null, when trying to import interesting files set definitions"); + return; + } + Collection importedSets; + try { + importedSets = InterestingItemsFilesSetSettings.readDefinitionsXML(selFile).values(); //read the xml from that path + if (importedSets.isEmpty()) { + throw new FilesSetsManager.FilesSetsManagerException("No Files Sets were read from the xml."); + } + } catch (FilesSetsManager.FilesSetsManagerException ex) { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.failImportMsg"), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.importButtonAction.featureName"), + JOptionPane.WARNING_MESSAGE); + logger.log(Level.WARNING, "No Interesting files set definitions were read from the selected file, exception", ex); + return; + } + for (FilesSet set : importedSets) { + int choice = JOptionPane.OK_OPTION; + if (filesSets.containsKey(set.getName())) { + Object[] options = {NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.yesOwMsg"), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.noSkipMsg"), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.cancelImportMsg")}; + choice = JOptionPane.showOptionDialog(this, + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.overwriteSetPrompt", set.getName()), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.importOwConflict"), + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0]); + } + if (choice == JOptionPane.OK_OPTION) { + selectedSet = set; + this.filesSets.put(set.getName(), set); + } else if (choice == JOptionPane.CANCEL_OPTION) { + break; + } + } + // Redo the list model for the files set list component + FilesSetDefsPanel.this.setsListModel.clear(); + for (FilesSet set : this.filesSets.values()) { + this.setsListModel.addElement(set); + } + // Select the new/edited files set definition in the set definitions + // list. This will cause the selection listeners to repopulate the + // subordinate components. + this.setsList.setSelectedValue(selectedSet, true); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + + }//GEN-LAST:event_importSetButtonActionPerformed + + @NbBundle.Messages({"FilesSetDefsPanel.interesting.exportButtonAction.featureName=Interesting Files Set Export", + "# {0} - file name", + "FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?", + "FilesSetDefsPanel.interesting.ExportedMsg=Interesting files set exported", + "FilesSetDefsPanel.interesting.failExportMsg=Export of interesting files set failed"}) + private void exportSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportSetButtonActionPerformed + //display warning that existing filessets with duplicate names will be overwritten + //create file chooser to get xml filefinal String FEATURE_NAME = NbBundle.getMessage(this.getClass(), + JFileChooser chooser = new JFileChooser(); + final String EXTENSION = "xml"; //NON-NLS + FileNameExtensionFilter autopsyFilter = new FileNameExtensionFilter( + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.fileExtensionFilterLbl"), EXTENSION); + chooser.addChoosableFileFilter(autopsyFilter); + chooser.setSelectedFile(new File(this.setsList.getSelectedValue().getName())); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + int returnVal = chooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + final String FEATURE_NAME = NbBundle.getMessage(this.getClass(), + "FilesSetDefsPanel.interesting.exportButtonAction.featureName"); + File selFile = chooser.getSelectedFile(); + if (selFile == null) { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.failExportMsg"), + FEATURE_NAME, + JOptionPane.WARNING_MESSAGE); + logger.warning("Selected file was null, when trying to export interesting files set definitions"); + return; + } + //force append extension if not given + String fileAbs = selFile.getAbsolutePath(); + if (!fileAbs.endsWith("." + EXTENSION)) { + fileAbs = fileAbs + "." + EXTENSION; + selFile = new File(fileAbs); + } + if (selFile.exists()) { + //if the file already exists ask the user how to proceed + final String FILE_EXISTS_MESSAGE = NbBundle.getMessage(this.getClass(), + "FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt", selFile.getName()); + boolean shouldWrite = JOptionPane.showConfirmDialog(null, FILE_EXISTS_MESSAGE, FEATURE_NAME, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION; + if (!shouldWrite) { + return; + } + } + List exportSets; + exportSets = new ArrayList<>(); + //currently only exports selectedValue + exportSets.add(this.setsList.getSelectedValue()); + boolean written = InterestingItemsFilesSetSettings.exportXmlDefinitionsFile(selFile, exportSets); + if (written) { + JOptionPane.showMessageDialog( + WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.ExportedMsg"), + FEATURE_NAME, + JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.failExportMsg"), + FEATURE_NAME, + JOptionPane.WARNING_MESSAGE); + logger.warning("Export of interesting files set failed unable to write definitions xml file"); + } + } + }//GEN-LAST:event_exportSetButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JRadioButton allRadioButton; + private javax.swing.JButton copySetButton; private javax.swing.JButton deleteRuleButton; private javax.swing.JButton deleteSetButton; private javax.swing.JRadioButton dirsRadioButton; private javax.swing.JButton editRuleButton; private javax.swing.JButton editSetButton; private javax.swing.JComboBox equalitySignComboBox; + private javax.swing.JButton exportSetButton; private javax.swing.ButtonGroup fileNameButtonGroup; private javax.swing.JRadioButton fileNameExtensionRadioButton; private javax.swing.JRadioButton fileNameRadioButton; @@ -1057,6 +1279,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp private javax.swing.JComboBox fileSizeUnitComboBox; private javax.swing.JRadioButton filesRadioButton; private javax.swing.JCheckBox ignoreKnownFilesCheckbox; + private javax.swing.JButton importSetButton; private javax.swing.JLabel ingestWarningLabel; private javax.swing.JCheckBox ingoreUnallocCheckbox; private javax.swing.JLabel jLabel1; diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java index bdd9f53a49..de88aee68e 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,19 +31,23 @@ import java.util.Map; import java.util.logging.Level; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.openide.util.io.NbObjectInputStream; import org.openide.util.io.NbObjectOutputStream; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.FileNameCondition; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.FileSizeCondition; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MetaTypeCondition; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MimeTypeCondition; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ParentPathCondition; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; -/** - * - * @author oliver - */ class InterestingItemsFilesSetSettings implements Serializable { private static final long serialVersionUID = 1L; @@ -51,9 +55,9 @@ class InterestingItemsFilesSetSettings implements Serializable { // TSK Framework FilesSet definitions file schema. private static final String FILE_SETS_ROOT_TAG = "INTERESTING_FILE_SETS"; //NON-NLS private static final String DESC_ATTR = "description"; //NON-NLS - private static final String RULE_UUID_ATTR = "ruleUUID"; //NON-NLS private static final String IGNORE_KNOWN_FILES_ATTR = "ignoreKnown"; //NON-NLS private static final String PATH_REGEX_ATTR = "pathRegex"; //NON-NLS + private static final String TYPE_FILTER_VALUE_ALL = "all"; private static final String TYPE_FILTER_VALUE_FILES_AND_DIRS = "files_and_dirs"; //NON-NLS private static final String IGNORE_UNALLOCATED_SPACE = "ingoreUnallocated"; //NON-NLS private static final String PATH_FILTER_ATTR = "pathFilter"; //NON-NLS @@ -62,12 +66,14 @@ class InterestingItemsFilesSetSettings implements Serializable { private static final List illegalFileNameChars = FilesSetsManager.getIllegalFileNameChars(); private static final String FILE_SET_TAG = "INTERESTING_FILE_SET"; //NON-NLS private static final String NAME_RULE_TAG = "NAME"; //NON-NLS - private static final String UNNAMED_LEGACY_RULE_PREFIX = "Unnamed Rule "; // NON-NLS private static final String NAME_ATTR = "name"; //NON-NLS + private static final String MIME_ATTR = "mimeType"; + private static final String FS_COMPARATOR_ATTR = "comparatorSymbol"; + private static final String FS_SIZE_ATTR = "sizeValue"; + private static final String FS_UNITS_ATTR = "sizeUnits"; private static final String TYPE_FILTER_VALUE_FILES = "file"; //NON-NLS private static final String XML_ENCODING = "UTF-8"; //NON-NLS private static final Logger logger = Logger.getLogger(InterestingItemsFilesSetSettings.class.getName()); - private static int unnamedLegacyRuleCounter; private static final String TYPE_FILTER_ATTR = "typeFilter"; //NON-NLS private static final String EXTENSION_RULE_TAG = "EXTENSION"; //NON-NLS @@ -93,7 +99,7 @@ class InterestingItemsFilesSetSettings implements Serializable { */ private static String readRuleName(Element elem) { // The rule must have a name. - String ruleName = elem.getAttribute(InterestingItemsFilesSetSettings.NAME_ATTR); + String ruleName = elem.getAttribute(NAME_ATTR); return ruleName; } @@ -130,22 +136,34 @@ class InterestingItemsFilesSetSettings implements Serializable { * @param ruleElement The XML element. * * @return The path condition, or null if there is an error (logged). + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ - private static FilesSet.Rule.ParentPathCondition readPathCondition(Element ruleElement) { - FilesSet.Rule.ParentPathCondition condition = null; - String path = ruleElement.getAttribute(InterestingItemsFilesSetSettings.PATH_FILTER_ATTR); - String pathRegex = ruleElement.getAttribute(InterestingItemsFilesSetSettings.PATH_REGEX_ATTR); - if (!pathRegex.isEmpty() && path.isEmpty()) { - try { - Pattern pattern = Pattern.compile(pathRegex); - condition = new FilesSet.Rule.ParentPathCondition(pattern); - } catch (PatternSyntaxException ex) { - logger.log(Level.SEVERE, "Error compiling " + InterestingItemsFilesSetSettings.PATH_REGEX_ATTR + " regex, ignoring malformed path condition definition", ex); // NON-NLS + private static ParentPathCondition readPathCondition(Element ruleElement) throws FilesSetsManager.FilesSetsManagerException { + // Read in the optional path condition. Null is o.k., but if the attribute + // is there, be sure it is not malformed. + ParentPathCondition pathCondition = null; + if (!ruleElement.getAttribute(PATH_FILTER_ATTR).isEmpty() || !ruleElement.getAttribute(PATH_REGEX_ATTR).isEmpty()) { + String path = ruleElement.getAttribute(PATH_FILTER_ATTR); + String pathRegex = ruleElement.getAttribute(PATH_REGEX_ATTR); + if (!pathRegex.isEmpty() && path.isEmpty()) { + try { + Pattern pattern = Pattern.compile(pathRegex); + pathCondition = new ParentPathCondition(pattern); + } catch (PatternSyntaxException ex) { + logger.log(Level.SEVERE, "Error compiling " + PATH_REGEX_ATTR + " regex, ignoring malformed path condition definition", ex); // NON-NLS + throw new FilesSetsManager.FilesSetsManagerException(String.format("error compiling %s regex", PATH_REGEX_ATTR), ex); + } + } else if (!path.isEmpty() && pathRegex.isEmpty()) { + pathCondition = new ParentPathCondition(path); + } + if (pathCondition == null) { + // Malformed attribute. + throw new FilesSetsManager.FilesSetsManagerException(String.format("Error creating path condition for rule %s", readRuleName(ruleElement))); } - } else if (!path.isEmpty() && pathRegex.isEmpty()) { - condition = new FilesSet.Rule.ParentPathCondition(path); } - return condition; + return pathCondition; } /** @@ -165,116 +183,129 @@ class InterestingItemsFilesSetSettings implements Serializable { } /** - * Construct a FilesSet file name extension rule from the data in an XML - * element. + * Construct a fileset membership rule from the data in an xml element for + * use in a FilesSet. * - * @param elem The file name extension rule XML element. + * @param elem The XML element. * - * @return A file name extension rule, or null if there is an error (the - * error is logged). + * @return A file set constructed from the conditions available in the XML element + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ - private static FilesSet.Rule readFileExtensionRule(Element elem) { - String ruleName = InterestingItemsFilesSetSettings.readRuleName(elem); - // The content of the rule tag is a file name extension condition. It may - // be a regex, or it may be from a TSK Framework rule definition - // with a "*" globbing char. - String content = elem.getTextContent(); - FilesSet.Rule.ExtensionCondition extCondition; - String regex = elem.getAttribute(InterestingItemsFilesSetSettings.REGEX_ATTR); - if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { - // NON-NLS - Pattern pattern = compileRegex(content); - if (pattern != null) { - extCondition = new FilesSet.Rule.ExtensionCondition(pattern); - } else { - logger.log(Level.SEVERE, "Error compiling " + InterestingItemsFilesSetSettings.EXTENSION_RULE_TAG + " regex, ignoring malformed {0} rule definition", ruleName); // NON-NLS - return null; - } - } else { - for (String illegalChar : illegalFileNameChars) { - if (content.contains(illegalChar)) { - logger.log(Level.SEVERE, "{0} content has illegal chars, ignoring malformed {1} rule definition", ruleName); // NON-NLS - return null; - } - } - extCondition = new FilesSet.Rule.ExtensionCondition(content); + private static FilesSet.Rule readRule(Element elem) throws FilesSetsManager.FilesSetsManagerException { + String ruleName = readRuleName(elem); + FileNameCondition nameCondition = readNameCondition(elem); + MetaTypeCondition metaCondition = readMetaTypeCondition(elem); + ParentPathCondition pathCondition = readPathCondition(elem); + MimeTypeCondition mimeCondition = readMimeCondition(elem); + FileSizeCondition sizeCondition = readSizeCondition(elem); + //if meta type condition or all four types of conditions the user can create are all null then don't make the rule + if (metaCondition == null || (nameCondition == null && pathCondition == null && mimeCondition == null && sizeCondition == null)) { + logger.log(Level.WARNING, "Error Reading Rule, " + ruleName + " was either missing a meta condition or contained only a meta condition. No rule was imported."); // NON-NLS + throw new FilesSetsManager.FilesSetsManagerException(String.format("Invalid Rule in FilesSet xml, missing necessary conditions for %s", ruleName)); } - // The rule must have a meta-type condition, unless a TSK Framework - // definitions file is being read. - FilesSet.Rule.MetaTypeCondition metaTypeCondition = null; - if (!elem.getAttribute(InterestingItemsFilesSetSettings.TYPE_FILTER_ATTR).isEmpty()) { - metaTypeCondition = InterestingItemsFilesSetSettings.readMetaTypeCondition(elem); - if (metaTypeCondition == null) { - // Malformed attribute. - return null; - } - } else { - metaTypeCondition = new FilesSet.Rule.MetaTypeCondition(FilesSet.Rule.MetaTypeCondition.Type.FILES); - } - // The rule may have a path condition. Null is o.k., but if the attribute - // is there, it must not be malformed. - FilesSet.Rule.ParentPathCondition pathCondition = null; - if (!elem.getAttribute(InterestingItemsFilesSetSettings.PATH_FILTER_ATTR).isEmpty() || !elem.getAttribute(InterestingItemsFilesSetSettings.PATH_REGEX_ATTR).isEmpty()) { - pathCondition = InterestingItemsFilesSetSettings.readPathCondition(elem); - if (pathCondition == null) { - // Malformed attribute. - return null; - } - } - return new FilesSet.Rule(ruleName, extCondition, metaTypeCondition, pathCondition, null, null); + return new FilesSet.Rule(ruleName, nameCondition, metaCondition, pathCondition, mimeCondition, sizeCondition); } /** - * Construct a FilesSet file name rule from the data in an XML element. + * Construct a file name condition for a FilesSet membership rule from data in an + * XML element. * - * @param elem The file name rule XML element. + * @param ruleElement The XML element. * - * @return A file name rule, or null if there is an error (the error is - * logged). + * @return The file name condition, or null if none existed + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ - private static FilesSet.Rule readFileNameRule(Element elem) { - String ruleName = InterestingItemsFilesSetSettings.readRuleName(elem); - // The content of the rule tag is a file name condition. It may be a - // regex, or it may be from a TSK Framework rule definition with a - // "*" globbing char, or it may be simple text. + private static FileNameCondition readNameCondition(Element elem) throws FilesSetsManager.FilesSetsManagerException { + FileNameCondition nameCondition = null; String content = elem.getTextContent(); - FilesSet.Rule.FullNameCondition nameCondition; - String regex = elem.getAttribute(InterestingItemsFilesSetSettings.REGEX_ATTR); - if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { - // NON-NLS - Pattern pattern = compileRegex(content); - if (pattern != null) { - nameCondition = new FilesSet.Rule.FullNameCondition(pattern); + String regex = elem.getAttribute(REGEX_ATTR); + if (content != null && !content.isEmpty()) { //if there isn't content this is not a valid name condition + if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { // NON-NLS + Pattern pattern = compileRegex(content); + if (pattern != null) { + if (elem.getTagName().equals(NAME_RULE_TAG)) { + nameCondition = new FilesSet.Rule.FullNameCondition(pattern); + } else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) { + nameCondition = new FilesSet.Rule.ExtensionCondition(pattern); + } else { + throw new FilesSetsManager.FilesSetsManagerException(String.format("Name condition has invalid tag name of %s for rule %s", elem.getTagName(), readRuleName(elem))); + } + } else { + logger.log(Level.SEVERE, "Error compiling " + elem.getTagName() + " regex, ignoring malformed '{0}' rule definition", readRuleName(elem)); // NON-NLS + throw new FilesSetsManager.FilesSetsManagerException(String.format("error compiling %s regex in rule %s", REGEX_ATTR, readRuleName(elem))); + } } else { - logger.log(Level.SEVERE, "Error compiling " + InterestingItemsFilesSetSettings.NAME_RULE_TAG + " regex, ignoring malformed '{0}' rule definition", ruleName); // NON-NLS - return null; - } - } else { - for (String illegalChar : illegalFileNameChars) { - if (content.contains(illegalChar)) { - logger.log(Level.SEVERE, InterestingItemsFilesSetSettings.NAME_RULE_TAG + " content has illegal chars, ignoring malformed '{0}' rule definition", new Object[]{InterestingItemsFilesSetSettings.NAME_RULE_TAG, ruleName}); // NON-NLS - return null; + for (String illegalChar : illegalFileNameChars) { + if (content.contains(illegalChar)) { + logger.log(Level.SEVERE, elem.getTagName() + " content has illegal chars, ignoring malformed '{0}' rule definition", new Object[]{elem.getTagName(), readRuleName(elem)}); // NON-NLS + throw new FilesSetsManager.FilesSetsManagerException(String.format("File name has illegal character of %s in rule %s", illegalChar, readRuleName(elem))); + } + } + if (elem.getTagName().equals(NAME_RULE_TAG)) { + nameCondition = new FilesSet.Rule.FullNameCondition(content); + } else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) { + nameCondition = new FilesSet.Rule.ExtensionCondition(content); } } - nameCondition = new FilesSet.Rule.FullNameCondition(content); } - // Read in the type condition. - FilesSet.Rule.MetaTypeCondition metaTypeCondition = InterestingItemsFilesSetSettings.readMetaTypeCondition(elem); - if (metaTypeCondition == null) { - // Malformed attribute. - return null; + return nameCondition; + } + + /** + * Construct a MIME type condition for a FilesSet membership rule from data in an + * XML element. + * + * @param ruleElement The XML element. + * + * @return The mime TYPE condition, or null if none existed + */ + private static MimeTypeCondition readMimeCondition(Element elem) { + MimeTypeCondition mimeCondition = null; + if (!elem.getAttribute(MIME_ATTR).isEmpty()) { + mimeCondition = new MimeTypeCondition(elem.getAttribute(MIME_ATTR)); + //no checks on mime type here which means + //if they import a rule with a custom MIME type they don't have + //the rule will not get any hits } - // Read in the optional path condition. Null is o.k., but if the attribute - // is there, be sure it is not malformed. - FilesSet.Rule.ParentPathCondition pathCondition = null; - if (!elem.getAttribute(InterestingItemsFilesSetSettings.PATH_FILTER_ATTR).isEmpty() || !elem.getAttribute(InterestingItemsFilesSetSettings.PATH_REGEX_ATTR).isEmpty()) { - pathCondition = InterestingItemsFilesSetSettings.readPathCondition(elem); - if (pathCondition == null) { - // Malformed attribute. - return null; + return mimeCondition; + } + + /** + * Construct a file size condition for a FilesSet membership rule from data in an + * XML element. + * + * @param ruleElement The XML element. + * + * @return The file size condition, or null if none existed + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException + */ + private static FileSizeCondition readSizeCondition(Element elem) throws FilesSetsManager.FilesSetsManagerException { + FileSizeCondition sizeCondition = null; + if (!elem.getAttribute(FS_COMPARATOR_ATTR).isEmpty() && !elem.getAttribute(FS_SIZE_ATTR).isEmpty() && !elem.getAttribute(FS_UNITS_ATTR).isEmpty()) { + try { //incase they modified the xml manually to invalid comparator, size unit, or non integer string for size + FileSizeCondition.COMPARATOR comparator = FileSizeCondition.COMPARATOR.fromSymbol(elem.getAttribute(FS_COMPARATOR_ATTR)); + FileSizeCondition.SIZE_UNIT sizeUnit = FileSizeCondition.SIZE_UNIT.fromName(elem.getAttribute(FS_UNITS_ATTR)); + int size = Integer.parseInt(elem.getAttribute(FS_SIZE_ATTR)); + sizeCondition = new FileSizeCondition(comparator, sizeUnit, size); + } catch (NumberFormatException nfEx) { + logger.log(Level.SEVERE, "Value in file size attribute was not an integer, unable to create FileSizeCondition for rule: " + readRuleName(elem), nfEx); + throw new FilesSetsManager.FilesSetsManagerException(String.format("Non integer size in FilesSet XML for rule %s", readRuleName(elem)), nfEx); + } catch (IllegalArgumentException iaEx) { + logger.log(Level.SEVERE, "Invalid Comparator symbol or Size Unit set in FilesSet xml, unable to create FileSizeCondition for rule: " + readRuleName(elem), iaEx); + throw new FilesSetsManager.FilesSetsManagerException(String.format("Invalid Comparator or Size unit in FilesSet XML for rule %s", readRuleName(elem)), iaEx); } + } //if all of them aren't populated but some of them are this is a malformed xml + else if (!elem.getAttribute(FS_COMPARATOR_ATTR).isEmpty() || !elem.getAttribute(FS_SIZE_ATTR).isEmpty() || !elem.getAttribute(FS_UNITS_ATTR).isEmpty()) { + logger.log(Level.SEVERE, "Invalid Comparator symbol or Size Unit set in FilesSet xml, unable to create FileSizeCondition for rule: " + readRuleName(elem)); + throw new FilesSetsManager.FilesSetsManagerException(String.format("XML malformed missing at least one fileSize attribute for rule %s", readRuleName(elem))); } - return new FilesSet.Rule(ruleName, nameCondition, metaTypeCondition, pathCondition, null, null); + return sizeCondition; } /** @@ -283,12 +314,15 @@ class InterestingItemsFilesSetSettings implements Serializable { * @param setElem A FilesSet XML element * @param filesSets A collection to which the set is to be added. * @param filePath The source file, used for error reporting. + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ - private static void readFilesSet(Element setElem, Map filesSets, String filePath) { + private static void readFilesSet(Element setElem, Map filesSets, String filePath) throws FilesSetsManager.FilesSetsManagerException { // The file set must have a unique name. - String setName = setElem.getAttribute(InterestingItemsFilesSetSettings.NAME_ATTR); + String setName = setElem.getAttribute(NAME_ATTR); if (setName.isEmpty()) { - logger.log(Level.SEVERE, "Found {0} element without required {1} attribute, ignoring malformed file set definition in FilesSet definition file at {2}", new Object[]{InterestingItemsFilesSetSettings.FILE_SET_TAG, InterestingItemsFilesSetSettings.NAME_ATTR, filePath}); // NON-NLS + logger.log(Level.SEVERE, "Found {0} element without required {1} attribute, ignoring malformed file set definition in FilesSet definition file at {2}", new Object[]{FILE_SET_TAG, NAME_ATTR, filePath}); // NON-NLS return; } if (filesSets.containsKey(setName)) { @@ -296,55 +330,39 @@ class InterestingItemsFilesSetSettings implements Serializable { return; } // The file set may have a description. The empty string is o.k. - String description = setElem.getAttribute(InterestingItemsFilesSetSettings.DESC_ATTR); + String description = setElem.getAttribute(DESC_ATTR); // The file set may or may not ignore known files. The default behavior // is to not ignore them. - String ignoreKnown = setElem.getAttribute(InterestingItemsFilesSetSettings.IGNORE_KNOWN_FILES_ATTR); + String ignoreKnown = setElem.getAttribute(IGNORE_KNOWN_FILES_ATTR); boolean ignoreKnownFiles = false; if (!ignoreKnown.isEmpty()) { ignoreKnownFiles = Boolean.parseBoolean(ignoreKnown); } // The file set may or may not skip unallocated space. The default behavior // is not to skip it. - String ignoreUnallocated = setElem.getAttribute(InterestingItemsFilesSetSettings.IGNORE_UNALLOCATED_SPACE); + String ignoreUnallocated = setElem.getAttribute(IGNORE_UNALLOCATED_SPACE); boolean ignoreUnallocatedSpace = false; if (!ignoreUnallocated.isEmpty()) { ignoreUnallocatedSpace = Boolean.parseBoolean(ignoreUnallocated); } - // Read file name set membership rules, if any. - InterestingItemsFilesSetSettings.unnamedLegacyRuleCounter = 1; + // Read the set membership rules, if any. Map rules = new HashMap<>(); - NodeList nameRuleElems = setElem.getElementsByTagName(InterestingItemsFilesSetSettings.NAME_RULE_TAG); - for (int j = 0; j < nameRuleElems.getLength(); ++j) { - Element elem = (Element) nameRuleElems.item(j); - FilesSet.Rule rule = InterestingItemsFilesSetSettings.readFileNameRule(elem); - if (rule != null) { - if (!rules.containsKey(rule.getUuid())) { - rules.put(rule.getUuid(), rule); + NodeList allRuleElems = setElem.getChildNodes(); + for (int j = 0; j < allRuleElems.getLength(); ++j) { + if (allRuleElems.item(j) instanceof Element) { //All the children we need to parse here are elements + Element elem = (Element) allRuleElems.item(j); + FilesSet.Rule rule = readRule(elem); + if (rule != null) { + if (!rules.containsKey(rule.getUuid())) { + rules.put(rule.getUuid(), rule); + } else { + logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in FilesSet definition file at {2}, discarding malformed set", new Object[]{rule.getUuid(), setName, filePath}); // NON-NLS + return; + } } else { - logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in FilesSet definition file at {2}, discarding malformed set", new Object[]{rule.getUuid(), setName, filePath}); // NON-NLS + logger.log(Level.SEVERE, "Found malformed rule for set named {0} in FilesSet definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); // NON-NLS return; } - } else { - logger.log(Level.SEVERE, "Found malformed rule for set named {0} in FilesSet definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); // NON-NLS - return; - } - } - // Read file extension set membership rules, if any. - NodeList extRuleElems = setElem.getElementsByTagName(InterestingItemsFilesSetSettings.EXTENSION_RULE_TAG); - for (int j = 0; j < extRuleElems.getLength(); ++j) { - Element elem = (Element) extRuleElems.item(j); - FilesSet.Rule rule = InterestingItemsFilesSetSettings.readFileExtensionRule(elem); - if (rule != null) { - if (!rules.containsKey(rule.getUuid())) { - rules.put(rule.getUuid(), rule); - } else { - logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in FilesSet definition file at {2}, discarding malformed set", new Object[]{rule.getUuid(), setName, filePath}); //NOI18N NON-NLS - return; - } - } else { - logger.log(Level.SEVERE, "Found malformed rule for set named {0} in FilesSet definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); //NOI18N NON-NLS - return; } } // Make the files set. Note that degenerate sets with no rules are @@ -353,18 +371,21 @@ class InterestingItemsFilesSetSettings implements Serializable { FilesSet set = new FilesSet(setName, description, ignoreKnownFiles, ignoreUnallocatedSpace, rules); filesSets.put(set.getName(), set); } - // Note: This method takes a file path to support the possibility of // multiple intersting files set definition files, e.g., one for // definitions that ship with Autopsy and one for user definitions. + /** - * Reads FilesSet definitions from an XML file. + * Reads FilesSet definitions from Serialized file or XML file. * * @param fileName The name of the file which is expected to store the * serialized definitions - * @param legacyFilePath Path of the set definitions file as a string. + * @param legacyFileName Name of the xml set definitions file as a string. * * @return The set definitions in a map of set names to sets. + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ static Map readDefinitionsFile(String fileName, String legacyFileName) throws FilesSetsManager.FilesSetsManagerException { Map filesSets = readSerializedDefinitions(fileName); @@ -373,32 +394,50 @@ class InterestingItemsFilesSetSettings implements Serializable { } // Check if the legacy xml file exists. if (!legacyFileName.isEmpty()) { - File defsFile = Paths.get(PlatformUtil.getUserConfigDirectory(), legacyFileName).toFile(); - if (!defsFile.exists()) { - return filesSets; - } - // Check if the file can be read. - if (!defsFile.canRead()) { - logger.log(Level.SEVERE, "FilesSet definition file at {0} exists, but cannot be read", defsFile.getPath()); // NON-NLS - return filesSets; - } - // Parse the XML in the file. - Document doc = XMLUtil.loadDoc(InterestingItemsFilesSetSettings.class, defsFile.getPath()); - if (doc == null) { - logger.log(Level.SEVERE, "FilesSet definition file at {0}", defsFile.getPath()); // NON-NLS - return filesSets; - } - // Get the root element. - Element root = doc.getDocumentElement(); - if (root == null) { - logger.log(Level.SEVERE, "Failed to get root {0} element tag of FilesSet definition file at {1}", new Object[]{InterestingItemsFilesSetSettings.FILE_SETS_ROOT_TAG, defsFile.getPath()}); // NON-NLS - return filesSets; - } - // Read in the files set definitions. - NodeList setElems = root.getElementsByTagName(FILE_SET_TAG); - for (int i = 0; i < setElems.getLength(); ++i) { - readFilesSet((Element) setElems.item(i), filesSets, defsFile.getPath()); - } + return readDefinitionsXML(Paths.get(PlatformUtil.getUserConfigDirectory(), legacyFileName).toFile()); + } + return filesSets; + } + + /** + * Reads an XML file and returns a map of fileSets. Allows for legacy XML + * support as well as importing of file sets to XMLs. + * + * @param xmlFilePath - The Path to the xml file containing the + * definition(s). + * + * @return fileSets - a Map of the definition(s) found in + * the xml file. + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException + */ + static Map readDefinitionsXML(File xmlFile) throws FilesSetsManager.FilesSetsManagerException { + Map filesSets = new HashMap<>(); + if (!xmlFile.exists()) { + return filesSets; + } + // Check if the file can be read. + if (!xmlFile.canRead()) { + logger.log(Level.SEVERE, "FilesSet definition file at {0} exists, but cannot be read", xmlFile.getPath()); // NON-NLS + return filesSets; + } + // Parse the XML in the file. + Document doc = XMLUtil.loadDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath()); + if (doc == null) { + logger.log(Level.SEVERE, "FilesSet definition file at {0}", xmlFile.getPath()); // NON-NLS + return filesSets; + } + // Get the root element. + Element root = doc.getDocumentElement(); + if (root == null) { + logger.log(Level.SEVERE, "Failed to get root {0} element tag of FilesSet definition file at {1}", new Object[]{FILE_SETS_ROOT_TAG, xmlFile.getPath()}); // NON-NLS + return filesSets; + } + // Read in the files set definitions. + NodeList setElems = root.getElementsByTagName(FILE_SET_TAG); + for (int i = 0; i < setElems.getLength(); ++i) { + readFilesSet((Element) setElems.item(i), filesSets, xmlFile.getPath()); } return filesSets; } @@ -422,6 +461,102 @@ class InterestingItemsFilesSetSettings implements Serializable { return true; } + /** + * Write the FilesSets to a file as an xml. + * + * @param xmlFile the file you will be writing the FilesSets to + * @param interestingFilesSets a map of the file sets you wish to write to + * the xml file + * + * @return true for successfully writing, false for a failure + */ + static boolean exportXmlDefinitionsFile(File xmlFile, List interestingFilesSets) { + DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); + try { + // Create the new XML document. + DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG); + doc.appendChild(rootElement); + // Add the interesting files sets to the document. + for (FilesSet set : interestingFilesSets) { + // Add the files set element and its attributes. + Element setElement = doc.createElement(FILE_SET_TAG); + setElement.setAttribute(NAME_ATTR, set.getName()); + setElement.setAttribute(DESC_ATTR, set.getDescription()); + setElement.setAttribute(IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles())); + // Add the child elements for the set membership rules. + // All conditions of a rule will be written as a single element in the xml + for (FilesSet.Rule rule : set.getRules().values()) { + // Add a rule element with the appropriate name Condition + // type tag. + Element ruleElement; + + FileNameCondition nameCondition = rule.getFileNameCondition(); + //The element type is just being used as another attribute for + //the name condition in legacy xmls. + //For rules which don't contain a name condition it doesn't matter + //what type of element it is + if (nameCondition instanceof FilesSet.Rule.FullNameCondition) { + ruleElement = doc.createElement(NAME_RULE_TAG); + } else { + ruleElement = doc.createElement(EXTENSION_RULE_TAG); + } + // Add the optional rule name attribute. + ruleElement.setAttribute(NAME_ATTR, rule.getName()); + if (nameCondition != null) { + // Add the name Condition regex attribute + ruleElement.setAttribute(REGEX_ATTR, Boolean.toString(nameCondition.isRegex())); + // Add the name Condition text as the rule element content. + ruleElement.setTextContent(nameCondition.getTextToMatch()); + } + // Add the type Condition attribute. + MetaTypeCondition typeCondition = rule.getMetaTypeCondition(); + switch (typeCondition.getMetaType()) { + case FILES: + ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_FILES); + break; + case DIRECTORIES: + ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_DIRS); + break; + default: + ruleElement.setAttribute(TYPE_FILTER_ATTR, TYPE_FILTER_VALUE_ALL); + break; + } + // Add the optional path Condition. + ParentPathCondition pathCondition = rule.getPathCondition(); + if (pathCondition != null) { + if (pathCondition.isRegex()) { + ruleElement.setAttribute(PATH_REGEX_ATTR, pathCondition.getTextToMatch()); + } else { + ruleElement.setAttribute(PATH_FILTER_ATTR, pathCondition.getTextToMatch()); + } + } + //Add the optional MIME type condition + MimeTypeCondition mimeCondition = rule.getMimeTypeCondition(); + if (mimeCondition != null) { + ruleElement.setAttribute(MIME_ATTR, mimeCondition.getMimeType()); + } + //Add the optional file size condition + FileSizeCondition sizeCondition = rule.getFileSizeCondition(); + if (sizeCondition != null) { + ruleElement.setAttribute(FS_COMPARATOR_ATTR, sizeCondition.getComparator().getSymbol()); + ruleElement.setAttribute(FS_SIZE_ATTR, Integer.toString(sizeCondition.getSizeValue())); + ruleElement.setAttribute(FS_UNITS_ATTR, sizeCondition.getUnit().getName()); + } + setElement.appendChild(ruleElement); + } + rootElement.appendChild(setElement); + } + // Overwrite the previous definitions file. Note that the utility + // method logs an error on failure. + return XMLUtil.saveDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath(), XML_ENCODING, doc); + } catch (ParserConfigurationException ex) { + logger.log(Level.SEVERE, "Error writing interesting files definition file to " + xmlFile.getPath(), ex); // NON-NLS + return false; + } + } + /** * Construct a meta-type condition for a FilesSet membership rule from data * in an XML element. @@ -429,30 +564,40 @@ class InterestingItemsFilesSetSettings implements Serializable { * @param ruleElement The XML element. * * @return The meta-type condition, or null if there is an error (logged). + * + * @throws + * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException */ - private static FilesSet.Rule.MetaTypeCondition readMetaTypeCondition(Element ruleElement) { - FilesSet.Rule.MetaTypeCondition condition = null; - String conditionAttribute = ruleElement.getAttribute(InterestingItemsFilesSetSettings.TYPE_FILTER_ATTR); - if (!conditionAttribute.isEmpty()) { - switch (conditionAttribute) { - case InterestingItemsFilesSetSettings.TYPE_FILTER_VALUE_FILES: - condition = new FilesSet.Rule.MetaTypeCondition(FilesSet.Rule.MetaTypeCondition.Type.FILES); - break; - case InterestingItemsFilesSetSettings.TYPE_FILTER_VALUE_DIRS: - condition = new FilesSet.Rule.MetaTypeCondition(FilesSet.Rule.MetaTypeCondition.Type.DIRECTORIES); - break; - case InterestingItemsFilesSetSettings.TYPE_FILTER_VALUE_FILES_AND_DIRS: - condition = new FilesSet.Rule.MetaTypeCondition(FilesSet.Rule.MetaTypeCondition.Type.FILES_AND_DIRECTORIES); - break; - default: - logger.log(Level.SEVERE, "Found {0} " + InterestingItemsFilesSetSettings.TYPE_FILTER_ATTR + " attribute with unrecognized value ''{0}'', ignoring malformed rule definition", conditionAttribute); // NON-NLS - break; + private static MetaTypeCondition readMetaTypeCondition(Element ruleElement) throws FilesSetsManager.FilesSetsManagerException { + MetaTypeCondition metaCondition = null; + // The rule must have a meta-type condition, unless a TSK Framework + // definitions file is being read. + if (!ruleElement.getAttribute(TYPE_FILTER_ATTR).isEmpty()) { + String conditionAttribute = ruleElement.getAttribute(TYPE_FILTER_ATTR); + if (!conditionAttribute.isEmpty()) { + switch (conditionAttribute) { + case TYPE_FILTER_VALUE_FILES: + metaCondition = new MetaTypeCondition(MetaTypeCondition.Type.FILES); + break; + case TYPE_FILTER_VALUE_DIRS: + metaCondition = new MetaTypeCondition(MetaTypeCondition.Type.DIRECTORIES); + break; + case TYPE_FILTER_VALUE_ALL: + case TYPE_FILTER_VALUE_FILES_AND_DIRS: //converts legacy xmls to current metaCondition terms + metaCondition = new MetaTypeCondition(MetaTypeCondition.Type.ALL); + break; + default: + logger.log(Level.SEVERE, "Found {0} " + TYPE_FILTER_ATTR + " attribute with unrecognized value ''{0}'', ignoring malformed rule definition", conditionAttribute); // NON-NLS + // Malformed attribute. + throw new FilesSetsManager.FilesSetsManagerException(String.format("Malformed XML for Metatype condition, %s, in rule %s", conditionAttribute, readRuleName(ruleElement))); + } } - } else { + } + if (metaCondition == null) { // Accept TSK Framework FilesSet definitions, // default to files. - condition = new FilesSet.Rule.MetaTypeCondition(FilesSet.Rule.MetaTypeCondition.Type.FILES); + metaCondition = new MetaTypeCondition(MetaTypeCondition.Type.FILES); } - return condition; + return metaCondition; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java index 8c1aae74ec..35e66647d8 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java @@ -86,6 +86,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); String timeStamp = dateFormat.format(Calendar.getInstance().getTime()); String ingestJobOutputDirName = context.getDataSource().getName() + "_" + context.getDataSource().getId() + "_" + timeStamp; + ingestJobOutputDirName = ingestJobOutputDirName.replace(':', '_'); ingestJobOutputDir = Paths.get(Case.getCurrentCase().getModuleDirectory(), VMExtractorIngestModuleFactory.getModuleName(), ingestJobOutputDirName); // create module output folder to write extracted virtual machine files to Files.createDirectories(ingestJobOutputDir); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 4b643e2a02..059ac8530f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -855,6 +855,8 @@ class ReportHTML implements TableReportModule { if (iconPath == null) { // use default Autopsy icon if custom icon is not set iconPath = "favicon.ico"; + } else { + iconPath = "agency_logo"; //ref to writeNav() for agency_logo } index.append("\n").append(reportTitle).append(" ").append( NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getDisplayName())).append( diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java index 22b2c83a38..8b82cdcba8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java @@ -28,17 +28,18 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.SwingWorker; import javax.swing.event.ListSelectionEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.StartupWindowProvider; import org.sleuthkit.autopsy.coreutils.Logger; @@ -280,24 +281,37 @@ public final class AutoIngestCasePanel extends JPanel { */ private void openCase(Path caseMetadataFilePath) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - AutoIngestCaseManager.getInstance().openCase(caseMetadataFilePath); - stopCasesTableRefreshes(); - StartupWindowProvider.getInstance().close(); - } catch (CaseActionException ex) { - logger.log(Level.SEVERE, String.format("Error while opening case with case metadata file path %s", caseMetadataFilePath), ex); - /* - * ReviewModeCaseManagerExceptions have user-friendly error - * messages. - */ - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getMessage(), - org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.cannotOpenCase"), - JOptionPane.ERROR_MESSAGE); + new SwingWorker<Void, Void>() { - } finally { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } + @Override + protected Void doInBackground() throws Exception { + AutoIngestCaseManager.getInstance().openCase(caseMetadataFilePath); + stopCasesTableRefreshes(); + StartupWindowProvider.getInstance().close(); + return null; + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, String.format("Error while opening case with case metadata file path %s", caseMetadataFilePath), ex); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.cannotOpenCase"), + JOptionPane.ERROR_MESSAGE); + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Error while opening case with case metadata file path %s", caseMetadataFilePath), ex); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.cannotOpenCase"), + JOptionPane.ERROR_MESSAGE); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + }.execute(); } /** diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java index 161a20286e..817fbac6a0 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2011 - 2017 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.experimental.autoingest; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -29,15 +28,12 @@ import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Date; -import org.sleuthkit.autopsy.coreutils.NetworkUtils; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import java.util.concurrent.TimeUnit; -import java.util.List; import javax.annotation.concurrent.Immutable; -import org.sleuthkit.autopsy.ingest.IngestModuleError; -import org.sleuthkit.autopsy.ingest.IngestManager.IngestManagerException; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; /** * A logger for the processing of an auto ingest job by an auto ingest node. An @@ -107,14 +103,16 @@ final class AutoIngestJobLogger { * Advanced users doing troubleshooting of an automated ingest cluster * should also consult the Autopsy and system logs as needed. * - * @param manifestPath The manifest for the auto ingest job. - * @param caseDirectoryPath The case directory. + * @param manifestPath The manifest for the auto ingest job. + * @param dataSourceFileName The file name of the data source for the auto + * ingest job. + * @param caseDirectoryPath The absolute path to the case directory. */ AutoIngestJobLogger(Path manifestPath, String dataSourceFileName, Path caseDirectoryPath) { this.manifestPath = manifestPath; manifestFileName = manifestPath.getFileName().toString(); this.dataSourceFileName = dataSourceFileName; - this.caseDirectoryPath = caseDirectoryPath; + this.caseDirectoryPath = caseDirectoryPath; hostName = NetworkUtils.getLocalHostName(); } @@ -195,30 +193,34 @@ final class AutoIngestJobLogger { void logDataSourceProcessorCancelled() throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.WARNING, "Cancelled adding data source to case"); } - + /** * Logs selection of a data source processor - * @param dsp Name of the data source processor + * + * @param dsp Name of the data source processor + * * @throws AutoIngestJobLoggerException if there is an error writing the log * message. * @throws InterruptedException if interrupted while blocked waiting * to acquire an exclusive lock on the * log file. */ - void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException{ + void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.INFO, "Using data source processor: " + dsp); } - + /** * Logs the failure of the selected data source processor. - * @param dsp Name of the data source processor + * + * @param dsp Name of the data source processor + * * @throws AutoIngestJobLoggerException if there is an error writing the log * message. * @throws InterruptedException if interrupted while blocked waiting * to acquire an exclusive lock on the * log file. */ - void logDataSourceProcessorError(String dsp) throws AutoIngestJobLoggerException, InterruptedException{ + void logDataSourceProcessorError(String dsp) throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.ERROR, "Error processing with data source processor: " + dsp); } @@ -431,9 +433,10 @@ final class AutoIngestJobLogger { * log file. */ private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException { - try (Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) { + Path logPath = getLogPath(caseDirectoryPath); + try (Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, logPath.toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) { if (null != lock) { - File logFile = getLogPath(caseDirectoryPath).toFile(); + File logFile = logPath.toFile(); try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) { writer.println(String.format("%s %s: %s: %s: %-8s: %s", logDateFormat.format((Date.from(Instant.now()).getTime())), hostName, manifestFileName, dataSourceFileName, category.toString(), message)); } catch (IOException ex) { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index eb45ae0df4..07254c247c 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -73,14 +73,11 @@ import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; -import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor; -import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; @@ -100,6 +97,8 @@ import static org.sleuthkit.autopsy.experimental.autoingest.ManifestNodeData.Pro import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException; +import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor; +import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -212,7 +211,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang void startUp() throws AutoIngestManagerStartupException { SYS_LOGGER.log(Level.INFO, "Auto ingest starting"); try { - coordinationService = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()); + coordinationService = CoordinationService.getInstance(); } catch (CoordinationServiceException ex) { throw new AutoIngestManagerStartupException("Failed to get coordination service", ex); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java index 653fc65807..227c203177 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java @@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.experimental.configuration.AutoIngestSettingsPanel.UpdateConfigSwingWorker; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; @@ -86,7 +87,6 @@ public class SharedConfiguration { private static final String PREFERENCES_FOLDER = "Preferences"; //NON-NLS public static final String FILE_EXPORTER_FOLDER = "Automated File Exporter"; //NON-NLS - private static final String LOCK_ROOT = "/autopsy"; // NON-NLS private static final String UPLOAD_IN_PROGRESS_FILE = "uploadInProgress"; // NON-NLS private static final String moduleDirPath = PlatformUtil.getUserConfigDirectory(); private static final Logger logger = Logger.getLogger(SharedConfiguration.class.getName()); @@ -160,7 +160,7 @@ public class SharedConfiguration { File remoteFolder = getSharedFolder(); - try (Lock writeLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetExclusiveLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { + try (Lock writeLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { if (writeLock == null) { logger.log(Level.INFO, String.format("Failed to lock %s - another node is currently uploading or downloading configuration", remoteFolder.getAbsolutePath())); return SharedConfigResult.LOCKED; @@ -230,7 +230,7 @@ public class SharedConfiguration { File remoteFolder = getSharedFolder(); - try (Lock readLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetSharedLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { + try (Lock readLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { if (readLock == null) { return SharedConfigResult.LOCKED; } diff --git a/KeywordSearch/ivy.xml b/KeywordSearch/ivy.xml index 3728514fcd..01ba14e833 100644 --- a/KeywordSearch/ivy.xml +++ b/KeywordSearch/ivy.xml @@ -19,8 +19,8 @@ <dependency conf="autopsy->*" org="org.apache.solr" name="solr-solrj" rev="6.2.1"/> <dependency conf="autopsy->*" org="commons-lang" name="commons-lang" rev="2.4"/> <dependency conf="autopsy->*" org="commons-validator" name="commons-validator" rev="1.5.1"/> - <dependency conf="autopsy->*" org="org.apache.tika" name="tika-parsers" rev="1.5"/> - + <dependency conf="autopsy->*" org="org.apache.tika" name="tika-parsers" rev="1.14"/> + <!-- metadata-extractor is required by Tika but it depends on version 2.6.2 which suffers from an XMP library issue. --> <dependency conf="autopsy->*" org="com.drewnoakes" name="metadata-extractor" rev="2.7.2" /> @@ -37,6 +37,10 @@ <dependency conf="slf4j-libs->default" org="org.slf4j" name="slf4j-log4j12" rev="1.7.10"/> <dependency conf="slf4j-libs->default" org="org.slf4j" name="jcl-over-slf4j" rev="1.7.10"/> <dependency conf="slf4j-libs->default" org="org.slf4j" name="jul-to-slf4j" rev="1.7.10"/> - + + <!-- Tika 1.4 seems to declare a (transitive?) dependency on cleartk-util 3.2.2, but the most recent + version available is 2.0.0 Overriding the version worked--> + <override org="org.cleartk" module="cleartk-util" rev="2.0.0"/> + </dependencies> </ivy-module> diff --git a/KeywordSearch/nbproject/project.properties b/KeywordSearch/nbproject/project.properties index ac11234f18..668ae6ee66 100644 --- a/KeywordSearch/nbproject/project.properties +++ b/KeywordSearch/nbproject/project.properties @@ -1,63 +1,173 @@ +file.reference.aopalliance-1.0.jar=release/modules/ext/aopalliance-1.0.jar file.reference.apache-mime4j-core-0.7.2.jar=release/modules/ext/apache-mime4j-core-0.7.2.jar file.reference.apache-mime4j-dom-0.7.2.jar=release/modules/ext/apache-mime4j-dom-0.7.2.jar +file.reference.asm-5.0.4.jar=release/modules/ext/asm-5.0.4.jar file.reference.asm-all-3.1.jar=release/modules/ext/asm-all-3.1.jar -file.reference.aspectjrt-1.6.11.jar=release/modules/ext/aspectjrt-1.6.11.jar -file.reference.bcmail-jdk15-1.45.jar=release/modules/ext/bcmail-jdk15-1.45.jar -file.reference.bcprov-jdk15-1.45.jar=release/modules/ext/bcprov-jdk15-1.45.jar -file.reference.commons-compress-1.5.jar=release/modules/ext/commons-compress-1.5.jar +file.reference.bcmail-jdk15on-1.54.jar=release/modules/ext/bcmail-jdk15on-1.54.jar +file.reference.bcpkix-jdk15on-1.54.jar=release/modules/ext/bcpkix-jdk15on-1.54.jar +file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar +file.reference.boilerpipe-1.1.0.jar=release/modules/ext/boilerpipe-1.1.0.jar +file.reference.bzip2-0.9.1.jar=release/modules/ext/bzip2-0.9.1.jar +file.reference.c3p0-0.9.1.1.jar=release/modules/ext/c3p0-0.9.1.1.jar +file.reference.cdm-4.5.5.jar=release/modules/ext/cdm-4.5.5.jar +file.reference.cleartk-util-2.0.0.jar=release/modules/ext/cleartk-util-2.0.0.jar +file.reference.commons-beanutils-1.9.2.jar=release/modules/ext/commons-beanutils-1.9.2.jar +file.reference.commons-codec-1.10.jar=release/modules/ext/commons-codec-1.10.jar +file.reference.commons-collections-3.2.2.jar=release/modules/ext/commons-collections-3.2.2.jar +file.reference.commons-collections4-4.1.jar=release/modules/ext/commons-collections4-4.1.jar +file.reference.commons-compress-1.12.jar=release/modules/ext/commons-compress-1.12.jar +file.reference.commons-csv-1.0.jar=release/modules/ext/commons-csv-1.0.jar +file.reference.commons-digester-1.8.1.jar=release/modules/ext/commons-digester-1.8.1.jar +file.reference.commons-exec-1.3.jar=release/modules/ext/commons-exec-1.3.jar file.reference.commons-io-2.3.jar=release/modules/ext/commons-io-2.3.jar -file.reference.commons-lang-2.4-javadoc.jar=release/modules/ext/commons-lang-2.4-javadoc.jar -file.reference.commons-lang-2.4-sources.jar=release/modules/ext/commons-lang-2.4-sources.jar -file.reference.commons-lang-2.4.jar=release/modules/ext/commons-lang-2.4.jar +file.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5.jar +file.reference.commons-lang-2.6.jar=release/modules/ext/commons-lang-2.6.jar +file.reference.commons-logging-1.2.jar=release/modules/ext/commons-logging-1.2.jar file.reference.commons-logging-api-1.1.jar=release/modules/ext/commons-logging-api-1.1.jar -file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar -file.reference.fontbox-1.8.4.jar=release/modules/ext/fontbox-1.8.4.jar -file.reference.geronimo-stax-api_1.0_spec-1.0.1.jar=release/modules/ext/geronimo-stax-api_1.0_spec-1.0.1.jar +file.reference.commons-validator-1.5.1-javadoc.jar=release/modules/ext/commons-validator-1.5.1-javadoc.jar +file.reference.commons-validator-1.5.1-sources.jar=release/modules/ext/commons-validator-1.5.1-sources.jar +file.reference.commons-validator-1.5.1.jar=release/modules/ext/commons-validator-1.5.1.jar +file.reference.commons-vfs2-2.0.jar=release/modules/ext/commons-vfs2-2.0.jar +file.reference.ctakes-core-3.2.2.jar=release/modules/ext/ctakes-core-3.2.2.jar +file.reference.ctakes-core-res-3.2.2.jar=release/modules/ext/ctakes-core-res-3.2.2.jar +file.reference.ctakes-type-system-3.2.2.jar=release/modules/ext/ctakes-type-system-3.2.2.jar +file.reference.ctakes-utils-3.2.2.jar=release/modules/ext/ctakes-utils-3.2.2.jar +file.reference.curvesapi-1.04.jar=release/modules/ext/curvesapi-1.04.jar +file.reference.cxf-core-3.0.3.jar=release/modules/ext/cxf-core-3.0.3.jar +file.reference.cxf-rt-frontend-jaxrs-3.0.3.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.3.jar +file.reference.cxf-rt-rs-client-3.0.3.jar=release/modules/ext/cxf-rt-rs-client-3.0.3.jar +file.reference.cxf-rt-transports-http-3.0.3.jar=release/modules/ext/cxf-rt-transports-http-3.0.3.jar +file.reference.ehcache-core-2.6.2.jar=release/modules/ext/ehcache-core-2.6.2.jar +file.reference.findstructapi-0.0.1.jar=release/modules/ext/findstructapi-0.0.1.jar +file.reference.fontbox-2.0.3.jar=release/modules/ext/fontbox-2.0.3.jar +file.reference.geoapi-3.0.0.jar=release/modules/ext/geoapi-3.0.0.jar +file.reference.grib-4.5.5.jar=release/modules/ext/grib-4.5.5.jar +file.reference.gson-2.2.4.jar=release/modules/ext/gson-2.2.4.jar +file.reference.guava-17.0.jar=release/modules/ext/guava-17.0.jar +file.reference.hamcrest-core-1.3.jar=release/modules/ext/hamcrest-core-1.3.jar file.reference.httpclient-4.3.1.jar=release/modules/ext/httpclient-4.3.1.jar +file.reference.httpclient-4.4.1.jar=release/modules/ext/httpclient-4.4.1.jar file.reference.httpcore-4.3.jar=release/modules/ext/httpcore-4.3.jar +file.reference.httpcore-4.4.1.jar=release/modules/ext/httpcore-4.4.1.jar file.reference.httpmime-4.3.1.jar=release/modules/ext/httpmime-4.3.1.jar +file.reference.httpmime-4.4.1.jar=release/modules/ext/httpmime-4.4.1.jar +file.reference.httpservices-4.5.5.jar=release/modules/ext/httpservices-4.5.5.jar file.reference.icu4j-3.8.jar=release/modules/ext/icu4j-3.8.jar -file.reference.isoparser-1.0-RC-1.jar=release/modules/ext/isoparser-1.0-RC-1.jar +file.reference.isoparser-1.1.18.jar=release/modules/ext/isoparser-1.1.18.jar +file.reference.jackcess-2.1.4.jar=release/modules/ext/jackcess-2.1.4.jar +file.reference.jackcess-encrypt-2.1.1.jar=release/modules/ext/jackcess-encrypt-2.1.1.jar +file.reference.jackson-annotations-2.5.4.jar=release/modules/ext/jackson-annotations-2.5.4.jar +file.reference.jackson-core-2.8.1.jar=release/modules/ext/jackson-core-2.8.1.jar +file.reference.jackson-databind-2.5.4.jar=release/modules/ext/jackson-databind-2.5.4.jar +file.reference.jakarta-regexp-1.4.jar=release/modules/ext/jakarta-regexp-1.4.jar +file.reference.java-libpst-0.8.1.jar=release/modules/ext/java-libpst-0.8.1.jar +file.reference.javax.annotation-api-1.2.jar=release/modules/ext/javax.annotation-api-1.2.jar +file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0.1.jar +file.reference.jcip-annotations-1.0.jar=release/modules/ext/jcip-annotations-1.0.jar +file.reference.jcl-over-slf4j-1.7.7.jar=release/modules/ext/jcl-over-slf4j-1.7.7.jar +file.reference.jcommander-1.35.jar=release/modules/ext/jcommander-1.35.jar file.reference.jdom-1.0.jar=release/modules/ext/jdom-1.0.jar -file.reference.jempbox-1.8.4.jar=release/modules/ext/jempbox-1.8.4.jar +file.reference.jdom2-2.0.4.jar=release/modules/ext/jdom2-2.0.4.jar +file.reference.jempbox-1.8.12.jar=release/modules/ext/jempbox-1.8.12.jar file.reference.jericho-html-3.3-javadoc.jar=release/modules/ext/jericho-html-3.3-javadoc.jar file.reference.jericho-html-3.3-sources.jar=release/modules/ext/jericho-html-3.3-sources.jar file.reference.jericho-html-3.3.jar=release/modules/ext/jericho-html-3.3.jar +file.reference.jhighlight-1.0.2.jar=release/modules/ext/jhighlight-1.0.2.jar +file.reference.jj2000-5.2.jar=release/modules/ext/jj2000-5.2.jar +file.reference.jmatio-1.2.jar=release/modules/ext/jmatio-1.2.jar +file.reference.jna-4.1.0.jar=release/modules/ext/jna-4.1.0.jar +file.reference.joda-time-2.2.jar=release/modules/ext/joda-time-2.2.jar +file.reference.json-20140107.jar=release/modules/ext/json-20140107.jar +file.reference.json-simple-1.1.1.jar=release/modules/ext/json-simple-1.1.1.jar +file.reference.jsoup-1.7.2.jar=release/modules/ext/jsoup-1.7.2.jar +file.reference.jsr-275-0.9.3.jar=release/modules/ext/jsr-275-0.9.3.jar +file.reference.junit-4.11.jar=release/modules/ext/junit-4.11.jar file.reference.juniversalchardet-1.0.3.jar=release/modules/ext/juniversalchardet-1.0.3.jar +file.reference.junrar-0.7.jar=release/modules/ext/junrar-0.7.jar +file.reference.jVinci-2.6.0.jar=release/modules/ext/jVinci-2.6.0.jar +file.reference.jwnl-1.3.3.jar=release/modules/ext/jwnl-1.3.3.jar +file.reference.libsvm-3.1.jar=release/modules/ext/libsvm-3.1.jar file.reference.log4j-1.2.17.jar=release/modules/ext/log4j-1.2.17.jar -file.reference.metadata-extractor-2.7.2.jar=release/modules/ext/metadata-extractor-2.7.2.jar -file.reference.netcdf-4.2-min.jar=release/modules/ext/netcdf-4.2-min.jar +file.reference.lucene-analyzers-common-4.0.0.jar=release/modules/ext/lucene-analyzers-common-4.0.0.jar +file.reference.lucene-core-4.0.0.jar=release/modules/ext/lucene-core-4.0.0.jar +file.reference.lucene-queries-4.0.0.jar=release/modules/ext/lucene-queries-4.0.0.jar +file.reference.lucene-queryparser-4.0.0.jar=release/modules/ext/lucene-queryparser-4.0.0.jar +file.reference.lucene-sandbox-4.0.0.jar=release/modules/ext/lucene-sandbox-4.0.0.jar +file.reference.maven-scm-api-1.4.jar=release/modules/ext/maven-scm-api-1.4.jar +file.reference.maven-scm-provider-svn-commons-1.4.jar=release/modules/ext/maven-scm-provider-svn-commons-1.4.jar +file.reference.maven-scm-provider-svnexe-1.4.jar=release/modules/ext/maven-scm-provider-svnexe-1.4.jar +file.reference.metadata-extractor-2.9.1.jar=release/modules/ext/metadata-extractor-2.9.1.jar +file.reference.netcdf4-4.5.5.jar=release/modules/ext/netcdf4-4.5.5.jar file.reference.noggit-0.5.jar=release/modules/ext/noggit-0.5.jar +file.reference.noggit-0.6.jar=release/modules/ext/noggit-0.6.jar +file.reference.openaifsm-0.0.1.jar=release/modules/ext/openaifsm-0.0.1.jar +file.reference.opennlp-maxent-3.0.3.jar=release/modules/ext/opennlp-maxent-3.0.3.jar +file.reference.opennlp-tools-1.5.3.jar=release/modules/ext/opennlp-tools-1.5.3.jar file.reference.org.apache.felix.scr.annotations-1.6.0.jar=release/modules/ext/org.apache.felix.scr.annotations-1.6.0.jar file.reference.org.apache.felix.scr.generator-1.1.2.jar=release/modules/ext/org.apache.felix.scr.generator-1.1.2.jar file.reference.org.osgi.compendium-4.0.0.jar=release/modules/ext/org.osgi.compendium-4.0.0.jar file.reference.org.osgi.core-4.0.0.jar=release/modules/ext/org.osgi.core-4.0.0.jar -file.reference.pdfbox-1.8.4.jar=release/modules/ext/pdfbox-1.8.4.jar -file.reference.poi-3.10-beta2.jar=release/modules/ext/poi-3.10-beta2.jar -file.reference.poi-ooxml-3.10-beta2.jar=release/modules/ext/poi-ooxml-3.10-beta2.jar -file.reference.poi-ooxml-schemas-3.10-beta2.jar=release/modules/ext/poi-ooxml-schemas-3.10-beta2.jar -file.reference.poi-scratchpad-3.10-beta2.jar=release/modules/ext/poi-scratchpad-3.10-beta2.jar +file.reference.pdfbox-2.0.3.jar=release/modules/ext/pdfbox-2.0.3.jar +file.reference.pdfbox-debugger-2.0.3.jar=release/modules/ext/pdfbox-debugger-2.0.3.jar +file.reference.pdfbox-tools-2.0.3.jar=release/modules/ext/pdfbox-tools-2.0.3.jar +file.reference.plexus-utils-1.5.6.jar=release/modules/ext/plexus-utils-1.5.6.jar +file.reference.poi-3.15.jar=release/modules/ext/poi-3.15.jar +file.reference.poi-ooxml-3.15.jar=release/modules/ext/poi-ooxml-3.15.jar +file.reference.poi-ooxml-schemas-3.15.jar=release/modules/ext/poi-ooxml-schemas-3.15.jar +file.reference.poi-scratchpad-3.15.jar=release/modules/ext/poi-scratchpad-3.15.jar +file.reference.protobuf-java-2.5.0.jar=release/modules/ext/protobuf-java-2.5.0.jar file.reference.qdox-1.12.jar=release/modules/ext/qdox-1.12.jar -file.reference.rome-0.9.jar=release/modules/ext/rome-0.9.jar -file.reference.slf4j-api-1.7.6.jar=release/modules/ext/slf4j-api-1.7.6.jar +file.reference.quartz-2.2.0.jar=release/modules/ext/quartz-2.2.0.jar +file.reference.regexp-1.3.jar=release/modules/ext/regexp-1.3.jar +file.reference.rome-1.5.1.jar=release/modules/ext/rome-1.5.1.jar +file.reference.rome-utils-1.5.1.jar=release/modules/ext/rome-utils-1.5.1.jar +file.reference.sis-metadata-0.6.jar=release/modules/ext/sis-metadata-0.6.jar +file.reference.sis-netcdf-0.6.jar=release/modules/ext/sis-netcdf-0.6.jar +file.reference.sis-referencing-0.6.jar=release/modules/ext/sis-referencing-0.6.jar +file.reference.sis-storage-0.6.jar=release/modules/ext/sis-storage-0.6.jar +file.reference.sis-utility-0.6.jar=release/modules/ext/sis-utility-0.6.jar +file.reference.slf4j-api-1.7.12.jar=release/modules/ext/slf4j-api-1.7.12.jar file.reference.solr-solrj-4.9.1-javadoc.jar=release/modules/ext/solr-solrj-4.9.1-javadoc.jar file.reference.solr-solrj-4.9.1-sources.jar=release/modules/ext/solr-solrj-4.9.1-sources.jar file.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1.jar +file.reference.solr-solrj-6.2.1-javadoc.jar=release/modules/ext/solr-solrj-6.2.1-javadoc.jar +file.reference.solr-solrj-6.2.1-sources.jar=release/modules/ext/solr-solrj-6.2.1-sources.jar +file.reference.solr-solrj-6.2.1.jar=release/modules/ext/solr-solrj-6.2.1.jar +file.reference.spring-aop-3.1.2.RELEASE.jar=release/modules/ext/spring-aop-3.1.2.RELEASE.jar +file.reference.spring-asm-3.1.2.RELEASE.jar=release/modules/ext/spring-asm-3.1.2.RELEASE.jar +file.reference.spring-beans-3.1.2.RELEASE.jar=release/modules/ext/spring-beans-3.1.2.RELEASE.jar +file.reference.spring-context-3.1.2.RELEASE.jar=release/modules/ext/spring-context-3.1.2.RELEASE.jar +file.reference.spring-core-3.1.2.RELEASE.jar=release/modules/ext/spring-core-3.1.2.RELEASE.jar +file.reference.spring-expression-3.1.2.RELEASE.jar=release/modules/ext/spring-expression-3.1.2.RELEASE.jar +file.reference.sqlite-jdbc-3.8.11.2.jar=release/modules/ext/sqlite-jdbc-3.8.11.2.jar +file.reference.sqlwrapper-0.0.1.jar=release/modules/ext/sqlwrapper-0.0.1.jar +file.reference.stax2-api-3.1.4.jar=release/modules/ext/stax2-api-3.1.4.jar file.reference.tagsoup-1.2.1.jar=release/modules/ext/tagsoup-1.2.1.jar -file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar -file.reference.tika-parsers-1.5.jar=release/modules/ext/tika-parsers-1.5.jar -file.reference.vorbis-java-core-0.1-tests.jar=release/modules/ext/vorbis-java-core-0.1-tests.jar -file.reference.vorbis-java-tika-0.1.jar=release/modules/ext/vorbis-java-tika-0.1.jar -file.reference.wstx-asl-3.2.7.jar=release/modules/ext/wstx-asl-3.2.7.jar -file.reference.xmlbeans-2.3.0.jar=release/modules/ext/xmlbeans-2.3.0.jar -file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar +file.reference.tika-core-1.14.jar=release/modules/ext/tika-core-1.14.jar +file.reference.tika-parsers-1.14-javadoc.jar=release/modules/ext/tika-parsers-1.14-javadoc.jar +file.reference.tika-parsers-1.14-sources.jar=release/modules/ext/tika-parsers-1.14-sources.jar +file.reference.tika-parsers-1.14.jar=release/modules/ext/tika-parsers-1.14.jar +file.reference.udunits-4.5.5.jar=release/modules/ext/udunits-4.5.5.jar +file.reference.uimafit-core-2.1.0.jar=release/modules/ext/uimafit-core-2.1.0.jar +file.reference.uimaj-adapter-vinci-2.6.0.jar=release/modules/ext/uimaj-adapter-vinci-2.6.0.jar +file.reference.uimaj-core-2.5.0.jar=release/modules/ext/uimaj-core-2.5.0.jar +file.reference.uimaj-cpe-2.6.0.jar=release/modules/ext/uimaj-cpe-2.6.0.jar +file.reference.uimaj-document-annotation-2.5.0.jar=release/modules/ext/uimaj-document-annotation-2.5.0.jar +file.reference.uimaj-examples-2.4.0.jar=release/modules/ext/uimaj-examples-2.4.0.jar +file.reference.uimaj-tools-2.6.0.jar=release/modules/ext/uimaj-tools-2.6.0.jar +file.reference.vorbis-java-core-0.8.jar=release/modules/ext/vorbis-java-core-0.8.jar +file.reference.vorbis-java-tika-0.8.jar=release/modules/ext/vorbis-java-tika-0.8.jar +file.reference.woodstox-core-asl-4.4.1.jar=release/modules/ext/woodstox-core-asl-4.4.1.jar +file.reference.xmlbeans-2.6.0.jar=release/modules/ext/xmlbeans-2.6.0.jar +file.reference.xmlschema-core-2.1.0.jar=release/modules/ext/xmlschema-core-2.1.0.jar file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar +file.reference.xz-1.5.jar=release/modules/ext/xz-1.5.jar +file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial +javadoc.reference.commons-validator-1.5.1.jar=release/modules/ext/commons-validator-1.5.1-javadoc.jar license.file=../LICENSE-2.0.txt nbm.homepage=http://www.sleuthkit.org/autopsy/ nbm.needs.restart=true -spec.version.base=6.3 -javadoc.reference.commons-validator-1.5.1.jar=release/modules/ext/commons-validator-1.5.1-javadoc.jar -file.reference.commons-validator-1.5.1.jar=release/modules/ext/commons-validator-1.5.1.jar source.reference.commons-validator-1.5.1.jar=release/modules/ext/commons-validator-1.5.1-sources.jar +spec.version.base=6.3 diff --git a/KeywordSearch/nbproject/project.xml b/KeywordSearch/nbproject/project.xml index f990e7a88d..3e8c0d4bd4 100644 --- a/KeywordSearch/nbproject/project.xml +++ b/KeywordSearch/nbproject/project.xml @@ -133,214 +133,522 @@ </dependency> </module-dependencies> <public-packages> - <package>org.apache.commons.lang</package> - <package>org.apache.commons.lang.builder</package> - <package>org.apache.commons.lang.enums</package> - <package>org.apache.commons.lang.exception</package> - <package>org.apache.commons.lang.math</package> - <package>org.apache.commons.lang.mutable</package> - <package>org.apache.commons.lang.text</package> - <package>org.apache.commons.lang.time</package> <package>org.apache.commons.logging.impl</package> - <package>org.apache.tika</package> - <package>org.apache.tika.config</package> - <package>org.apache.tika.detect</package> - <package>org.apache.tika.exception</package> - <package>org.apache.tika.extractor</package> - <package>org.apache.tika.fork</package> - <package>org.apache.tika.io</package> - <package>org.apache.tika.language</package> - <package>org.apache.tika.metadata</package> - <package>org.apache.tika.mime</package> - <package>org.apache.tika.parser</package> - <package>org.apache.tika.parser.asm</package> - <package>org.apache.tika.parser.audio</package> - <package>org.apache.tika.parser.chm</package> - <package>org.apache.tika.parser.chm.accessor</package> - <package>org.apache.tika.parser.chm.assertion</package> - <package>org.apache.tika.parser.chm.core</package> - <package>org.apache.tika.parser.chm.exception</package> - <package>org.apache.tika.parser.chm.lzx</package> - <package>org.apache.tika.parser.crypto</package> - <package>org.apache.tika.parser.dwg</package> - <package>org.apache.tika.parser.epub</package> - <package>org.apache.tika.parser.executable</package> - <package>org.apache.tika.parser.external</package> - <package>org.apache.tika.parser.feed</package> - <package>org.apache.tika.parser.font</package> - <package>org.apache.tika.parser.hdf</package> - <package>org.apache.tika.parser.html</package> - <package>org.apache.tika.parser.image</package> - <package>org.apache.tika.parser.image.xmp</package> - <package>org.apache.tika.parser.internal</package> - <package>org.apache.tika.parser.iptc</package> - <package>org.apache.tika.parser.iwork</package> - <package>org.apache.tika.parser.jpeg</package> - <package>org.apache.tika.parser.mail</package> - <package>org.apache.tika.parser.mbox</package> - <package>org.apache.tika.parser.microsoft</package> - <package>org.apache.tika.parser.microsoft.ooxml</package> - <package>org.apache.tika.parser.mp3</package> - <package>org.apache.tika.parser.mp4</package> - <package>org.apache.tika.parser.netcdf</package> - <package>org.apache.tika.parser.odf</package> - <package>org.apache.tika.parser.opendocument</package> - <package>org.apache.tika.parser.pdf</package> - <package>org.apache.tika.parser.pkg</package> - <package>org.apache.tika.parser.prt</package> - <package>org.apache.tika.parser.rtf</package> <package>org.apache.tika.parser.txt</package> - <package>org.apache.tika.parser.video</package> - <package>org.apache.tika.parser.xml</package> - <package>org.apache.tika.sax</package> - <package>org.apache.tika.sax.xpath</package> - <package>org.apache.tika.utils</package> <package>org.sleuthkit.autopsy.keywordsearch</package> </public-packages> <class-path-extension> - <runtime-relative-path>ext/bcmail-jdk15-1.45.jar</runtime-relative-path> - <binary-origin>release/modules/ext/bcmail-jdk15-1.45.jar</binary-origin> + <runtime-relative-path>ext/commons-validator-1.5.1-sources.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-validator-1.5.1-sources.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/tika-parsers-1.5.jar</runtime-relative-path> - <binary-origin>release/modules/ext/tika-parsers-1.5.jar</binary-origin> + <runtime-relative-path>ext/commons-digester-1.8.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-digester-1.8.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/jericho-html-3.3.jar</runtime-relative-path> - <binary-origin>release/modules/ext/jericho-html-3.3.jar</binary-origin> + <runtime-relative-path>ext/jwnl-1.3.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jwnl-1.3.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/tika-core-1.14.jar</runtime-relative-path> + <binary-origin>release/modules/ext/tika-core-1.14.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpmime-4.4.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpmime-4.4.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/lucene-queryparser-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/lucene-queryparser-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-examples-2.4.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-examples-2.4.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/cdm-4.5.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cdm-4.5.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/gson-2.2.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/gson-2.2.4.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/org.osgi.compendium-4.0.0.jar</runtime-relative-path> <binary-origin>release/modules/ext/org.osgi.compendium-4.0.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/slf4j-api-1.7.6.jar</runtime-relative-path> - <binary-origin>release/modules/ext/slf4j-api-1.7.6.jar</binary-origin> + <runtime-relative-path>ext/geoapi-3.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/geoapi-3.0.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/xmlbeans-2.3.0.jar</runtime-relative-path> - <binary-origin>release/modules/ext/xmlbeans-2.3.0.jar</binary-origin> + <runtime-relative-path>ext/hamcrest-core-1.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/hamcrest-core-1.3.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/jempbox-1.8.4.jar</runtime-relative-path> - <binary-origin>release/modules/ext/jempbox-1.8.4.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/poi-ooxml-3.10-beta2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/poi-ooxml-3.10-beta2.jar</binary-origin> + <runtime-relative-path>ext/boilerpipe-1.1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/boilerpipe-1.1.0.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/commons-logging-api-1.1.jar</runtime-relative-path> <binary-origin>release/modules/ext/commons-logging-api-1.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/asm-all-3.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/asm-all-3.1.jar</binary-origin> + <runtime-relative-path>ext/jakarta-regexp-1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jakarta-regexp-1.4.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/solr-solrj-6.2.1-javadoc.jar</runtime-relative-path> - <binary-origin>release/modules/ext/solr-solrj-6.2.1-javadoc.jar</binary-origin> + <runtime-relative-path>ext/xmlbeans-2.6.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/xmlbeans-2.6.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/poi-3.10-beta2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/poi-3.10-beta2.jar</binary-origin> + <runtime-relative-path>ext/maven-scm-api-1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/maven-scm-api-1.4.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/icu4j-3.8.jar</runtime-relative-path> - <binary-origin>release/modules/ext/icu4j-3.8.jar</binary-origin> + <runtime-relative-path>ext/quartz-2.2.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/quartz-2.2.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/fontbox-1.8.4.jar</runtime-relative-path> - <binary-origin>release/modules/ext/fontbox-1.8.4.jar</binary-origin> + <runtime-relative-path>ext/jackcess-2.1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackcess-2.1.4.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/xmpcore-5.1.2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/xmpcore-5.1.2.jar</binary-origin> + <runtime-relative-path>ext/slf4j-api-1.7.12.jar</runtime-relative-path> + <binary-origin>release/modules/ext/slf4j-api-1.7.12.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/metadata-extractor-2.7.2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/metadata-extractor-2.7.2.jar</binary-origin> + <runtime-relative-path>ext/guava-17.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/guava-17.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/tagsoup-1.2.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/tagsoup-1.2.1.jar</binary-origin> + <runtime-relative-path>ext/opennlp-maxent-3.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/opennlp-maxent-3.0.3.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-compress-1.5.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-compress-1.5.jar</binary-origin> + <runtime-relative-path>ext/jVinci-2.6.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jVinci-2.6.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-lang-2.4-javadoc.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-lang-2.4-javadoc.jar</binary-origin> + <runtime-relative-path>ext/json-simple-1.1.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/json-simple-1.1.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/org.osgi.core-4.0.0.jar</runtime-relative-path> - <binary-origin>release/modules/ext/org.osgi.core-4.0.0.jar</binary-origin> + <runtime-relative-path>ext/sis-utility-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sis-utility-0.6.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/httpclient-4.4.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/httpclient-4.4.1.jar</binary-origin> + <runtime-relative-path>ext/jj2000-5.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jj2000-5.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/isoparser-1.0-RC-1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/isoparser-1.0-RC-1.jar</binary-origin> + <runtime-relative-path>ext/jhighlight-1.0.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jhighlight-1.0.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-cpe-2.6.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-cpe-2.6.0.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/log4j-1.2.17.jar</runtime-relative-path> <binary-origin>release/modules/ext/log4j-1.2.17.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-lang-2.4-sources.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-lang-2.4-sources.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/tika-core-1.5.jar</runtime-relative-path> - <binary-origin>release/modules/ext/tika-core-1.5.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/jericho-html-3.3-javadoc.jar</runtime-relative-path> - <binary-origin>release/modules/ext/jericho-html-3.3-javadoc.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/apache-mime4j-dom-0.7.2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/apache-mime4j-dom-0.7.2.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/org.apache.felix.scr.generator-1.1.2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/org.apache.felix.scr.generator-1.1.2.jar</binary-origin> + <runtime-relative-path>ext/ctakes-utils-3.2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/ctakes-utils-3.2.2.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/solr-solrj-6.2.1.jar</runtime-relative-path> <binary-origin>release/modules/ext/solr-solrj-6.2.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/poi-scratchpad-3.10-beta2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/poi-scratchpad-3.10-beta2.jar</binary-origin> + <runtime-relative-path>ext/apache-mime4j-dom-0.7.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/apache-mime4j-dom-0.7.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/netcdf-4.2-min.jar</runtime-relative-path> - <binary-origin>release/modules/ext/netcdf-4.2-min.jar</binary-origin> + <runtime-relative-path>ext/commons-compress-1.12.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-compress-1.12.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/vorbis-java-core-0.1-tests.jar</runtime-relative-path> - <binary-origin>release/modules/ext/vorbis-java-core-0.1-tests.jar</binary-origin> + <runtime-relative-path>ext/openaifsm-0.0.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/openaifsm-0.0.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/stax2-api-3.1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/stax2-api-3.1.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-collections4-4.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-collections4-4.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/cxf-rt-rs-client-3.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cxf-rt-rs-client-3.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jackson-annotations-2.5.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackson-annotations-2.5.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/findstructapi-0.0.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/findstructapi-0.0.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jcommander-1.35.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jcommander-1.35.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sis-metadata-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sis-metadata-0.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/isoparser-1.1.18.jar</runtime-relative-path> + <binary-origin>release/modules/ext/isoparser-1.1.18.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/vorbis-java-core-0.8.jar</runtime-relative-path> + <binary-origin>release/modules/ext/vorbis-java-core-0.8.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jdom2-2.0.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jdom2-2.0.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/java-libpst-0.8.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/java-libpst-0.8.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-codec-1.10.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-codec-1.10.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/lucene-queries-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/lucene-queries-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/bcprov-jdk15on-1.54.jar</runtime-relative-path> + <binary-origin>release/modules/ext/bcprov-jdk15on-1.54.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/poi-ooxml-schemas-3.15.jar</runtime-relative-path> + <binary-origin>release/modules/ext/poi-ooxml-schemas-3.15.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/poi-scratchpad-3.15.jar</runtime-relative-path> + <binary-origin>release/modules/ext/poi-scratchpad-3.15.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-vfs2-2.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-vfs2-2.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jcl-over-slf4j-1.7.7.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jcl-over-slf4j-1.7.7.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/tika-parsers-1.14.jar</runtime-relative-path> + <binary-origin>release/modules/ext/tika-parsers-1.14.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jackcess-encrypt-2.1.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackcess-encrypt-2.1.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/spring-expression-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-expression-3.1.2.RELEASE.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/lucene-analyzers-common-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/lucene-analyzers-common-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/tika-parsers-1.14-sources.jar</runtime-relative-path> + <binary-origin>release/modules/ext/tika-parsers-1.14-sources.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/javax.ws.rs-api-2.0.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/javax.ws.rs-api-2.0.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/junrar-0.7.jar</runtime-relative-path> + <binary-origin>release/modules/ext/junrar-0.7.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/libsvm-3.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/libsvm-3.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/xmlschema-core-2.1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/xmlschema-core-2.1.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jna-4.1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jna-4.1.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/xz-1.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/xz-1.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/qdox-1.12.jar</runtime-relative-path> + <binary-origin>release/modules/ext/qdox-1.12.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/bcpkix-jdk15on-1.54.jar</runtime-relative-path> + <binary-origin>release/modules/ext/bcpkix-jdk15on-1.54.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/bzip2-0.9.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/bzip2-0.9.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/grib-4.5.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/grib-4.5.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/maven-scm-provider-svn-commons-1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/maven-scm-provider-svn-commons-1.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/poi-3.15.jar</runtime-relative-path> + <binary-origin>release/modules/ext/poi-3.15.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/spring-aop-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-aop-3.1.2.RELEASE.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/json-20140107.jar</runtime-relative-path> + <binary-origin>release/modules/ext/json-20140107.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-core-2.5.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-core-2.5.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/pdfbox-2.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/pdfbox-2.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jmatio-1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jmatio-1.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/metadata-extractor-2.9.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/metadata-extractor-2.9.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-csv-1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-csv-1.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-validator-1.5.1-javadoc.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-validator-1.5.1-javadoc.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/solr-solrj-6.2.1-javadoc.jar</runtime-relative-path> + <binary-origin>release/modules/ext/solr-solrj-6.2.1-javadoc.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/lucene-sandbox-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/lucene-sandbox-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jericho-html-3.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jericho-html-3.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jsr-275-0.9.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jsr-275-0.9.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpservices-4.5.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpservices-4.5.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/plexus-utils-1.5.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/plexus-utils-1.5.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/fontbox-2.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/fontbox-2.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/bcmail-jdk15on-1.54.jar</runtime-relative-path> + <binary-origin>release/modules/ext/bcmail-jdk15on-1.54.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/spring-beans-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-beans-3.1.2.RELEASE.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/asm-all-3.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/asm-all-3.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/tika-parsers-1.14-javadoc.jar</runtime-relative-path> + <binary-origin>release/modules/ext/tika-parsers-1.14-javadoc.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/icu4j-3.8.jar</runtime-relative-path> + <binary-origin>release/modules/ext/icu4j-3.8.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/curvesapi-1.04.jar</runtime-relative-path> + <binary-origin>release/modules/ext/curvesapi-1.04.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/xmpcore-5.1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/xmpcore-5.1.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sis-referencing-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sis-referencing-0.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/ctakes-core-res-3.2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/ctakes-core-res-3.2.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/joda-time-2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/joda-time-2.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/tagsoup-1.2.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/tagsoup-1.2.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/lucene-core-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/lucene-core-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/asm-5.0.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/asm-5.0.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-logging-1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-logging-1.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jackson-core-2.8.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackson-core-2.8.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/org.osgi.core-4.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/org.osgi.core-4.0.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/c3p0-0.9.1.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/c3p0-0.9.1.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sqlwrapper-0.0.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sqlwrapper-0.0.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jericho-html-3.3-javadoc.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jericho-html-3.3-javadoc.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/udunits-4.5.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/udunits-4.5.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/aopalliance-1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/aopalliance-1.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-collections-3.2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-collections-3.2.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/cxf-rt-frontend-jaxrs-3.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cxf-rt-frontend-jaxrs-3.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-document-annotation-2.5.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-document-annotation-2.5.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/woodstox-core-asl-4.4.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/woodstox-core-asl-4.4.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/poi-ooxml-3.15.jar</runtime-relative-path> + <binary-origin>release/modules/ext/poi-ooxml-3.15.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/org.apache.felix.scr.generator-1.1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/org.apache.felix.scr.generator-1.1.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/zookeeper-3.4.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/cxf-rt-transports-http-3.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cxf-rt-transports-http-3.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/pdfbox-debugger-2.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/pdfbox-debugger-2.0.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jackson-databind-2.5.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackson-databind-2.5.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/spring-core-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-core-3.1.2.RELEASE.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/netcdf4-4.5.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/netcdf4-4.5.5.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpclient-4.4.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpclient-4.4.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/noggit-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/noggit-0.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/solr-solrj-6.2.1-sources.jar</runtime-relative-path> + <binary-origin>release/modules/ext/solr-solrj-6.2.1-sources.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/spring-asm-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-asm-3.1.2.RELEASE.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/commons-io-2.5.jar</runtime-relative-path> <binary-origin>release/modules/ext/commons-io-2.5.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-codec-1.5.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-codec-1.5.jar</binary-origin> - </class-path-extension> + <runtime-relative-path>ext/junit-4.11.jar</runtime-relative-path> + <binary-origin>release/modules/ext/junit-4.11.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/protobuf-java-2.5.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/protobuf-java-2.5.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sis-netcdf-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sis-netcdf-0.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/javax.annotation-api-1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/javax.annotation-api-1.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpcore-4.4.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpcore-4.4.1.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/jericho-html-3.3-sources.jar</runtime-relative-path> <binary-origin>release/modules/ext/jericho-html-3.3-sources.jar</binary-origin> </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/solr-solrj-6.2.1-sources.jar</runtime-relative-path> - <binary-origin>release/modules/ext/solr-solrj-6.2.1-sources.jar</binary-origin> - </class-path-extension> <class-path-extension> <runtime-relative-path>ext/juniversalchardet-1.0.3.jar</runtime-relative-path> <binary-origin>release/modules/ext/juniversalchardet-1.0.3.jar</binary-origin> @@ -350,76 +658,112 @@ <binary-origin>release/modules/ext/org.apache.felix.scr.annotations-1.6.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/noggit-0.6.jar</runtime-relative-path> - <binary-origin>release/modules/ext/noggit-0.6.jar</binary-origin> + <runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-lang-2.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sqlite-jdbc-3.8.11.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sqlite-jdbc-3.8.11.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jempbox-1.8.12.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jempbox-1.8.12.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/maven-scm-provider-svnexe-1.4.jar</runtime-relative-path> + <binary-origin>release/modules/ext/maven-scm-provider-svnexe-1.4.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/opennlp-tools-1.5.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/opennlp-tools-1.5.3.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/apache-mime4j-core-0.7.2.jar</runtime-relative-path> <binary-origin>release/modules/ext/apache-mime4j-core-0.7.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/httpmime-4.4.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/httpmime-4.4.1.jar</binary-origin> + <runtime-relative-path>ext/rome-utils-1.5.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/rome-utils-1.5.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/qdox-1.12.jar</runtime-relative-path> - <binary-origin>release/modules/ext/qdox-1.12.jar</binary-origin> + <runtime-relative-path>ext/pdfbox-tools-2.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/pdfbox-tools-2.0.3.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/httpcore-4.4.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/httpcore-4.4.1.jar</binary-origin> + <runtime-relative-path>ext/sis-storage-0.6.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sis-storage-0.6.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jcip-annotations-1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jcip-annotations-1.0.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/commons-validator-1.5.1.jar</runtime-relative-path> <binary-origin>release/modules/ext/commons-validator-1.5.1.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-validator-1.5.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-validator-1.5.1.jar</binary-origin> + <runtime-relative-path>ext/uimafit-core-2.1.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimafit-core-2.1.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/commons-lang-2.4.jar</runtime-relative-path> - <binary-origin>release/modules/ext/commons-lang-2.4.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/rome-0.9.jar</runtime-relative-path> - <binary-origin>release/modules/ext/rome-0.9.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/geronimo-stax-api_1.0_spec-1.0.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/geronimo-stax-api_1.0_spec-1.0.1.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/vorbis-java-tika-0.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/vorbis-java-tika-0.1.jar</binary-origin> + <runtime-relative-path>ext/cleartk-util-2.0.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cleartk-util-2.0.0.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/jdom-1.0.jar</runtime-relative-path> <binary-origin>release/modules/ext/jdom-1.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/poi-ooxml-schemas-3.10-beta2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/poi-ooxml-schemas-3.10-beta2.jar</binary-origin> + <runtime-relative-path>ext/cxf-core-3.0.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/cxf-core-3.0.3.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/pdfbox-1.8.4.jar</runtime-relative-path> - <binary-origin>release/modules/ext/pdfbox-1.8.4.jar</binary-origin> + <runtime-relative-path>ext/regexp-1.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/regexp-1.3.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/wstx-asl-3.2.7.jar</runtime-relative-path> - <binary-origin>release/modules/ext/wstx-asl-3.2.7.jar</binary-origin> + <runtime-relative-path>ext/commons-beanutils-1.9.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-beanutils-1.9.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/aspectjrt-1.6.11.jar</runtime-relative-path> - <binary-origin>release/modules/ext/aspectjrt-1.6.11.jar</binary-origin> + <runtime-relative-path>ext/ehcache-core-2.6.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/ehcache-core-2.6.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path> - <binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin> + <runtime-relative-path>ext/spring-context-3.1.2.RELEASE.jar</runtime-relative-path> + <binary-origin>release/modules/ext/spring-context-3.1.2.RELEASE.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/bcprov-jdk15-1.45.jar</runtime-relative-path> - <binary-origin>release/modules/ext/bcprov-jdk15-1.45.jar</binary-origin> + <runtime-relative-path>ext/ctakes-type-system-3.2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/ctakes-type-system-3.2.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/rome-1.5.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/rome-1.5.1.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-adapter-vinci-2.6.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-adapter-vinci-2.6.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/ctakes-core-3.2.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/ctakes-core-3.2.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jsoup-1.7.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jsoup-1.7.2.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-exec-1.3.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-exec-1.3.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/uimaj-tools-2.6.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/uimaj-tools-2.6.0.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/vorbis-java-tika-0.8.jar</runtime-relative-path> + <binary-origin>release/modules/ext/vorbis-java-tika-0.8.jar</binary-origin> </class-path-extension> </data> </configuration> diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java index 0f5d42ca03..dd87722705 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java @@ -89,8 +89,17 @@ class AddKeywordsDialog extends javax.swing.JDialog { * Intended to be used to redisplay any keywords that contained errors * @param initialKeywords */ - void setInitialKeywordList(String initialKeywords){ + void setInitialKeywordList(String initialKeywords, boolean isLiteral, boolean isWholeWord){ keywordTextArea.setText(initialKeywords); + if (!isLiteral){ + regexRadioButton.setSelected(true); + } + else if (isWholeWord){ + exactRadioButton.setSelected(true); + } + else { + substringRadioButton.setSelected(true); + } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 0a7215bee1..408ac811c8 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -309,8 +309,14 @@ GlobalEditListPanel.keywordDupesSkipped.text={0} keyword was already in the list GlobalEditListPanel.keywordDupesSkippedPlural.text={0} keywords were already in the list. GlobalEditListPanel.keywordErrors.text={0} keyword could not be parsed. Please review and try again. GlobalEditListPanel.keywordErrorsPlural.text={0} keywords could not be parsed. Please review and try again. +GlobalListsManagementPanel.exportButton.text=Export List +GlobalListsManagementPanel.deleteListButton.text=Delete List +GlobalListsManagementPanel.copyListButton.text=Copy List +GlobalListsManagementPanel.renameListButton.text=Rename List +GlobalEditListPanel.editWordButton.text=Edit keyword SolrSearchService.ServiceName=Solr Keyword Search Service SolrSearchService.IndexUpgradeDialog.title=Text Index Upgrade Required In Order To Open Case SolrSearchService.IndexUpgradeDialog.msg=<html>The text index upgrade can take some time. <br />When completed, you will be able to see existing keyword search results and perform literal keyword searches,<br />but you will not be able to add new text to the index or perform regex searches. You may instead open the case<br /> with your previous version of this application. Do you wish to proceed with the index upgrade?</html> SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only SolrSearchService.IndexReadOnlyDialog.msg=<html>The text index for this case is read-only. <br />You will be able to see existing keyword search results and perform literal keyword searches,<br />but you will not be able to add new text to the index or perform regex searches. You may instead open the case<br /> with your previous version of this application.</html> + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties index 762a922a88..78412ea29e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties @@ -278,3 +278,6 @@ SolrConnectionCheck.Hostname=hostname\u304c\u7121\u52b9\u3067\u3059\u3002 SolrConnectionCheck.Port=\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7121\u52b9\u3067\u3059\u3002 SolrConnectionCheck.MissingHostname=hostname\u304c\u6b20\u3051\u3066\u307e\u3059\u3002 RawText.getText.error.msg=\u30c6\u30ad\u30b9\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +GlobalListsManagementPanel.exportButton.text=\u30ea\u30b9\u30c8\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8 +GlobalListsManagementPanel.deleteListButton.text=\u30ea\u30b9\u30c8\u3092\u524a\u9664 +GlobalListsManagementPanel.copyListButton.text=\u30ea\u30b9\u30c8\u3092\u30b3\u30d4\u30fc diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java index 91136040c9..2d19342923 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java @@ -155,26 +155,50 @@ class DropdownToolbar extends javax.swing.JPanel { @Override public void propertyChange(PropertyChangeEvent evt) { - String changed = evt.getPropertyName(); - if (changed.equals(Case.Events.CURRENT_CASE.toString())) { - dropPanel.clearSearchBox(); - if (RuntimeProperties.runningWithGUI() || null == evt.getNewValue()) { - try { - Server server = KeywordSearch.getServer(); - Index indexInfo = server.getIndexInfo(); - if (server.coreIsOpen() && IndexFinder.getCurrentSolrVersion().equals(indexInfo.getSolrVersion())) { - boolean schemaIsCurrent = IndexFinder.getCurrentSchemaVersion().equals(indexInfo.getSchemaVersion()); - listsButton.setEnabled(schemaIsCurrent); - searchDropButton.setEnabled(true); - dropPanel.setRegexSearchEnabled(schemaIsCurrent); - active = true; - } else { + if (RuntimeProperties.runningWithGUI()) { + String changed = evt.getPropertyName(); + if (changed.equals(Case.Events.CURRENT_CASE.toString())) { + if (null != evt.getNewValue()) { + /* + * A case has been opened. + */ + try { + Server server = KeywordSearch.getServer(); + Index indexInfo = server.getIndexInfo(); + if (server.coreIsOpen() && IndexFinder.getCurrentSolrVersion().equals(indexInfo.getSolrVersion())) { + /* + * Solr version is current, so check the Solr + * schema version and selectively enable the ad + * hoc search UI components. + */ + boolean schemaIsCurrent = IndexFinder.getCurrentSchemaVersion().equals(indexInfo.getSchemaVersion()); + listsButton.setEnabled(schemaIsCurrent); + searchDropButton.setEnabled(true); + dropPanel.setRegexSearchEnabled(schemaIsCurrent); + active = true; + } else { + /* + * Unsupported Solr version, disable the ad hoc + * search UI components. + */ + searchDropButton.setEnabled(false); + listsButton.setEnabled(false); + active = false; + } + } catch (KeywordSearchModuleException ex) { + /* + * Error, disable the ad hoc search UI components. + */ + logger.log(Level.SEVERE, "Error getting text index info", ex); //NON-NLS searchDropButton.setEnabled(false); listsButton.setEnabled(false); active = false; } - } catch (KeywordSearchModuleException ex) { - logger.log(Level.SEVERE, "Error getting text index info", ex); //NON-NLS + } else { + /* + * A case has been closed. + */ + dropPanel.clearSearchBox(); searchDropButton.setEnabled(false); listsButton.setEnabled(false); active = false; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form index c43b029272..6ccf9f6836 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form @@ -44,52 +44,34 @@ <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="102" attributes="0"> + <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="10" pref="10" max="-2" attributes="0"/> - <Component id="addKeywordPanel" max="32767" attributes="0"/> - </Group> <Group type="102" attributes="0"> + <Component id="keywordsLabel" min="-2" max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + <Group type="102" alignment="0" attributes="0"> + <EmptySpace min="-2" pref="10" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="listOptionsLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="listOptionsSeparator" max="32767" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <Component id="keywordOptionsLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="keywordOptionsSeparator" max="32767" attributes="0"/> - </Group> - <Group type="102" attributes="0"> - <EmptySpace min="10" pref="10" max="-2" attributes="0"/> - <Component id="jScrollPane1" pref="482" max="32767" attributes="0"/> - </Group> + <Component id="jScrollPane1" pref="483" max="32767" attributes="0"/> <Group type="102" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="keywordsLabel" min="-2" max="-2" attributes="0"/> - <Group type="102" attributes="0"> - <EmptySpace min="10" pref="10" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="ingestMessagesCheckbox" alignment="0" min="-2" max="-2" attributes="0"/> - <Group type="102" alignment="0" attributes="0"> - <Component id="exportButton" min="-2" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="saveListButton" min="-2" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="deleteListButton" min="-2" max="-2" attributes="0"/> - </Group> - </Group> + <Component id="ingestMessagesCheckbox" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> + <Component id="newKeywordsButton" linkSize="3" min="-2" pref="133" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="14" max="-2" attributes="0"/> + <Component id="editWordButton" linkSize="3" min="-2" pref="132" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="14" max="-2" attributes="0"/> + <Component id="deleteWordButton" linkSize="3" min="-2" pref="144" max="-2" attributes="0"/> </Group> </Group> <EmptySpace min="0" pref="0" max="32767" attributes="0"/> </Group> </Group> - <EmptySpace max="-2" attributes="0"/> </Group> </Group> + <EmptySpace min="-2" max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -98,34 +80,17 @@ <Group type="102" alignment="1" attributes="0"> <EmptySpace min="-2" max="-2" attributes="0"/> <Component id="keywordsLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" max="-2" attributes="0"/> - <Component id="jScrollPane1" pref="117" max="32767" attributes="0"/> - <EmptySpace min="-2" pref="10" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="1" attributes="0"> - <Component id="keywordOptionsSeparator" min="-2" pref="7" max="-2" attributes="0"/> - <Component id="keywordOptionsLabel" alignment="1" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace min="-2" pref="7" max="-2" attributes="0"/> - <Component id="addKeywordPanel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="0" pref="0" max="-2" attributes="0"/> - <Component id="listOptionsLabel" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="123" max="-2" attributes="0"/> - <Component id="listOptionsSeparator" min="-2" pref="6" max="-2" attributes="0"/> - </Group> - </Group> - <EmptySpace min="-2" max="-2" attributes="0"/> - <Component id="ingestMessagesCheckbox" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jScrollPane1" pref="257" max="32767" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> - <Component id="exportButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="saveListButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="deleteListButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="deleteWordButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="newKeywordsButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="editWordButton" alignment="3" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="ingestMessagesCheckbox" min="-2" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="9" max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -162,61 +127,6 @@ </Component> </SubComponents> </Container> - <Container class="javax.swing.JPanel" name="addKeywordPanel"> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="newKeywordsButton" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="deleteWordButton" min="-2" max="-2" attributes="0"/> - <EmptySpace max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace min="-2" pref="0" max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="deleteWordButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="newKeywordsButton" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace min="-2" pref="72" max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JButton" name="deleteWordButton"> - <Properties> - <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> - <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/delete16.png"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.deleteWordButton.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="deleteWordButtonActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JButton" name="newKeywordsButton"> - <Properties> - <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> - <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/new16.png"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalEditListPanel.newKeywordsButton.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="newKeywordsButtonActionPerformed"/> - </Events> - </Component> - </SubComponents> - </Container> <Component class="javax.swing.JCheckBox" name="ingestMessagesCheckbox"> <Properties> <Property name="selected" type="boolean" value="true"/> @@ -238,58 +148,43 @@ </Property> </Properties> </Component> - <Component class="javax.swing.JLabel" name="keywordOptionsLabel"> + <Component class="javax.swing.JButton" name="newKeywordsButton"> <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/new16.png"/> + </Property> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.keywordOptionsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalEditListPanel.newKeywordsButton.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="newKeywordsButtonActionPerformed"/> + </Events> </Component> - <Component class="javax.swing.JLabel" name="listOptionsLabel"> - <Properties> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.listOptionsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> - <Component class="javax.swing.JSeparator" name="keywordOptionsSeparator"> - </Component> - <Component class="javax.swing.JSeparator" name="listOptionsSeparator"> - </Component> - <Component class="javax.swing.JButton" name="deleteListButton"> + <Component class="javax.swing.JButton" name="deleteWordButton"> <Properties> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/delete16.png"/> </Property> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.deleteListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.deleteWordButton.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="deleteListButtonActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteWordButtonActionPerformed"/> </Events> </Component> - <Component class="javax.swing.JButton" name="saveListButton"> + <Component class="javax.swing.JButton" name="editWordButton"> <Properties> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> - <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/save16.png"/> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/edit16.png"/> </Property> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.saveListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> - <Component class="javax.swing.JButton" name="exportButton"> - <Properties> - <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> - <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/export16.png"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.exportButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalEditListPanel.editWordButton.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="exportButtonActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editWordButtonActionPerformed"/> </Events> </Component> </SubComponents> diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java index 8c8262ecdf..227b034683 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,22 +19,17 @@ package org.sleuthkit.autopsy.keywordsearch; import java.awt.EventQueue; -import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.File; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import javax.swing.JFileChooser; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import org.netbeans.spi.options.OptionsPanelController; @@ -64,8 +59,6 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis private void customizeComponents() { newKeywordsButton.setToolTipText((NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.addWordToolTip"))); - exportButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.exportToFile")); - saveListButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip")); deleteWordButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.removeSelectedMsg")); keywordTable.getParent().setBackground(keywordTable.getBackground()); @@ -87,11 +80,10 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis lsm.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { - if (lsm.isSelectionEmpty() || currentKeywordList.isEditable() || IngestManager.getInstance().isIngestRunning()) { - deleteWordButton.setEnabled(false); - } else { - deleteWordButton.setEnabled(true); - } + boolean canDelete = !(lsm.isSelectionEmpty() || currentKeywordList.isEditable() || IngestManager.getInstance().isIngestRunning()); + boolean canEdit = canDelete && (lsm.getMaxSelectionIndex() == lsm.getMinSelectionIndex()); //edit only enabled with single selection + deleteWordButton.setEnabled(canDelete); + editWordButton.setEnabled(canEdit); } }); @@ -109,322 +101,56 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis } }); } - + + /** + * Enables and disables buttons on this panel based on the current state. + */ void setButtonStates() { boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); boolean isListSelected = currentKeywordList != null; // items that only need a selected list - boolean canEditList = ((isListSelected == true) && (isIngestRunning == false)); + boolean canEditList = isListSelected && !isIngestRunning; ingestMessagesCheckbox.setEnabled(canEditList); ingestMessagesCheckbox.setSelected(currentKeywordList != null && currentKeywordList.getIngestMessages()); - listOptionsLabel.setEnabled(canEditList); - listOptionsSeparator.setEnabled(canEditList); // items that need an unlocked list w/out ingest running - boolean isListLocked = ((isListSelected == false) || (currentKeywordList.isEditable())); - boolean canAddWord = isListSelected && !isIngestRunning && !isListLocked; + boolean canAddWord = canEditList && !currentKeywordList.isEditable(); newKeywordsButton.setEnabled(canAddWord); - keywordOptionsLabel.setEnabled(canAddWord); - keywordOptionsSeparator.setEnabled(canAddWord); - deleteListButton.setEnabled(canAddWord); // items that need a non-empty list if ((currentKeywordList == null) || (currentKeywordList.getKeywords().isEmpty())) { - saveListButton.setEnabled(false); - exportButton.setEnabled(false); deleteWordButton.setEnabled(false); - } else { - saveListButton.setEnabled(true); - exportButton.setEnabled(true); - // We do not set deleteWordButton because it will be set by the list select model code when a word is selected. + editWordButton.setEnabled(false); } } /** - * 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. + * Adds keywords to a keyword list, returns true if at least one keyword was successfully added and no + * duplicates were found. + * + * @return - true or false */ - @SuppressWarnings("unchecked") - // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents - private void initComponents() { - - listEditorPanel = new javax.swing.JPanel(); - jScrollPane1 = new javax.swing.JScrollPane(); - keywordTable = new javax.swing.JTable(); - addKeywordPanel = new javax.swing.JPanel(); - deleteWordButton = new javax.swing.JButton(); - newKeywordsButton = new javax.swing.JButton(); - ingestMessagesCheckbox = new javax.swing.JCheckBox(); - keywordsLabel = new javax.swing.JLabel(); - keywordOptionsLabel = new javax.swing.JLabel(); - listOptionsLabel = new javax.swing.JLabel(); - keywordOptionsSeparator = new javax.swing.JSeparator(); - listOptionsSeparator = new javax.swing.JSeparator(); - deleteListButton = new javax.swing.JButton(); - saveListButton = new javax.swing.JButton(); - exportButton = new javax.swing.JButton(); - - setMinimumSize(new java.awt.Dimension(0, 0)); - - listEditorPanel.setMinimumSize(new java.awt.Dimension(0, 0)); - - jScrollPane1.setPreferredSize(new java.awt.Dimension(340, 300)); - - keywordTable.setModel(tableModel); - keywordTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); - keywordTable.setGridColor(new java.awt.Color(153, 153, 153)); - keywordTable.setMaximumSize(new java.awt.Dimension(30000, 30000)); - keywordTable.getTableHeader().setReorderingAllowed(false); - jScrollPane1.setViewportView(keywordTable); - - deleteWordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N - deleteWordButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteWordButton.text")); // NOI18N - deleteWordButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - deleteWordButtonActionPerformed(evt); - } - }); - - newKeywordsButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N - newKeywordsButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.newKeywordsButton.text")); // NOI18N - newKeywordsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - newKeywordsButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout addKeywordPanelLayout = new javax.swing.GroupLayout(addKeywordPanel); - addKeywordPanel.setLayout(addKeywordPanelLayout); - addKeywordPanelLayout.setHorizontalGroup( - addKeywordPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(addKeywordPanelLayout.createSequentialGroup() - .addComponent(newKeywordsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteWordButton) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - addKeywordPanelLayout.setVerticalGroup( - addKeywordPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(addKeywordPanelLayout.createSequentialGroup() - .addGap(0, 0, 0) - .addGroup(addKeywordPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(deleteWordButton) - .addComponent(newKeywordsButton)) - .addGap(72, 72, 72)) - ); - - ingestMessagesCheckbox.setSelected(true); - ingestMessagesCheckbox.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.ingestMessagesCheckbox.text")); // NOI18N - ingestMessagesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.ingestMessagesCheckbox.toolTipText")); // NOI18N - ingestMessagesCheckbox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - ingestMessagesCheckboxActionPerformed(evt); - } - }); - - keywordsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.keywordsLabel.text")); // NOI18N - - keywordOptionsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.keywordOptionsLabel.text")); // NOI18N - - listOptionsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.listOptionsLabel.text")); // NOI18N - - deleteListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N - deleteListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteListButton.text")); // NOI18N - deleteListButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - deleteListButtonActionPerformed(evt); - } - }); - - saveListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/save16.png"))); // NOI18N - saveListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.saveListButton.text")); // NOI18N - - exportButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/export16.png"))); // NOI18N - exportButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.exportButton.text")); // NOI18N - exportButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - exportButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout listEditorPanelLayout = new javax.swing.GroupLayout(listEditorPanel); - listEditorPanel.setLayout(listEditorPanelLayout); - listEditorPanelLayout.setHorizontalGroup( - listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(addKeywordPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addComponent(listOptionsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(listOptionsSeparator)) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addComponent(keywordOptionsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(keywordOptionsSeparator)) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(keywordsLabel) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(ingestMessagesCheckbox) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addComponent(exportButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(saveListButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(deleteListButton))))) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()))) - ); - listEditorPanelLayout.setVerticalGroup( - listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, listEditorPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(keywordsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE) - .addGap(10, 10, 10) - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(keywordOptionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 7, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(keywordOptionsLabel)) - .addGap(7, 7, 7) - .addComponent(addKeywordPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(listOptionsLabel)) - .addGroup(listEditorPanelLayout.createSequentialGroup() - .addGap(123, 123, 123) - .addComponent(listOptionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 6, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(ingestMessagesCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(exportButton) - .addComponent(saveListButton) - .addComponent(deleteListButton)) - .addContainerGap()) - ); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(listEditorPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(listEditorPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(5, 5, 5)) - ); - }// </editor-fold>//GEN-END:initComponents - - private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed - if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.removeKwMsg"), NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.deleteWordButtonActionPerformed.delConfirmMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) { - - tableModel.deleteSelected(keywordTable.getSelectedRows()); - XmlKeywordSearchList.getCurrent().addList(currentKeywordList); - setButtonStates(); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - } - }//GEN-LAST:event_deleteWordButtonActionPerformed - - private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed - - final String FEATURE_NAME = NbBundle.getMessage(this.getClass(), - "KeywordSearchEditListPanel.exportButtonAction.featureName.text"); - - JFileChooser chooser = new JFileChooser(); - final String EXTENSION = "xml"; //NON-NLS - FileNameExtensionFilter filter = new FileNameExtensionFilter( - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.fileFilterLabel"), EXTENSION); - chooser.setFileFilter(filter); - chooser.setSelectedFile(new File(currentKeywordList.getName())); - chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - - int returnVal = chooser.showSaveDialog(this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File selFile = chooser.getSelectedFile(); - if (selFile == null) { - return; - } - - //force append extension if not given - String fileAbs = selFile.getAbsolutePath(); - if (!fileAbs.endsWith("." + EXTENSION)) { - fileAbs = fileAbs + "." + EXTENSION; - selFile = new File(fileAbs); - } - - boolean shouldWrite = true; - if (selFile.exists()) { - shouldWrite = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.fileExistPrompt", - selFile.getName()), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); - } - if (!shouldWrite) { - return; - } - - XmlKeywordSearchList reader = XmlKeywordSearchList.getCurrent(); - - List<KeywordList> toWrite = new ArrayList<>(); - toWrite.add(reader.getList(currentKeywordList.getName())); - final XmlKeywordSearchList exporter = new XmlKeywordSearchList(fileAbs); - boolean written = exporter.saveLists(toWrite); - if (written) { - KeywordSearchUtil.displayDialog(FEATURE_NAME, - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.kwListExportedMsg"), - KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); - } - } - }//GEN-LAST:event_exportButtonActionPerformed - - private void ingestMessagesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ingestMessagesCheckboxActionPerformed - currentKeywordList.setIngestMessages(ingestMessagesCheckbox.isSelected()); - XmlKeywordSearchList updater = XmlKeywordSearchList.getCurrent(); - updater.addList(currentKeywordList); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_ingestMessagesCheckboxActionPerformed - - private void deleteListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteListButtonActionPerformed - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_deleteListButtonActionPerformed - - private void newKeywordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newKeywordsButtonActionPerformed - String keywordsToRedisplay = ""; + private boolean addKeywordsAction(String existingKeywords, boolean isLiteral, boolean isWholeWord) { + String keywordsToRedisplay = existingKeywords; AddKeywordsDialog dialog = new AddKeywordsDialog(); - - int goodCount; - int dupeCount; + + int goodCount = 0; + int dupeCount = 0; int badCount = 1; // Default to 1 so we enter the loop the first time - while(badCount > 0){ - dialog.setInitialKeywordList(keywordsToRedisplay); + while (badCount > 0) { + dialog.setInitialKeywordList(keywordsToRedisplay, isLiteral, isWholeWord); dialog.display(); - + goodCount = 0; dupeCount = 0; badCount = 0; keywordsToRedisplay = ""; - - if(!dialog.getKeywords().isEmpty()){ + if (!dialog.getKeywords().isEmpty()) { - for(String newWord:dialog.getKeywords()){ + for (String newWord : dialog.getKeywords()) { if (newWord.isEmpty()) { continue; } @@ -459,30 +185,30 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis XmlKeywordSearchList.getCurrent().addList(currentKeywordList); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - if((badCount > 0) || (dupeCount > 0)){ + if ((badCount > 0) || (dupeCount > 0)) { // Display the error counts to the user // The add keywords dialog will pop up again if any were invalid with any // invalid entries (valid entries and dupes will disappear) - + String summary = ""; KeywordSearchUtil.DIALOG_MESSAGE_TYPE level = KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO; - if(goodCount > 0){ - if(goodCount > 1){ + if (goodCount > 0) { + if (goodCount > 1) { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordsAddedPlural.text", goodCount) + "\n"; } else { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordsAdded.text", goodCount) + "\n"; } } - if(dupeCount > 0){ - if(dupeCount > 1){ + if (dupeCount > 0) { + if (dupeCount > 1) { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordDupesSkippedPlural.text", dupeCount) + "\n"; } else { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordDupesSkipped.text", dupeCount) + "\n"; } level = KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN; } - if(badCount > 0){ - if(badCount > 1){ + if (badCount > 0) { + if (badCount > 1) { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordErrorsPlural.text", badCount) + "\n"; } else { summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordErrors.text", badCount) + "\n"; @@ -496,24 +222,185 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis } setFocusOnKeywordTextBox(); setButtonStates(); + return (goodCount >= 1 && dupeCount == 0); + } + + /** + * Remove one or more keywords from a keyword list. + * + * @param selectedKeywords the indices of the keywords you would like to delete + */ + private void deleteKeywordAction(int[] selectedKeywords) { + tableModel.deleteSelected(selectedKeywords); + XmlKeywordSearchList.getCurrent().addList(currentKeywordList); + setButtonStates(); + } + + /** + * 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() { + + listEditorPanel = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + keywordTable = new javax.swing.JTable(); + ingestMessagesCheckbox = new javax.swing.JCheckBox(); + keywordsLabel = new javax.swing.JLabel(); + newKeywordsButton = new javax.swing.JButton(); + deleteWordButton = new javax.swing.JButton(); + editWordButton = new javax.swing.JButton(); + + setMinimumSize(new java.awt.Dimension(0, 0)); + + listEditorPanel.setMinimumSize(new java.awt.Dimension(0, 0)); + + jScrollPane1.setPreferredSize(new java.awt.Dimension(340, 300)); + + keywordTable.setModel(tableModel); + keywordTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); + keywordTable.setGridColor(new java.awt.Color(153, 153, 153)); + keywordTable.setMaximumSize(new java.awt.Dimension(30000, 30000)); + keywordTable.getTableHeader().setReorderingAllowed(false); + jScrollPane1.setViewportView(keywordTable); + + ingestMessagesCheckbox.setSelected(true); + ingestMessagesCheckbox.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.ingestMessagesCheckbox.text")); // NOI18N + ingestMessagesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.ingestMessagesCheckbox.toolTipText")); // NOI18N + ingestMessagesCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + ingestMessagesCheckboxActionPerformed(evt); + } + }); + + keywordsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.keywordsLabel.text")); // NOI18N + + newKeywordsButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N + newKeywordsButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.newKeywordsButton.text")); // NOI18N + newKeywordsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newKeywordsButtonActionPerformed(evt); + } + }); + + deleteWordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N + deleteWordButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteWordButton.text")); // NOI18N + deleteWordButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteWordButtonActionPerformed(evt); + } + }); + + editWordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/edit16.png"))); // NOI18N + editWordButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.editWordButton.text")); // NOI18N + editWordButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + editWordButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout listEditorPanelLayout = new javax.swing.GroupLayout(listEditorPanel); + listEditorPanel.setLayout(listEditorPanelLayout); + listEditorPanelLayout.setHorizontalGroup( + listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(listEditorPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(listEditorPanelLayout.createSequentialGroup() + .addComponent(keywordsLabel) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(listEditorPanelLayout.createSequentialGroup() + .addGap(10, 10, 10) + .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) + .addGroup(listEditorPanelLayout.createSequentialGroup() + .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(ingestMessagesCheckbox) + .addGroup(listEditorPanelLayout.createSequentialGroup() + .addComponent(newKeywordsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(14, 14, 14) + .addComponent(editWordButton, javax.swing.GroupLayout.PREFERRED_SIZE, 132, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(14, 14, 14) + .addComponent(deleteWordButton, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(0, 0, Short.MAX_VALUE))))) + .addContainerGap()) + ); + + listEditorPanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteWordButton, editWordButton, newKeywordsButton}); + + listEditorPanelLayout.setVerticalGroup( + listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, listEditorPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(keywordsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 257, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(listEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(deleteWordButton) + .addComponent(newKeywordsButton) + .addComponent(editWordButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(ingestMessagesCheckbox) + .addGap(9, 9, 9)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(listEditorPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(listEditorPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(5, 5, 5)) + ); + }// </editor-fold>//GEN-END:initComponents + + private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed + if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.removeKwMsg"), + NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.deleteWordButtonActionPerformed.delConfirmMsg"), + KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) { + deleteKeywordAction(keywordTable.getSelectedRows()); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_deleteWordButtonActionPerformed + + private void ingestMessagesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ingestMessagesCheckboxActionPerformed + currentKeywordList.setIngestMessages(ingestMessagesCheckbox.isSelected()); + XmlKeywordSearchList updater = XmlKeywordSearchList.getCurrent(); + updater.addList(currentKeywordList); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_ingestMessagesCheckboxActionPerformed + + private void newKeywordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newKeywordsButtonActionPerformed + addKeywordsAction("", true, true); }//GEN-LAST:event_newKeywordsButtonActionPerformed + private void editWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editWordButtonActionPerformed + int[] selectedKeywords = keywordTable.getSelectedRows(); + if (selectedKeywords.length == 1) { + Keyword currentKeyword = currentKeywordList.getKeywords().get(selectedKeywords[0]); + if (addKeywordsAction(currentKeyword.getSearchTerm(), currentKeyword.searchTermIsLiteral(), currentKeyword.searchTermIsWholeWord())) { + deleteKeywordAction(selectedKeywords); + } + } + }//GEN-LAST:event_editWordButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JPanel addKeywordPanel; - private javax.swing.JButton deleteListButton; private javax.swing.JButton deleteWordButton; - private javax.swing.JButton exportButton; + private javax.swing.JButton editWordButton; private javax.swing.JCheckBox ingestMessagesCheckbox; private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JLabel keywordOptionsLabel; - private javax.swing.JSeparator keywordOptionsSeparator; private javax.swing.JTable keywordTable; private javax.swing.JLabel keywordsLabel; private javax.swing.JPanel listEditorPanel; - private javax.swing.JLabel listOptionsLabel; - private javax.swing.JSeparator listOptionsSeparator; private javax.swing.JButton newKeywordsButton; - private javax.swing.JButton saveListButton; // End of variables declaration//GEN-END:variables @Override @@ -554,14 +441,6 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis currentKeywordList = list; } - void addDeleteButtonActionPerformed(ActionListener l) { - deleteListButton.addActionListener(l); - } - - void addSaveButtonActionPerformed(ActionListener l) { - saveListButton.addActionListener(l); - } - private class KeywordTableModel extends AbstractTableModel { @Override diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.form index 4ba5f92a52..ba8a541eba 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.form @@ -41,13 +41,18 @@ <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Border info="null"/> </Property> - <Property name="dividerLocation" type="int" value="275"/> + <Property name="dividerLocation" type="int" value="300"/> <Property name="dividerSize" type="int" value="1"/> </Properties> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <SubComponents> <Container class="javax.swing.JPanel" name="leftPanel"> + <Properties> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[287, 327]"/> + </Property> + </Properties> <Constraints> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <JSplitPaneConstraints position="left"/> @@ -57,7 +62,7 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="275" max="32767" attributes="0"/> + <EmptySpace min="0" pref="300" max="32767" attributes="0"/> </Group> </DimensionLayout> <DimensionLayout dim="1"> @@ -82,7 +87,7 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="397" max="32767" attributes="0"/> + <EmptySpace min="0" pref="372" max="32767" attributes="0"/> </Group> </DimensionLayout> <DimensionLayout dim="1"> diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java index bac57fc76d..9be40c184a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.keywordsearch; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; +import java.util.ArrayList; import java.util.List; import javax.swing.JOptionPane; import org.openide.util.NbBundle; @@ -41,68 +42,32 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option private void customizeComponents() { listsManagementPanel.addListSelectionListener(editListPanel); - editListPanel.addDeleteButtonActionPerformed(new ActionListener() { + listsManagementPanel.addDeleteButtonActionPerformed(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) { String toDelete = editListPanel.getCurrentKeywordList().getName(); - editListPanel.setCurrentKeywordList(null); - editListPanel.setButtonStates(); - XmlKeywordSearchList deleter = XmlKeywordSearchList.getCurrent(); - deleter.deleteList(toDelete); + deleteAction(toDelete); listsManagementPanel.resync(); } } }); - editListPanel.addSaveButtonActionPerformed(new ActionListener() { + listsManagementPanel.addRenameButtonActionPerformed(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - final String FEATURE_NAME = NbBundle.getMessage(this.getClass(), - "KeywordSearchGlobalListSettingsPanel.component.featureName.text"); - KeywordList currentKeywordList = editListPanel.getCurrentKeywordList(); - - List<Keyword> keywords = currentKeywordList.getKeywords(); - if (keywords.isEmpty()) { - KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.keywordListEmptyErr"), - KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); - return; - } - - String listName = (String) JOptionPane.showInputDialog( - null, - NbBundle.getMessage(this.getClass(), "KeywordSearch.newKwListTitle"), - FEATURE_NAME, - JOptionPane.PLAIN_MESSAGE, - null, - null, - currentKeywordList.getName()); - if (listName == null || listName.trim().equals("")) { - return; - } - - XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent(); - if (writer.listExists(listName) && writer.getList(listName).isEditable()) { - KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); - return; - } - boolean shouldAdd = false; - if (writer.listExists(listName)) { - boolean replace = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.kwListExistMsg", listName), - KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); - if (replace) { - shouldAdd = true; - } - - } else { - shouldAdd = true; - } - - if (shouldAdd) { - writer.addList(listName, keywords); - KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.kwListSavedMsg", listName), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); + String toDelete = editListPanel.getCurrentKeywordList().getName(); + if (copyAction()) { + deleteAction(toDelete); + listsManagementPanel.resync(); } + } + }); + listsManagementPanel.addCopyButtonActionPerformed(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + copyAction(); listsManagementPanel.resync(); } }); @@ -113,6 +78,69 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option mainSplitPane.repaint(); } + /** + * Delete the specified Keyword List + * + * @param toDelete - the list to delete + */ + private void deleteAction(String toDelete) { + XmlKeywordSearchList deleter = XmlKeywordSearchList.getCurrent(); + deleter.deleteList(toDelete); + editListPanel.setCurrentKeywordList(null); + editListPanel.setButtonStates(); + listsManagementPanel.setButtonStates(); + } + + /** + * Duplicates the selected keyword list, returns whether or not the keyword + * list was duplicated. + * + * @return true or false + */ + private boolean copyAction() { + boolean shouldAdd = false; + final String FEATURE_NAME = NbBundle.getMessage(this.getClass(), + "KeywordSearchGlobalListSettingsPanel.component.featureName.text"); + KeywordList currentKeywordList = editListPanel.getCurrentKeywordList(); + + List<Keyword> keywords = new ArrayList<>(); + keywords.addAll(currentKeywordList.getKeywords()); + + String listName = (String) JOptionPane.showInputDialog( + null, + NbBundle.getMessage(this.getClass(), "KeywordSearch.newKwListTitle"), + FEATURE_NAME, + JOptionPane.PLAIN_MESSAGE, + null, + null, + currentKeywordList.getName()); + if (listName == null || listName.trim().equals("")) { + return shouldAdd; + } + + XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent(); + if (writer.listExists(listName) && writer.getList(listName).isEditable()) { + KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); + return shouldAdd; + } + if (writer.listExists(listName)) { + boolean replace = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.kwListExistMsg", listName), + KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); + if (replace) { + shouldAdd = true; + } + + } else { + shouldAdd = true; + } + + if (shouldAdd) { + writer.addList(listName, keywords); + KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.kwListSavedMsg", listName), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); + } + return shouldAdd; + } + @Override public void addPropertyChangeListener(PropertyChangeListener l) { listsManagementPanel.addPropertyChangeListener(l); @@ -159,14 +187,16 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option rightPanel = new javax.swing.JPanel(); mainSplitPane.setBorder(null); - mainSplitPane.setDividerLocation(275); + mainSplitPane.setDividerLocation(300); mainSplitPane.setDividerSize(1); + leftPanel.setPreferredSize(new java.awt.Dimension(287, 327)); + javax.swing.GroupLayout leftPanelLayout = new javax.swing.GroupLayout(leftPanel); leftPanel.setLayout(leftPanelLayout); leftPanelLayout.setHorizontalGroup( leftPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 275, Short.MAX_VALUE) + .addGap(0, 300, Short.MAX_VALUE) ); leftPanelLayout.setVerticalGroup( leftPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -181,7 +211,7 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option rightPanel.setLayout(rightPanelLayout); rightPanelLayout.setHorizontalGroup( rightPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 397, Short.MAX_VALUE) + .addGap(0, 372, Short.MAX_VALUE) ); rightPanelLayout.setVerticalGroup( rightPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.form index 3bbbe3f390..b86ed01a12 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.form @@ -21,20 +21,32 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> + <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jScrollPane1" alignment="1" pref="0" max="32767" attributes="1"/> - <Group type="102" attributes="0"> + <Component id="jScrollPane1" pref="0" max="32767" attributes="1"/> + <Component id="keywordListsLabel" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="keywordListsLabel" min="-2" max="-2" attributes="0"/> - <Group type="102" alignment="0" attributes="0"> - <Component id="newListButton" min="-2" pref="114" max="-2" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="importButton" min="-2" pref="126" max="-2" attributes="0"/> + <Component id="newListButton" linkSize="4" min="-2" pref="85" max="-2" attributes="0"/> + <Component id="deleteListButton" linkSize="4" alignment="0" pref="0" max="32767" attributes="0"/> + </Group> + <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Component id="renameListButton" linkSize="4" pref="75" max="32767" attributes="0"/> + <Component id="importButton" linkSize="4" max="32767" attributes="0"/> + </Group> + <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" attributes="0"> + <Component id="copyListButton" linkSize="4" pref="0" max="32767" attributes="0"/> + <EmptySpace min="0" pref="1" max="32767" attributes="0"/> + </Group> + <Group type="102" alignment="1" attributes="0"> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + <Component id="exportButton" linkSize="4" min="-2" pref="79" max="-2" attributes="0"/> </Group> </Group> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> </Group> </Group> <EmptySpace max="-2" attributes="0"/> @@ -44,14 +56,21 @@ <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> - <EmptySpace min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> <Component id="keywordListsLabel" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/> - <Component id="jScrollPane1" pref="355" max="32767" attributes="0"/> - <EmptySpace min="-2" pref="5" max="-2" attributes="0"/> + <Component id="jScrollPane1" pref="316" max="32767" attributes="0"/> + <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> - <Component id="newListButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="importButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="newListButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="renameListButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="copyListButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace min="-2" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="importButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="exportButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="deleteListButton" linkSize="5" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace min="-2" max="-2" attributes="0"/> </Group> @@ -94,6 +113,10 @@ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.newListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newListButtonActionPerformed"/> @@ -107,6 +130,10 @@ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.importButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="importButtonActionPerformed"/> @@ -119,5 +146,73 @@ </Property> </Properties> </Component> + <Component class="javax.swing.JButton" name="exportButton"> + <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/export16.png"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.exportButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportButtonActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JButton" name="copyListButton"> + <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/add16.png"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.copyListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="copyListButtonActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JButton" name="deleteListButton"> + <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/delete16.png"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.deleteListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteListButtonActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JButton" name="renameListButton"> + <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/edit16.png"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="GlobalListsManagementPanel.renameListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="iconTextGap" type="int" value="2"/> + <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> + <Insets value="[2, 2, 2, 2]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="renameListButtonActionPerformed"/> + </Events> + </Component> </SubComponents> </Form> diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java index abc9a0bebd..730bfc1170 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2016 Basis Technology Corp. + * + * Copyright 2011-2017 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. @@ -18,7 +18,11 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.awt.EventQueue; +import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -31,7 +35,7 @@ import javax.swing.table.AbstractTableModel; import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; -import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; /** * A panel to manage all keyword lists created/imported in Autopsy. @@ -39,8 +43,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPanel { private static final long serialVersionUID = 1L; - - private final Logger logger = Logger.getLogger(GlobalListsManagementPanel.class.getName()); private final KeywordListTableModel tableModel; private final org.sleuthkit.autopsy.keywordsearch.GlobalListSettingsPanel globalListSettingsPanel; @@ -56,122 +58,61 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa listsTable.setTableHeader(null); listsTable.setShowHorizontalLines(false); listsTable.setShowVerticalLines(false); - + exportButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.exportToFile")); + copyListButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip")); listsTable.getParent().setBackground(listsTable.getBackground()); listsTable.setCellSelectionEnabled(false); listsTable.setRowSelectionAllowed(true); tableModel.resync(); + setButtonStates(); listsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { globalListSettingsPanel.setFocusOnKeywordTextBox(); + setButtonStates(); } }); - /* - * XmlKeywordSearchList.getCurrent().addPropertyChangeListener(new - * PropertyChangeListener() { - * - * @Override public void propertyChange(PropertyChangeEvent evt) { if - * (evt.getPropertyName().equals(XmlKeywordSearchList.ListsEvt.LIST_ADDED.toString())) - * { tableModel.resync(); for(int i = 0; i<listsTable.getRowCount(); - * i++) { String name = (String) listsTable.getValueAt(i, 0); - * if(((String) evt.getNewValue()).equals(name)) { - * listsTable.getSelectionModel().setSelectionInterval(i, i); } } } else - * if - * (evt.getPropertyName().equals(XmlKeywordSearchList.ListsEvt.LIST_DELETED.toString())) - * { tableModel.resync(); if(listsTable.getRowCount() > 0) { - * listsTable.getSelectionModel().setSelectionInterval(0, 0); } else { - * listsTable.getSelectionModel().clearSelection(); } } } }); - */ + + IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Object source = evt.getSource(); + if (source instanceof String && ((String) source).equals("LOCAL")) { //NON-NLS + EventQueue.invokeLater(() -> { + globalListSettingsPanel.setFocusOnKeywordTextBox(); + setButtonStates(); + }); + } + } + }); + } + + void addDeleteButtonActionPerformed(ActionListener l) { + deleteListButton.addActionListener(l); + } + + void addRenameButtonActionPerformed(ActionListener l) { + renameListButton.addActionListener(l); + } + + void addCopyButtonActionPerformed(ActionListener l) { + copyListButton.addActionListener(l); } /** - * 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. + * Opens the dialogue for creating a new keyword list and adds it to the table. */ - @SuppressWarnings("unchecked") - // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - listsTable = new javax.swing.JTable(); - newListButton = new javax.swing.JButton(); - importButton = new javax.swing.JButton(); - keywordListsLabel = new javax.swing.JLabel(); - - setMinimumSize(new java.awt.Dimension(250, 0)); - - listsTable.setModel(tableModel); - listsTable.setMaximumSize(new java.awt.Dimension(30000, 30000)); - listsTable.setShowHorizontalLines(false); - listsTable.setShowVerticalLines(false); - listsTable.getTableHeader().setReorderingAllowed(false); - listsTable.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyPressed(java.awt.event.KeyEvent evt) { - listsTableKeyPressed(evt); - } - }); - jScrollPane1.setViewportView(listsTable); - - newListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N - newListButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.newListButton.text")); // NOI18N - newListButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - newListButtonActionPerformed(evt); - } - }); - - importButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/import16.png"))); // NOI18N - importButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.importButton.text")); // NOI18N - importButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - importButtonActionPerformed(evt); - } - }); - - keywordListsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.keywordListsLabel.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(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(keywordListsLabel) - .addGroup(layout.createSequentialGroup() - .addComponent(newListButton, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(importButton, javax.swing.GroupLayout.PREFERRED_SIZE, 126, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(keywordListsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 355, Short.MAX_VALUE) - .addGap(5, 5, 5) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newListButton) - .addComponent(importButton)) - .addContainerGap()) - ); - }// </editor-fold>//GEN-END:initComponents - - private void newListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newListButtonActionPerformed + private void newKeywordListAction() { XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent(); - String listName = (String) JOptionPane.showInputDialog(null, NbBundle.getMessage(this.getClass(), "KeywordSearch.newKwListTitle"), - NbBundle.getMessage(this.getClass(), "KeywordSearch.newKeywordListMsg"), JOptionPane.PLAIN_MESSAGE, null, null, ""); - if (listName == null || listName.trim().equals("")) { + String listName = ""; + + + listName = (String) JOptionPane.showInputDialog(null, NbBundle.getMessage(this.getClass(), "KeywordSearch.newKwListTitle"), + NbBundle.getMessage(this.getClass(), "KeywordSearch.newKeywordListMsg"), JOptionPane.PLAIN_MESSAGE, null, null, listName); + + if (listName == null || listName.trim().isEmpty()) { return; } boolean shouldAdd = false; @@ -197,8 +138,9 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa shouldAdd = true; } if (shouldAdd) { - writer.addList(listName, new ArrayList<Keyword>()); + writer.addList(listName, new ArrayList<>()); } + tableModel.resync(); //This loop selects the recently ADDED keywordslist in the JTable @@ -207,6 +149,178 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa listsTable.getSelectionModel().addSelectionInterval(i, i); } } + } + + /** + * Enables and disables buttons on this panel based on the current state. + */ + void setButtonStates() { + boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); + boolean isListSelected = !listsTable.getSelectionModel().isSelectionEmpty(); + boolean canEditList = isListSelected && !isIngestRunning; + // items that only need ingest to not be running + importButton.setEnabled(!isIngestRunning); + + // items that need an unlocked list w/out ingest running + deleteListButton.setEnabled(canEditList); + renameListButton.setEnabled(canEditList); + + // items that only need a selected list + copyListButton.setEnabled(isListSelected); + exportButton.setEnabled(isListSelected); + } + + /** + * 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() { + + jScrollPane1 = new javax.swing.JScrollPane(); + listsTable = new javax.swing.JTable(); + newListButton = new javax.swing.JButton(); + importButton = new javax.swing.JButton(); + keywordListsLabel = new javax.swing.JLabel(); + exportButton = new javax.swing.JButton(); + copyListButton = new javax.swing.JButton(); + deleteListButton = new javax.swing.JButton(); + renameListButton = new javax.swing.JButton(); + + setMinimumSize(new java.awt.Dimension(250, 0)); + + listsTable.setModel(tableModel); + listsTable.setMaximumSize(new java.awt.Dimension(30000, 30000)); + listsTable.setShowHorizontalLines(false); + listsTable.setShowVerticalLines(false); + listsTable.getTableHeader().setReorderingAllowed(false); + listsTable.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyPressed(java.awt.event.KeyEvent evt) { + listsTableKeyPressed(evt); + } + }); + jScrollPane1.setViewportView(listsTable); + + newListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N + newListButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.newListButton.text")); // NOI18N + newListButton.setIconTextGap(2); + newListButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + newListButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newListButtonActionPerformed(evt); + } + }); + + importButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/import16.png"))); // NOI18N + importButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.importButton.text")); // NOI18N + importButton.setIconTextGap(2); + importButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + importButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + importButtonActionPerformed(evt); + } + }); + + keywordListsLabel.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.keywordListsLabel.text")); // NOI18N + + exportButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/export16.png"))); // NOI18N + exportButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.exportButton.text")); // NOI18N + exportButton.setIconTextGap(2); + exportButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + exportButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exportButtonActionPerformed(evt); + } + }); + + copyListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/add16.png"))); // NOI18N + copyListButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.copyListButton.text")); // NOI18N + copyListButton.setIconTextGap(2); + copyListButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + copyListButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + copyListButtonActionPerformed(evt); + } + }); + + deleteListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N + deleteListButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.deleteListButton.text")); // NOI18N + deleteListButton.setIconTextGap(2); + deleteListButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + deleteListButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteListButtonActionPerformed(evt); + } + }); + + renameListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/edit16.png"))); // NOI18N + renameListButton.setText(org.openide.util.NbBundle.getMessage(GlobalListsManagementPanel.class, "GlobalListsManagementPanel.renameListButton.text")); // NOI18N + renameListButton.setIconTextGap(2); + renameListButton.setMargin(new java.awt.Insets(2, 2, 2, 2)); + renameListButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + renameListButtonActionPerformed(evt); + } + }); + + 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(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addComponent(keywordListsLabel) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(newListButton, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(deleteListButton, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)) + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(renameListButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, Short.MAX_VALUE) + .addComponent(importButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(copyListButton, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE) + .addGap(0, 1, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(exportButton, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE))))) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copyListButton, deleteListButton, exportButton, importButton, newListButton, renameListButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(keywordListsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 316, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(newListButton) + .addComponent(renameListButton) + .addComponent(copyListButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(importButton) + .addComponent(exportButton) + .addComponent(deleteListButton)) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {copyListButton, deleteListButton, exportButton, importButton, newListButton, renameListButton}); + + }// </editor-fold>//GEN-END:initComponents + + private void newListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newListButtonActionPerformed + newKeywordListAction(); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); globalListSettingsPanel.setFocusOnKeywordTextBox(); }//GEN-LAST:event_newListButtonActionPerformed @@ -251,7 +365,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa } List<KeywordList> toImport = reader.getListsL(); - List<KeywordList> toImportConfirmed = new ArrayList<KeywordList>(); + List<KeywordList> toImportConfirmed = new ArrayList<>(); final XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent(); @@ -321,12 +435,81 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa tableModel.resync(); }//GEN-LAST:event_listsTableKeyPressed + private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed + + final String FEATURE_NAME = NbBundle.getMessage(this.getClass(), + "KeywordSearchEditListPanel.exportButtonAction.featureName.text"); + + JFileChooser chooser = new JFileChooser(); + final String EXTENSION = "xml"; //NON-NLS + FileNameExtensionFilter filter = new FileNameExtensionFilter( + NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.fileFilterLabel"), EXTENSION); + chooser.setFileFilter(filter); + String listName = listsTable.getValueAt(listsTable.getSelectedRow(), 0).toString(); + + chooser.setSelectedFile(new File(listName)); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int returnVal = chooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File selFile = chooser.getSelectedFile(); + if (selFile == null) { + return; + } + + //force append extension if not given + String fileAbs = selFile.getAbsolutePath(); + if (!fileAbs.endsWith("." + EXTENSION)) { + fileAbs = fileAbs + "." + EXTENSION; + selFile = new File(fileAbs); + } + + boolean shouldWrite = true; + if (selFile.exists()) { + shouldWrite = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, + NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.fileExistPrompt", + selFile.getName()), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); + } + if (!shouldWrite) { + return; + } + + XmlKeywordSearchList reader = XmlKeywordSearchList.getCurrent(); + + List<KeywordList> toWrite = new ArrayList<>(); + toWrite.add(reader.getList(listName)); + final XmlKeywordSearchList exporter = new XmlKeywordSearchList(fileAbs); + boolean written = exporter.saveLists(toWrite); + if (written) { + KeywordSearchUtil.displayDialog(FEATURE_NAME, + NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.exportButtonActionPerformed.kwListExportedMsg"), + KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); + } + } + }//GEN-LAST:event_exportButtonActionPerformed + + private void copyListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyListButtonActionPerformed + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_copyListButtonActionPerformed + + private void deleteListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteListButtonActionPerformed + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_deleteListButtonActionPerformed + + private void renameListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_renameListButtonActionPerformed + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_renameListButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton copyListButton; + private javax.swing.JButton deleteListButton; + private javax.swing.JButton exportButton; private javax.swing.JButton importButton; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel keywordListsLabel; private javax.swing.JTable listsTable; private javax.swing.JButton newListButton; + private javax.swing.JButton renameListButton; // End of variables declaration//GEN-END:variables @Override @@ -345,6 +528,8 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa private class KeywordListTableModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; + private final XmlKeywordSearchList listsHandle = XmlKeywordSearchList.getCurrent(); @Override diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java index 83f2179132..9aa8b74a4f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.concurrent.GuardedBy; @@ -244,7 +243,6 @@ class HighlightedText implements IndexedText { @Override public boolean hasNextPage() { return getIndexOfCurrentPage() < pages.size() - 1; - } @Override @@ -255,7 +253,7 @@ class HighlightedText implements IndexedText { @Override public int nextPage() { if (hasNextPage()) { - currentPage = Iterators.get(pages.iterator(),getIndexOfCurrentPage() + 1); + currentPage = Iterators.get(pages.iterator(), getIndexOfCurrentPage() + 1); return currentPage; } else { throw new IllegalStateException("No next page."); @@ -265,7 +263,7 @@ class HighlightedText implements IndexedText { @Override public int previousPage() { if (hasPreviousPage()) { - currentPage = Iterators.get(pages.iterator(),getIndexOfCurrentPage()-1); + currentPage = Iterators.get(pages.iterator(), getIndexOfCurrentPage() - 1); return currentPage; } else { throw new IllegalStateException("No previous page."); @@ -387,7 +385,7 @@ class HighlightedText implements IndexedText { highlightedContent = insertAnchors(highlightedContent); return "<html><pre>" + highlightedContent + "</pre></html>"; //NON-NLS - } catch (Exception ex) { + } catch (TskCoreException | KeywordSearchModuleException | NoOpenCoreException ex) { logger.log(Level.SEVERE, "Error getting highlighted text for " + objectId, ex); //NON-NLS return NbBundle.getMessage(this.getClass(), "HighlightedMatchesSource.getMarkup.queryFailedMsg"); } @@ -453,8 +451,8 @@ class HighlightedText implements IndexedText { //we also need to escape the keyword so that it matches the escpared text final String escapedKeyword = StringEscapeUtils.escapeHtml(keyword); int textOffset = 0; - int hitOffset; - while ((hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset)) != -1) { + int hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset); + while (hitOffset != -1) { // Append the portion of text up to (but not including) the hit. highlightedText.append(text.substring(textOffset, hitOffset)); // Add in the highlighting around the keyword. @@ -464,13 +462,15 @@ class HighlightedText implements IndexedText { // Advance the text offset past the keyword. textOffset = hitOffset + escapedKeyword.length(); + + hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset); } // Append the remainder of text field highlightedText.append(text.substring(textOffset, text.length())); - + if (highlightedText.length() == 0) { return NbBundle.getMessage(HighlightedText.class, "HighlightedMatchesSource.getMarkup.noMatchMsg"); - } + } //reset for next pass text = highlightedText.toString(); highlightedText = new StringBuilder(""); @@ -487,22 +487,21 @@ class HighlightedText implements IndexedText { * @return */ private String insertAnchors(String searchableContent) { - int searchOffset = 0; - int index = -1; - StringBuilder buf = new StringBuilder(searchableContent); - final String searchToken = HIGHLIGHT_PRE; final int indexSearchTokLen = searchToken.length(); final String insertPre = "<a name='" + ANCHOR_PREFIX; //NON-NLS final String insertPost = "'></a>"; //NON-NLS int count = 0; - while ((index = buf.indexOf(searchToken, searchOffset)) >= 0) { + int searchOffset = 0; + int index = buf.indexOf(searchToken, searchOffset); + while (index >= 0) { String insertString = insertPre + Integer.toString(count + 1) + insertPost; int insertStringLen = insertString.length(); buf.insert(index, insertString); searchOffset = index + indexSearchTokLen + insertStringLen; //next offset past this anchor ++count; + index = buf.indexOf(searchToken, searchOffset); } //store total hits for this page, now that we know it diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java index 2dae510a19..d1345dfa1d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java @@ -20,11 +20,13 @@ package org.sleuthkit.autopsy.keywordsearch; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.math.NumberUtils; import org.sleuthkit.autopsy.casemodule.Case; @@ -99,10 +101,10 @@ class IndexFinder { } /** - * Creates a copy of an existing Solr index. + * Creates a copy of an existing Solr index. * - * @param indexToUpgrade Index object to create a copy of - * @param context AutopsyService.CaseContext object + * @param indexToUpgrade Index object to create a copy of + * @param context AutopsyService.CaseContext object * * @return The absolute path of the new Solr index directory * @@ -120,20 +122,31 @@ class IndexFinder { List<File> contents = getAllContentsInFolder(targetDirPath.getAbsolutePath()); if (!contents.isEmpty()) { // target directory is not empty - throw new AutopsyService.AutopsyServiceException("Directory to store the upgraded index must be empty " + targetDirPath.getAbsolutePath()); + try { + FileUtils.deleteDirectory(targetDirPath.getParentFile()); //We don't want partial copies + } catch (IOException | IllegalArgumentException deleteEx) { + throw new AutopsyService.AutopsyServiceException("Failed to delete existing directory to store the upgraded index " + targetDirPath.getAbsolutePath() + "which was not empty", deleteEx); + } + logger.log(Level.WARNING, String.format("Directory %s existed with contents and was deleted so the upgrade could proceed", indexFolderName)); } } targetDirPath.mkdirs(); FileUtils.copyDirectory(new File(indexToUpgrade.getIndexPath()), targetDirPath); return targetDirPath.getAbsolutePath(); } catch (AutopsyService.AutopsyServiceException | IOException ex) { + try { + Path targetDirPath = Paths.get(context.getCase().getModuleDirectory(), KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, indexFolderName); //NON-NLS + FileUtils.deleteDirectory(targetDirPath.toFile()); //We don't want partial copies + } catch (IOException | IllegalArgumentException deleteEx) { + logger.log(Level.SEVERE, String.format("Failed to delete %s when upgrade cancelled", indexFolderName), deleteEx); + } throw new AutopsyService.AutopsyServiceException("Error occurred while creating a copy of keyword search index", ex); } } /** - * Find existing Solr 4 Schema 1.8 index directory location for the case. This is done via - * subdirectory search of all existing + * Find existing Solr 4 Schema 1.8 index directory location for the case. + * This is done via subdirectory search of all existing * "ModuleOutput/node_name/keywordsearch/data/" folders. * * @param theCase the case to get index dir for @@ -146,8 +159,9 @@ class IndexFinder { // multi user cases contain a subfolder for each node that participated in case ingest or review. // Any one (but only one!) of those subfolders may contain the actual index. /* - * NOTE: the following path is an example of valid Solr 4 Schema 1.8 multi-user index - * path: X:\Case\ingest1\ModuleOutput\keywordsearch\data\index + * NOTE: the following path is an example of valid Solr 4 Schema 1.8 + * multi-user index path: + * X:\Case\ingest1\ModuleOutput\keywordsearch\data\index */ // get a list of all folder's contents @@ -169,8 +183,8 @@ class IndexFinder { } else { // single user case /* - * NOTE: the following path is valid single user Solr 4 Schema 1.8 index - * path: X:\Case\ModuleOutput\keywordsearch\data\index + * NOTE: the following path is valid single user Solr 4 Schema 1.8 + * index path: X:\Case\ModuleOutput\keywordsearch\data\index */ File path = Paths.get(theCase.getModuleDirectory(), KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, INDEX_FOLDER_NAME).toFile(); //NON-NLS // must be a non-empty index directory diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexUpgrader.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexUpgrader.java index 944afa4e3c..d355db1296 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexUpgrader.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexUpgrader.java @@ -186,7 +186,7 @@ class IndexUpgrader { * * @return The new Solr index version. */ - private int upgradeSolrIndexVersion5to6(int currentIndexVersion, String solr5IndexPath, String tempResultsDir, UserCancelledProcessTerminator terminatior) throws AutopsyService.AutopsyServiceException, SecurityException, IOException { + private int upgradeSolrIndexVersion5to6(int currentIndexVersion, String solr5IndexPath, String tempResultsDir, UserCancelledProcessTerminator terminator) throws AutopsyService.AutopsyServiceException, SecurityException, IOException { if (currentIndexVersion != 5) { return currentIndexVersion; } @@ -218,7 +218,11 @@ class IndexUpgrader { ProcessBuilder processBuilder = new ProcessBuilder(commandLine); processBuilder.redirectOutput(new File(outputFileFullPath)); processBuilder.redirectError(new File(errFileFullPath)); - ExecUtil.execute(processBuilder, terminatior); + try { + ExecUtil.execute(processBuilder, terminator); + } catch (SecurityException | IOException ex) { + throw new AutopsyService.AutopsyServiceException("Error executing Solr 4 to Solr 5 upgrade tool"); + } // alternatively can execute lucene upgrade command from the folder where lucene jars are located // java -cp ".;lucene-core-6.2.1.jar;lucene-backward-codecs-6.2.1.jar;lucene-codecs-6.2.1.jar;lucene-analyzers-common-6.2.1.jar" org.apache.lucene.index.IndexUpgrader \path\to\index diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java index 19d60f71f7..bb2a7a4111 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java @@ -47,9 +47,7 @@ class KeywordSearchUtil { return query; } - StringBuilder sb = new StringBuilder(); - sb.append("\"").append(query).append("\""); - return sb.toString(); + return "\""+query+"\""; } /** diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 789f238329..741928c92d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -27,10 +27,12 @@ import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrRequest.METHOD; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.params.CursorMarkParams; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Version; @@ -38,6 +40,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; /** @@ -47,14 +50,13 @@ import org.sleuthkit.datamodel.TskException; class LuceneQuery implements KeywordSearchQuery { private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName()); - private final String keywordString; //original unescaped query private String keywordStringEscaped; private boolean isEscaped; - private Keyword originalKeyword = null; - private KeywordList keywordList = null; + private final Keyword originalKeyword ; + private final KeywordList keywordList ; private final List<KeywordQueryFilter> filters = new ArrayList<>(); private String field = null; - private static final int MAX_RESULTS = 20000; + private static final int MAX_RESULTS_PER_CURSOR_MARK = 512; static final int SNIPPET_LENGTH = 50; static final String HIGHLIGHT_FIELD = Server.Schema.TEXT.toString(); @@ -65,14 +67,10 @@ class LuceneQuery implements KeywordSearchQuery { * * @param keyword */ - public LuceneQuery(KeywordList keywordList, Keyword keyword) { + LuceneQuery(KeywordList keywordList, Keyword keyword) { this.keywordList = keywordList; this.originalKeyword = keyword; - - // @@@ BC: Long-term, we should try to get rid of this string and use only the - // keyword object. Refactoring did not make its way through this yet. - this.keywordString = keyword.getSearchTerm(); - this.keywordStringEscaped = this.keywordString; + this.keywordStringEscaped = this.originalKeyword.getSearchTerm(); } @Override @@ -89,12 +87,12 @@ class LuceneQuery implements KeywordSearchQuery { public void setSubstringQuery() { // Note that this is not a full substring search. Normally substring // searches will be done with TermComponentQuery objects instead. - keywordStringEscaped = keywordStringEscaped + "*"; + keywordStringEscaped += "*"; } @Override public void escape() { - keywordStringEscaped = KeywordSearchUtil.escapeLuceneQuery(keywordString); + keywordStringEscaped = KeywordSearchUtil.escapeLuceneQuery(originalKeyword.getSearchTerm()); isEscaped = true; } @@ -115,22 +113,82 @@ class LuceneQuery implements KeywordSearchQuery { @Override public String getQueryString() { - return this.keywordString; + return this.originalKeyword.getSearchTerm(); + } + + @Override + public KeywordList getKeywordList() { + return keywordList; } @Override public QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException { - QueryResults results = new QueryResults(this, keywordList); + + final Server solrServer = KeywordSearch.getServer(); + double indexSchemaVersion = NumberUtils.toDouble(solrServer.getIndexInfo().getSchemaVersion()); + + SolrQuery solrQuery = createAndConfigureSolrQuery(KeywordSearchSettings.getShowSnippets()); + + final String strippedQueryString = StringUtils.strip(getQueryString(), "\""); + + String cursorMark = CursorMarkParams.CURSOR_MARK_START; + boolean allResultsProcessed = false; + List<KeywordHit> matches = new ArrayList<>(); + while (!allResultsProcessed) { + solrQuery.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark); + QueryResponse response = solrServer.query(solrQuery, SolrRequest.METHOD.POST); + SolrDocumentList resultList = response.getResults(); + // objectId_chunk -> "text" -> List of previews + Map<String, Map<String, List<String>>> highlightResponse = response.getHighlighting(); + + for (SolrDocument resultDoc : resultList) { + try { + /* + * for each result doc, check that the first occurence of + * that term is before the window. if all the ocurences + * start within the window, don't record them for this + * chunk, they will get picked up in the next one. + */ + final String docId = resultDoc.getFieldValue(Server.Schema.ID.toString()).toString(); + final Integer chunkSize = (Integer) resultDoc.getFieldValue(Server.Schema.CHUNK_SIZE.toString()); + final Collection<Object> content = resultDoc.getFieldValues(Server.Schema.CONTENT_STR.toString()); + + if (indexSchemaVersion < 2.0) { + //old schema versions don't support chunk_size or the content_str fields, so just accept hits + matches.add(createKeywordtHit(highlightResponse, docId)); + } else { + //check against file name and actual content seperately. + for (Object content_obj : content) { + String content_str = (String) content_obj; + //for new schemas, check that the hit is before the chunk/window boundary. + int firstOccurence = StringUtils.indexOfIgnoreCase(content_str, strippedQueryString); + //there is no chunksize field for "parent" entries in the index + if (chunkSize == null || chunkSize == 0 || (firstOccurence > -1 && firstOccurence < chunkSize)) { + matches.add(createKeywordtHit(highlightResponse, docId)); + } + } + } + } catch (TskException ex) { + throw new KeywordSearchModuleException(ex); + } + } + String nextCursorMark = response.getNextCursorMark(); + if (cursorMark.equals(nextCursorMark)) { + allResultsProcessed = true; + } + cursorMark = nextCursorMark; + } + + QueryResults results = new QueryResults(this); //in case of single term literal query there is only 1 term - boolean showSnippets = KeywordSearchSettings.getShowSnippets(); - results.addResult(new Keyword(keywordString, true), performLuceneQuery(showSnippets)); + results.addResult(new Keyword(originalKeyword.getSearchTerm(), true), matches); return results; } @Override public boolean validate() { - return keywordString != null && !keywordString.equals(""); + return StringUtils.isNotBlank(originalKeyword.getSearchTerm()); } @Override @@ -143,7 +201,7 @@ class LuceneQuery implements KeywordSearchQuery { try { bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); writeResult = new KeywordCachedArtifact(bba); - } catch (Exception e) { + } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb artifact for keyword hit", e); //NON-NLS return null; } @@ -152,13 +210,10 @@ class LuceneQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); } attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); - if ((listName != null) && (listName.equals("") == false)) { + if (StringUtils.isNotBlank(listName)) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); } - //bogus - workaround the dir tree table issue - //attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(), MODULE_NAME, "", "")); - //selector if (originalKeyword != null) { BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType(); if (selType != null) { @@ -180,81 +235,14 @@ class LuceneQuery implements KeywordSearchQuery { bba.addAttributes(attributes); //write out to bb writeResult.add(attributes); return writeResult; - } catch (TskException e) { + } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS + return null; } - return null; } - /** - * Perform the query and return results of unique files. - * - * @param snippets True if results should have a snippet - * - * @return list of ContentHit objects. One per file with hit (ignores - * multiple hits of the word in the same doc) - * - * @throws NoOpenCoreException - */ - private List<KeywordHit> performLuceneQuery(boolean snippets) throws KeywordSearchModuleException, NoOpenCoreException { - List<KeywordHit> matches = new ArrayList<>(); - boolean allMatchesFetched = false; - final Server solrServer = KeywordSearch.getServer(); - SolrQuery q = createAndConfigureSolrQuery(snippets); - QueryResponse response; - SolrDocumentList resultList; - Map<String, Map<String, List<String>>> highlightResponse; - - response = solrServer.query(q, METHOD.POST); - - resultList = response.getResults(); - // objectId_chunk -> "text" -> List of previews - highlightResponse = response.getHighlighting(); - - final String strippedQueryString = StringUtils.strip(getQueryString(), "\""); - - // cycle through results in sets of MAX_RESULTS - for (int start = 0; !allMatchesFetched; start = start + MAX_RESULTS) { - q.setStart(start); - - allMatchesFetched = start + MAX_RESULTS >= resultList.getNumFound(); - - for (SolrDocument resultDoc : resultList) { - try { - /* for each result, check that the first occurence of that - * term is before the window. if all the ocurences start - * within the window, don't record them for this chunk, they - * will get picked up in the next one. */ - final String docId = resultDoc.getFieldValue(Server.Schema.ID.toString()).toString(); - final Integer chunkSize = (Integer) resultDoc.getFieldValue(Server.Schema.CHUNK_SIZE.toString()); - final Collection<Object> content = resultDoc.getFieldValues(Server.Schema.CONTENT_STR.toString()); - - double indexSchemaVersion = NumberUtils.toDouble(KeywordSearch.getServer().getIndexInfo().getSchemaVersion()); - if (indexSchemaVersion < 2.0) { - //old schema versions don't support chunk_size or the content_str fields, so just accept hits - matches.add(createKeywordtHit(highlightResponse, docId)); - } else { - //check against file name and actual content seperately. - for (Object content_obj : content) { - String content_str = (String) content_obj; - //for new schemas, check that the hit is before the chunk/window boundary. - int firstOccurence = StringUtils.indexOfIgnoreCase(content_str, strippedQueryString); - //there is no chunksize field for "parent" entries in the index - if (chunkSize == null || chunkSize == 0 || (firstOccurence > -1 && firstOccurence < chunkSize)) { - matches.add(createKeywordtHit(highlightResponse, docId)); - } - } - } - } catch (TskException ex) { - return matches; - } - } - } - return matches; - } - - /** + /* * Create the query object for the stored keyword * * @param snippets True if query should request snippets @@ -265,51 +253,59 @@ class LuceneQuery implements KeywordSearchQuery { SolrQuery q = new SolrQuery(); q.setShowDebugInfo(DEBUG); //debug // Wrap the query string in quotes if this is a literal search term. - String theQueryStr = originalKeyword.searchTermIsLiteral() + String queryStr = originalKeyword.searchTermIsLiteral() ? KeywordSearchUtil.quoteQuery(keywordStringEscaped) : keywordStringEscaped; // Run the query against an optional alternative field. if (field != null) { //use the optional field - StringBuilder sb = new StringBuilder(); - sb.append(field).append(":").append(theQueryStr); - theQueryStr = sb.toString(); + queryStr = field + ":" + queryStr; } - q.setQuery(theQueryStr); - q.setRows(MAX_RESULTS); + q.setQuery(queryStr); + q.setRows(MAX_RESULTS_PER_CURSOR_MARK); + // Setting the sort order is necessary for cursor based paging to work. + q.setSort(SolrQuery.SortClause.asc(Server.Schema.ID.toString())); q.setFields(Server.Schema.ID.toString(), Server.Schema.CHUNK_SIZE.toString(), Server.Schema.CONTENT_STR.toString()); - q.addSort(Server.Schema.ID.toString(), SolrQuery.ORDER.asc); + for (KeywordQueryFilter filter : filters) { q.addFilterQuery(filter.toString()); } if (snippets) { - q.addHighlightField(Server.Schema.TEXT.toString()); - //q.setHighlightSimplePre("«"); //original highlighter only - //q.setHighlightSimplePost("»"); //original highlighter only - q.setHighlightSnippets(1); - q.setHighlightFragsize(SNIPPET_LENGTH); - - //tune the highlighter - q.setParam("hl.useFastVectorHighlighter", "on"); //fast highlighter scales better than standard one NON-NLS - q.setParam("hl.tag.pre", "«"); //makes sense for FastVectorHighlighter only NON-NLS - q.setParam("hl.tag.post", "«"); //makes sense for FastVectorHighlighter only NON-NLS - q.setParam("hl.fragListBuilder", "simple"); //makes sense for FastVectorHighlighter only NON-NLS - - //Solr bug if fragCharSize is smaller than Query string, StringIndexOutOfBoundsException is thrown. - q.setParam("hl.fragCharSize", Integer.toString(theQueryStr.length())); //makes sense for FastVectorHighlighter only NON-NLS - - //docs says makes sense for the original Highlighter only, but not really - //analyze all content SLOW! consider lowering - q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //NON-NLS + configurwQueryForHighlighting(q); } return q; } + /** + * Configure the given query to return highlighting information. Must be + * called after setQuery() has been invoked on q. + * + * @param q The SolrQuery to configure. + */ + private static void configurwQueryForHighlighting(SolrQuery q) { + q.addHighlightField(HIGHLIGHT_FIELD); + q.setHighlightSnippets(1); + q.setHighlightFragsize(SNIPPET_LENGTH); + + //tune the highlighter + q.setParam("hl.useFastVectorHighlighter", "on"); //fast highlighter scales better than standard one NON-NLS + q.setParam("hl.tag.pre", "«"); //makes sense for FastVectorHighlighter only NON-NLS + q.setParam("hl.tag.post", "«"); //makes sense for FastVectorHighlighter only NON-NLS + q.setParam("hl.fragListBuilder", "simple"); //makes sense for FastVectorHighlighter only NON-NLS + + //Solr bug if fragCharSize is smaller than Query string, StringIndexOutOfBoundsException is thrown. + q.setParam("hl.fragCharSize", Integer.toString(q.getQuery().length())); //makes sense for FastVectorHighlighter only NON-NLS + + //docs says makes sense for the original Highlighter only, but not really + //analyze all content SLOW! consider lowering + q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //NON-NLS + } + private KeywordHit createKeywordtHit(Map<String, Map<String, List<String>>> highlightResponse, String docId) throws TskException { /** * Get the first snippet from the document if keyword search is @@ -324,7 +320,7 @@ class LuceneQuery implements KeywordSearchQuery { } } - return new KeywordHit(docId, snippet, keywordString); + return new KeywordHit(docId, snippet, originalKeyword.getSearchTerm()); } /** @@ -341,7 +337,7 @@ class LuceneQuery implements KeywordSearchQuery { * * @return */ - public static String querySnippet(String query, long solrObjectId, boolean isRegex, boolean group) throws NoOpenCoreException { + static String querySnippet(String query, long solrObjectId, boolean isRegex, boolean group) throws NoOpenCoreException { return querySnippet(query, solrObjectId, 0, isRegex, group); } @@ -360,62 +356,33 @@ class LuceneQuery implements KeywordSearchQuery { * * @return */ - public static String querySnippet(String query, long solrObjectId, int chunkID, boolean isRegex, boolean group) throws NoOpenCoreException { - Server solrServer = KeywordSearch.getServer(); - + static String querySnippet(String query, long solrObjectId, int chunkID, boolean isRegex, boolean group) throws NoOpenCoreException { SolrQuery q = new SolrQuery(); + q.setShowDebugInfo(DEBUG); //debug String queryStr; - if (isRegex) { - StringBuilder sb = new StringBuilder(); - sb.append(LuceneQuery.HIGHLIGHT_FIELD).append(":"); - if (group) { - sb.append("\""); - } - sb.append(query); - if (group) { - sb.append("\""); - } - - queryStr = sb.toString(); + queryStr = HIGHLIGHT_FIELD + ":" + + (group ? KeywordSearchUtil.quoteQuery(query) + : query); } else { - //simplify query/escaping and use default field - //always force grouping/quotes + /* + * simplify query/escaping and use default field always force + * grouping/quotes + */ queryStr = KeywordSearchUtil.quoteQuery(query); } - q.setQuery(queryStr); - String contentIDStr; - - if (chunkID == 0) { - contentIDStr = Long.toString(solrObjectId); - } else { - contentIDStr = Server.getChunkIdString(solrObjectId, chunkID); - } - + String contentIDStr = (chunkID == 0) + ? Long.toString(solrObjectId) + : Server.getChunkIdString(solrObjectId, chunkID); String idQuery = Server.Schema.ID.toString() + ":" + KeywordSearchUtil.escapeLuceneQuery(contentIDStr); - q.setShowDebugInfo(DEBUG); //debug q.addFilterQuery(idQuery); - q.addHighlightField(LuceneQuery.HIGHLIGHT_FIELD); - //q.setHighlightSimplePre("«"); //original highlighter only - //q.setHighlightSimplePost("»"); //original highlighter only - q.setHighlightSnippets(1); - q.setHighlightFragsize(SNIPPET_LENGTH); - //tune the highlighter - q.setParam("hl.useFastVectorHighlighter", "on"); //fast highlighter scales better than standard one NON-NLS - q.setParam("hl.tag.pre", "«"); //makes sense for FastVectorHighlighter only NON-NLS - q.setParam("hl.tag.post", "«"); //makes sense for FastVectorHighlighter only NON-NLS - q.setParam("hl.fragListBuilder", "simple"); //makes sense for FastVectorHighlighter only NON-NLS + configurwQueryForHighlighting(q); - //Solr bug if fragCharSize is smaller than Query string, StringIndexOutOfBoundsException is thrown. - q.setParam("hl.fragCharSize", Integer.toString(queryStr.length())); //makes sense for FastVectorHighlighter only NON-NLS - - //docs says makes sense for the original Highlighter only, but not really - //analyze all content SLOW! consider lowering - q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //NON-NLS + Server solrServer = KeywordSearch.getServer(); try { QueryResponse response = solrServer.query(q, METHOD.POST); @@ -439,42 +406,4 @@ class LuceneQuery implements KeywordSearchQuery { return ""; } } - - @Override - public KeywordList getKeywordList() { - return keywordList; - } - - /** - * Compares SolrDocuments based on their ID's. Two SolrDocuments with - * different chunk numbers are considered equal. - */ - static class SolrDocumentComparatorIgnoresChunkId implements Comparator<SolrDocument> { - - @Override - public int compare(SolrDocument left, SolrDocument right) { - // ID is in the form of ObjectId_Chunk - - final String idName = Server.Schema.ID.toString(); - - // get object id of left doc - String leftID = left.getFieldValue(idName).toString(); - int index = leftID.indexOf(Server.CHUNK_ID_SEPARATOR); - if (index != -1) { - leftID = leftID.substring(0, index); - } - - // get object id of right doc - String rightID = right.getFieldValue(idName).toString(); - index = rightID.indexOf(Server.CHUNK_ID_SEPARATOR); - if (index != -1) { - rightID = rightID.substring(0, index); - } - - Long leftLong = new Long(leftID); - Long rightLong = new Long(rightID); - return leftLong.compareTo(rightLong); - } - } - } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 8d47831dba..410ec1a94c 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -60,25 +60,17 @@ class QueryResults { */ private final Map<Keyword, List<KeywordHit>> results = new HashMap<>(); - /** - * The list of keywords - */ - // TODO: This is redundant. The keyword list is in the query. - private final KeywordList keywordList; + - QueryResults(KeywordSearchQuery query, KeywordList keywordList) { + QueryResults(KeywordSearchQuery query) { this.keywordSearchQuery = query; - this.keywordList = keywordList; } void addResult(Keyword keyword, List<KeywordHit> hits) { results.put(keyword, hits); } - // TODO: This is redundant. The keyword list is in the query. - KeywordList getKeywordList() { - return keywordList; - } + KeywordSearchQuery getQuery() { return keywordSearchQuery; @@ -129,7 +121,7 @@ class QueryResults { if (hitDisplayStr.length() > 50) { hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; } - subProgress.progress(keywordList.getName() + ": " + hitDisplayStr, unitProgress); + subProgress.progress(keywordSearchQuery.getKeywordList().getName() + ": " + hitDisplayStr, unitProgress); } for (KeywordHit hit : getOneHitPerObject(keyword)) { @@ -138,7 +130,11 @@ class QueryResults { if (StringUtils.isBlank(snippet)) { final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(termString); try { - //this doesn't work for regex queries... + /* + * this doesn't work for regex queries... But that is + * okay because regex queries always have snippets made + * from the content_str field we pull back from Solr + */ snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !keywordSearchQuery.isLiteral(), true); } catch (NoOpenCoreException e) { logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS @@ -149,16 +145,14 @@ class QueryResults { continue; } } - if (snippet != null) { - KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordList.getName()); - if (writeResult != null) { - newArtifacts.add(writeResult.getArtifact()); - if (notifyInbox) { - writeSingleFileInboxMessage(writeResult, hit.getContent()); - } - } else { - logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS + KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); + if (writeResult != null) { + newArtifacts.add(writeResult.getArtifact()); + if (notifyInbox) { + writeSingleFileInboxMessage(writeResult, hit.getContent()); } + } else { + logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS } } ++unitProgress; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index ea5e330ca9..c7004b1fa7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -81,7 +81,7 @@ final class RegexQuery implements KeywordSearchQuery { private final Keyword originalKeyword; // The regular expression originalKeyword used to perform the search. private String field = Server.Schema.CONTENT_STR.toString(); private final String keywordString; - static final private int MAX_RESULTS = 512; + static final private int MAX_RESULTS_PER_CURSOR_MARK = 512; private boolean escaped; private String escapedQuery; @@ -154,7 +154,6 @@ final class RegexQuery implements KeywordSearchQuery { @Override public QueryResults performQuery() throws NoOpenCoreException { - QueryResults results = new QueryResults(this, keywordList); final Server solrServer = KeywordSearch.getServer(); SolrQuery solrQuery = new SolrQuery(); @@ -187,12 +186,12 @@ final class RegexQuery implements KeywordSearchQuery { .map(KeywordQueryFilter::toString) .forEach(solrQuery::addFilterQuery); - solrQuery.setRows(MAX_RESULTS); + solrQuery.setRows(MAX_RESULTS_PER_CURSOR_MARK); // Setting the sort order is necessary for cursor based paging to work. solrQuery.setSort(SortClause.asc(Server.Schema.ID.toString())); String cursorMark = CursorMarkParams.CURSOR_MARK_START; - SolrDocumentList resultList = null; + SolrDocumentList resultList ; boolean allResultsProcessed = false; while (!allResultsProcessed) { @@ -218,15 +217,14 @@ final class RegexQuery implements KeywordSearchQuery { } cursorMark = nextCursorMark; } catch (KeywordSearchModuleException ex) { - LOGGER.log(Level.SEVERE, "Error executing Lucene Solr Query: " + keywordString, ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage()); } } - + QueryResults results = new QueryResults(this); for (Keyword k : hitsMultiMap.keySet()) { results.addResult(k, hitsMultiMap.get(k)); } - return results; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 2090aa3187..ce0d07bc78 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -590,7 +590,7 @@ public final class SearchRunner { // Create a new (empty) QueryResults object to hold the most recently // found hits. - QueryResults newResults = new QueryResults(queryResult.getQuery(), queryResult.getKeywordList()); + QueryResults newResults = new QueryResults(queryResult.getQuery()); // For each keyword represented in the results. for (Keyword keyword : queryResult.getKeywords()) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index 44cc8ab5e9..7ecfdbe35a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -280,7 +280,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { /* * Do a term query for each term that matched the regex. */ - QueryResults results = new QueryResults(this, keywordList); + QueryResults results = new QueryResults(this); for (Term term : terms) { /* * If searching for credit card account numbers, do a Luhn check on diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java index f49a3d4fc8..09c94c017c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.keywordsearch; import com.google.common.io.CharSource; import java.io.IOException; +import java.io.PushbackReader; import java.io.Reader; import java.util.List; import java.util.MissingResourceException; @@ -69,20 +70,29 @@ class TikaTextExtractor extends FileTextExtractor { final Future<Reader> future = tikaParseExecutor.submit(() -> new Tika().parse(stream, metadata)); try { final Reader tikaReader = future.get(getTimeout(sourceFile.getSize()), TimeUnit.SECONDS); - CharSource metaDataCharSource = getMetaDataCharSource(metadata); + + //check if the reader is empty + PushbackReader pushbackReader = new PushbackReader(tikaReader); + int read = pushbackReader.read(); + if (read == -1) { + throw new TextExtractorException("Tika returned empty reader for " + sourceFile); + } + pushbackReader.unread(read); + //concatenate parsed content and meta data into a single reader. - return CharSource.concat(new ReaderCharSource(tikaReader), metaDataCharSource).openStream(); + CharSource metaDataCharSource = getMetaDataCharSource(metadata); + return CharSource.concat(new ReaderCharSource(pushbackReader), metaDataCharSource).openStream(); } catch (TimeoutException te) { final String msg = NbBundle.getMessage(this.getClass(), "AbstractFileTikaTextExtract.index.tikaParseTimeout.text", sourceFile.getId(), sourceFile.getName()); logWarning(msg, te); - future.cancel(true); throw new TextExtractorException(msg, te); } catch (Exception ex) { KeywordSearch.getTikaLogger().log(Level.WARNING, "Exception: Unable to Tika parse the content" + sourceFile.getId() + ": " + sourceFile.getName(), ex.getCause()); //NON-NLS final String msg = NbBundle.getMessage(this.getClass(), "AbstractFileTikaTextExtract.index.exception.tikaParse.msg", sourceFile.getId(), sourceFile.getName()); logWarning(msg, ex); - future.cancel(true); throw new TextExtractorException(msg, ex); + } finally { + future.cancel(true); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/edit16.png b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/edit16.png new file mode 100644 index 0000000000..7be65127b7 Binary files /dev/null and b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/edit16.png differ diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index aeaf4d972a..a25f03f5b5 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -1,19 +1,19 @@ - /* +/* * * Autopsy Forensic Browser - * + * * Copyright 2012-2014 Basis Technology Corp. - * + * * Copyright 2012 42six Solutions. - * + * * Project Contact/Architect: carrier <at> sleuthkit <dot> org - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -112,6 +112,7 @@ class Chrome extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < historyFiles.size()) { String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName().toString() + j + ".db"; //NON-NLS @@ -155,13 +156,18 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } dbFile.delete(); } - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), - BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( + NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts)); } /** @@ -185,6 +191,7 @@ class Chrome extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < bookmarkFiles.size()) { @@ -286,9 +293,10 @@ class Chrome extends Extract { NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), domain)); bbart.addAttributes(bbattributes); - + // index the artifact for keyword search this.indexArtifact(bbart); + bbartifacts.add(bbart); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error while trying to insert Chrome bookmark artifact{0}", ex); //NON-NLS this.addErrorMessage( @@ -299,7 +307,9 @@ class Chrome extends Extract { dbFile.delete(); } - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( + NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts)); } /** @@ -324,6 +334,7 @@ class Chrome extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < cookiesFiles.size()) { AbstractFile cookiesFile = cookiesFiles.get(j++); @@ -370,13 +381,19 @@ class Chrome extends Extract { domain = domain.replaceFirst("^\\.+(?!$)", ""); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), domain)); - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } dbFile.delete(); } - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( + NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts)); } /** @@ -400,6 +417,7 @@ class Chrome extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < downloadFiles.size()) { AbstractFile downloadFile = downloadFiles.get(j++); @@ -456,14 +474,19 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } dbFile.delete(); } - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), - BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( + NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts)); } /** @@ -487,6 +510,7 @@ class Chrome extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < signonFiles.size()) { AbstractFile signonFile = signonFiles.get(j++); @@ -539,8 +563,13 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), result.get("signon_realm").toString())); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, signonFile, bbattributes); + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, signonFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } + + // Don't add TSK_OS_ACCOUNT artifacts to the ModuleDataEvent Collection<BlackboardAttribute> osAcctAttributes = new ArrayList<>(); osAcctAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), @@ -551,7 +580,9 @@ class Chrome extends Extract { dbFile.delete(); } - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY)); + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( + NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts)); } private boolean isChromePreVersion30(String temps) { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index 0c2c2e1ffb..5af71ecec0 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -86,16 +86,19 @@ abstract class Extract { * @param bbattributes is the collection of blackboard attributes that need * to be added to the artifact after the artifact has * been created + * @return The newly-created artifact, or null on error */ - protected void addArtifact(BlackboardArtifact.ARTIFACT_TYPE type, AbstractFile content, Collection<BlackboardAttribute> bbattributes) { + protected BlackboardArtifact addArtifact(BlackboardArtifact.ARTIFACT_TYPE type, AbstractFile content, Collection<BlackboardAttribute> bbattributes) { try { BlackboardArtifact bbart = content.newArtifact(type); bbart.addAttributes(bbattributes); // index the artifact for keyword search this.indexArtifact(bbart); + return bbart; } catch (TskException ex) { logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS } + return null; } /** diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java index 818e17ef6a..a494f64f77 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java @@ -1,19 +1,19 @@ - /* +/* * * Autopsy Forensic Browser - * + * * Copyright 2011-2016 Basis Technology Corp. - * + * * Copyright 2012 42six Solutions. * Contact: aebadirad <at> 42six <dot> com * Project Contact/Architect: carrier <at> sleuthkit <dot> org - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,6 +41,7 @@ import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import java.util.Collection; import java.util.Scanner; +import java.util.stream.Collectors; import org.openide.modules.InstalledFileLocator; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.datamodel.ContentUtils; @@ -109,6 +110,7 @@ class ExtractIE extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); for (AbstractFile fav : favoritesFiles) { if (fav.getSize() == 0) { continue; @@ -143,10 +145,15 @@ class ExtractIE extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName.noSpace"), domain)); - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, fav, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, fav, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK)); + NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts)); } private String getURLFromIEBookmarkFile(AbstractFile fav) { @@ -205,6 +212,7 @@ class ExtractIE extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); for (AbstractFile cookiesFile : cookiesFiles) { if (context.dataSourceIngestIsCancelled()) { break; @@ -253,10 +261,14 @@ class ExtractIE extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName.noSpace"), domain)); - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE)); + NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts)); } /** @@ -302,6 +314,7 @@ class ExtractIE extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); String temps; String indexFileName; for (AbstractFile indexFile : indexFiles) { @@ -336,7 +349,10 @@ class ExtractIE extends Extract { //At this point pasco2 proccessed the index files. //Now fetch the results, parse them and the delete the files. if (bPascProcSuccess) { - parsePascoOutput(indexFile, filename); + // Don't add TSK_OS_ACCOUNT artifacts to the ModuleDataEvent + bbartifacts.addAll(parsePascoOutput(indexFile, filename).stream() + .filter(bbart -> bbart.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) + .collect(Collectors.toList())); foundHistory = true; //Delete index<n>.dat file since it was succcessfully by Pasco @@ -350,7 +366,8 @@ class ExtractIE extends Extract { if (foundHistory) { services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY)); + NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts)); } } @@ -403,9 +420,12 @@ class ExtractIE extends Extract { * @param origFile Original index.dat file that was analyzed to * get this output * @param pascoOutputFileName name of pasco output file + * + * @return A collection of created artifacts */ - private void parsePascoOutput(AbstractFile origFile, String pascoOutputFileName) { + private Collection<BlackboardArtifact> parsePascoOutput(AbstractFile origFile, String pascoOutputFileName) { + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); String fnAbs = moduleTempResultsDir + File.separator + pascoOutputFileName; File file = new File(fnAbs); @@ -414,13 +434,13 @@ class ExtractIE extends Extract { NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.notFound", this.getName(), file.getName())); logger.log(Level.WARNING, "Pasco Output not found: {0}", file.getPath()); //NON-NLS - return; + return bbartifacts; } // Make sure the file the is not empty or the Scanner will // throw a "No Line found" Exception if (file.length() == 0) { - return; + return bbartifacts; } Scanner fileScanner; @@ -431,7 +451,7 @@ class ExtractIE extends Extract { NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.errParsing", this.getName(), file.getName())); logger.log(Level.WARNING, "Unable to find the Pasco file at " + file.getPath(), ex); //NON-NLS - return; + return bbartifacts; } // Keep a list of reported user accounts to avoid repeats @@ -521,6 +541,7 @@ class ExtractIE extends Extract { // index the artifact for keyword search this.indexArtifact(bbart); + bbartifacts.add(bbart); if ((!user.isEmpty()) && (!reportedUserAccounts.contains(user))) { BlackboardArtifact osAttr = origFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); @@ -529,6 +550,7 @@ class ExtractIE extends Extract { // index the artifact for keyword search this.indexArtifact(osAttr); + bbartifacts.add(osAttr); reportedUserAccounts.add(user); } @@ -537,5 +559,6 @@ class ExtractIE extends Extract { } } fileScanner.close(); + return bbartifacts; } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java index 9608c5aed1..34dfc24733 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java @@ -97,7 +97,7 @@ class Firefox extends Extract { } dataFound = true; - + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; for (AbstractFile historyFile : historyFiles) { if (historyFile.getSize() == 0) { @@ -148,14 +148,19 @@ class Firefox extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName.noSpace"), (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } ++j; dbFile.delete(); } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY)); + NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts)); } /** @@ -180,7 +185,7 @@ class Firefox extends Extract { } dataFound = true; - + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; for (AbstractFile bookmarkFile : bookmarkFiles) { if (bookmarkFile.getSize() == 0) { @@ -228,15 +233,19 @@ class Firefox extends Extract { NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName.noSpace"), (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes); + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } ++j; dbFile.delete(); } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK)); + NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts)); } /** @@ -260,6 +269,7 @@ class Firefox extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; for (AbstractFile cookiesFile : cookiesFiles) { if (cookiesFile.getSize() == 0) { @@ -326,14 +336,19 @@ class Firefox extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName.noSpace"), domain)); - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } ++j; dbFile.delete(); } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE)); + NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts)); } /** @@ -368,6 +383,7 @@ class Firefox extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; for (AbstractFile downloadsFile : downloadsFiles) { if (downloadsFile.getSize() == 0) { @@ -436,8 +452,11 @@ class Firefox extends Extract { NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName.noSpace"), (Util.extractDomain((result.get("source").toString() != null) ? result.get("source").toString() : "")))); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } if (errors > 0) { this.addErrorMessage( @@ -450,7 +469,8 @@ class Firefox extends Extract { } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD)); + NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts)); } /** @@ -476,6 +496,7 @@ class Firefox extends Extract { } dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; for (AbstractFile downloadsFile : downloadsFiles) { if (downloadsFile.getSize() == 0) { @@ -546,8 +567,11 @@ class Firefox extends Extract { NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName.noSpace"), (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS - this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (bbart != null) { + bbartifacts.add(bbart); + } } if (errors > 0) { this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errParsingArtifacts", @@ -559,6 +583,7 @@ class Firefox extends Extract { } services.fireModuleDataEvent(new ModuleDataEvent( - NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD)); + NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts)); } } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 2588954fb9..1cc67f5a00 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Mon, 30 Jan 2017 13:39:12 -0500 +#Mon, 06 Mar 2017 10:02:14 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 0fe774eeab..ac48ca4731 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Mon, 30 Jan 2017 13:39:12 -0500 +#Mon, 06 Mar 2017 10:02:14 -0500 CTL_MainWindow_Title=Autopsy 4.3.0 CTL_MainWindow_Title_No_Project=Autopsy 4.3.0 diff --git a/build.xml b/build.xml index 55951fb079..9288184ea1 100755 --- a/build.xml +++ b/build.xml @@ -8,7 +8,7 @@ <import file="nbproject/build-impl.xml"/> <!-- IMPORTANT: nbproject/platform.properties has a netbeans-plat-version property that MUST be kept in sync (manually) --> - <property name="netbeans-plat-version" value="8.1" /> + <property name="netbeans-plat-version" value="8.2" /> <property name="nbplatform.active.dir" value="${basedir}/netbeans-plat/${netbeans-plat-version}" /> <!-- Supported java versions.--> <condition property="supported-java-versions"> diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index c91a51a355..2b63032682 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -8,4 +8,4 @@ nbproject/build-impl.xml.script.CRC32=b72261eb nbproject/build-impl.xml.stylesheet.CRC32=0f381476@2.47.1 nbproject/platform.xml.data.CRC32=2e7964b0 nbproject/platform.xml.script.CRC32=6dcbd131 -nbproject/platform.xml.stylesheet.CRC32=4e1f53d4@2.62.1 +nbproject/platform.xml.stylesheet.CRC32=45ddf0e0@2.72.1 diff --git a/nbproject/platform.properties b/nbproject/platform.properties index 9d31c5e1e0..07a18d325c 100644 --- a/nbproject/platform.properties +++ b/nbproject/platform.properties @@ -1,7 +1,7 @@ branding.token=autopsy # Version of platform that is automatically downloaded # IMPORTANT: autopsy/build.xml has a netbeans-plat-version property that MUST be kept in sync (manually) -netbeans-plat-version=8.1 +netbeans-plat-version=8.2 suite.dir=${basedir} nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version} harness.dir=${nbplatform.active.dir}/harness diff --git a/thirdparty/junit/8.2/junit.zip b/thirdparty/junit/8.2/junit.zip new file mode 100644 index 0000000000..db1b83aea2 Binary files /dev/null and b/thirdparty/junit/8.2/junit.zip differ
"); //NON-NLS - buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_attribute()); - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS + buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_type()); + buffer.append(""); //NON-NLS buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_value()); - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_sources()); - buffer.append("