diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/AbstractFileSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/AbstractFileSearchFilter.java index 67f65be80b..149e11afa5 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/AbstractFileSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/AbstractFileSearchFilter.java @@ -31,9 +31,20 @@ import javax.swing.JComponent; abstract class AbstractFileSearchFilter implements FileSearchFilter { final private T component; - + private String lastErrorMessage; + AbstractFileSearchFilter(T component) { this.component = component; + this.lastErrorMessage = ""; + } + + void setLastError(String mes){ + lastErrorMessage = mes; + } + + @Override + public String getLastError(){ + return this.lastErrorMessage; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties index fff8007fd4..bb91c8e05c 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties @@ -57,4 +57,5 @@ MimeTypePanel.jLabel1.text=*Note: Multiple MIME types can be selected FileSearchPanel.searchButton.text=Search MimeTypePanel.mimeTypeCheckBox.text=MIME Type: HashSearchPanel.md5CheckBox.text=MD5: -HashSearchPanel.emptyHashMsg.text=Must enter something for hash search. \ No newline at end of file +HashSearchPanel.emptyHashMsg.text=Must enter something for hash search. +FileSearchPanel.errorLabel.text=\ diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java index 205db56998..3291c5ae42 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java @@ -42,6 +42,7 @@ import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.openide.util.NbBundle.Messages; /** * Filters file date properties (modified/created/etc.. times) @@ -79,25 +80,10 @@ class DateSearchFilter extends AbstractFileSearchFilter { String query = "NULL"; DateSearchPanel panel = this.getComponent(); - // first, get the selected timeZone from the dropdown list - String tz = this.getComponent().getTimeZoneComboBox().getSelectedItem().toString(); - String tzID = tz.substring(tz.indexOf(" ") + 1); // 1 index after the space is the ID - TimeZone selectedTZ = TimeZone.getTimeZone(tzID); // - // convert the date from the selected timezone to get the GMT long fromDate = 0; String startDateValue = panel.getDateFromTextField().getText(); - Calendar startDate = null; - try { - DateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); - sdf.setTimeZone(selectedTZ); // get the time in the selected timezone - Date temp = sdf.parse(startDateValue); - - startDate = Calendar.getInstance(new SimpleTimeZone(0, "GMT")); //NON-NLS - startDate.setTime(temp); // convert to GMT - } catch (ParseException ex) { - // for now, no need to show the error message to the user here - } + Calendar startDate = getCalendarDate(startDateValue); if (!startDateValue.isEmpty()) { if (startDate != null) { fromDate = startDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds @@ -106,31 +92,13 @@ class DateSearchFilter extends AbstractFileSearchFilter { long toDate = 0; String endDateValue = panel.getDateToTextField().getText(); - Calendar endDate = null; - try { - DateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); - sdf.setTimeZone(selectedTZ); // get the time in the selected timezone - Date temp2 = sdf.parse(endDateValue); - - endDate = Calendar.getInstance(new SimpleTimeZone(0, "GMT")); //NON-NLS - endDate.setTime(temp2); // convert to GMT - endDate.set(Calendar.HOUR, endDate.get(Calendar.HOUR) + 24); // get the next 24 hours - } catch (ParseException ex) { - // for now, no need to show the error message to the user here - } + Calendar endDate = getCalendarDate(endDateValue); if (!endDateValue.isEmpty()) { if (endDate != null) { toDate = endDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds } } - // If they put the dates in backwards, help them out. - if (fromDate > toDate) { - long temp = toDate; - toDate = fromDate; - fromDate = temp; - } - final boolean modifiedChecked = panel.getModifiedCheckBox().isSelected(); final boolean changedChecked = panel.getChangedCheckBox().isSelected(); final boolean accessedChecked = panel.getAccessedCheckBox().isSelected(); @@ -206,14 +174,56 @@ class DateSearchFilter extends AbstractFileSearchFilter { return timeZones; } + private TimeZone getSelectedTimeZone() { + String tz = this.getComponent().getTimeZoneComboBox().getSelectedItem().toString(); + String tzID = tz.substring(tz.indexOf(" ") + 1); // 1 index after the space is the ID + TimeZone selectedTZ = TimeZone.getTimeZone(tzID); // + return selectedTZ; + } + + private Calendar getCalendarDate(String dateValue) { + TimeZone selectedTZ = getSelectedTimeZone(); + Calendar inputDate = null; + try { + DateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); + sdf.setTimeZone(selectedTZ); // get the time in the selected timezone + Date temp = sdf.parse(dateValue); + + inputDate = Calendar.getInstance(new SimpleTimeZone(0, "GMT")); //NON-NLS + inputDate.setTime(temp); // convert to GMT + } catch (ParseException ex) { + // for now, no need to show the error message to the user here + } + return inputDate; + } + @Override public void addActionListener(ActionListener l) { getComponent().addActionListener(l); } @Override + @Messages ({ + "DateSearchFilter.errorMessage.endDateBeforeStartDate=The end date should be after the start date.", + "DateSearchFilter.errorMessage.noCheckboxSelected=At least one date type checkbox must be selected." + }) public boolean isValid() { - return this.getComponent().isValidSearch(); + + DateSearchPanel panel = this.getComponent(); + Calendar startDate = getCalendarDate(panel.getDateFromTextField().getText()); + Calendar endDate = getCalendarDate(panel.getDateToTextField().getText()); + + if ((startDate != null && startDate.after(endDate)) || (endDate != null && endDate.before(startDate))) { + setLastError(Bundle.DateSearchFilter_errorMessage_endDateBeforeStartDate()); + return false; + } + + if (!panel.isValidSearch()) { + setLastError(Bundle.DateSearchFilter_errorMessage_noCheckboxSelected()); + return false; + } + + return true; } /** diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.form index e92d57fdd9..59082745da 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.form @@ -149,6 +149,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java index 7867a5ebd6..b9f490041b 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java @@ -29,6 +29,8 @@ import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; /** * Subpanel with controls for file data filtering. @@ -50,6 +52,7 @@ class DateSearchPanel extends javax.swing.JPanel { dateFromTextField.setComponentPopupMenu(rightClickMenu); dateToTextField.setComponentPopupMenu(rightClickMenu); + ActionListener actList = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -74,6 +77,41 @@ class DateSearchPanel extends javax.swing.JPanel { copyMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList); selectAllMenuItem.addActionListener(actList); + this.dateFromTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + }); + + this.dateToTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + }); + + this.setComponentsEnabled(); } @@ -176,6 +214,7 @@ class DateSearchPanel extends javax.swing.JPanel { selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.selectAllMenuItem.text")); // NOI18N rightClickMenu.add(selectAllMenuItem); + dateToTextField.setEditable(false); dateToTextField.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateToTextField.text")); // NOI18N dateToTextField.addFocusListener(new java.awt.event.FocusAdapter() { public void focusLost(java.awt.event.FocusEvent evt) { @@ -197,6 +236,7 @@ class DateSearchPanel extends javax.swing.JPanel { jLabel3.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N jLabel3.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.jLabel3.text")); // NOI18N + dateFromTextField.setEditable(false); dateFromTextField.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateFromTextField.text")); // NOI18N dateFromTextField.addFocusListener(new java.awt.event.FocusAdapter() { public void focusLost(java.awt.event.FocusEvent evt) { @@ -365,6 +405,7 @@ class DateSearchPanel extends javax.swing.JPanel { if (evt.getNewValue() instanceof Date) { setToDate((Date) evt.getNewValue()); } + }//GEN-LAST:event_dateToPopupChanged private void dateCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dateCheckBoxActionPerformed @@ -399,6 +440,7 @@ class DateSearchPanel extends javax.swing.JPanel { if (date != null) { dateStringResult = dateFormat.format(date); } + dateFromTextField.setText(dateStringResult); dateFromButtonCalendar.setTargetDate(date); } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchFilter.java index 457db56570..db16fdd1e0 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchFilter.java @@ -47,6 +47,13 @@ interface FileSearchFilter { * @return Whether the panel has valid input for search. */ boolean isValid(); + + /** + * Get the last error recorded during the call to isValid + * + * @return Description of why the filter is invalid + */ + String getLastError(); /** * Gets predicate expression to include in the SQL filter expression diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form index dbb90bea12..a64489bb01 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form @@ -23,7 +23,9 @@ - + + + @@ -32,9 +34,14 @@ - - - + + + + + + + + @@ -63,6 +70,19 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index d7efb0b46f..f6c72df611 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -27,7 +27,6 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -111,13 +110,7 @@ class FileSearchPanel extends javax.swing.JPanel { } }); } - - addListenerToAll(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - search(); - } - }); + searchButton.setEnabled(isValidSearch()); } @@ -130,11 +123,13 @@ class FileSearchPanel extends javax.swing.JPanel { if (filter.isEnabled()) { enabled = true; if (!filter.isValid()) { + errorLabel.setText(filter.getLastError()); return false; } } } + errorLabel.setText(""); return enabled; } @@ -280,6 +275,7 @@ class FileSearchPanel extends javax.swing.JPanel { filterPanel = new javax.swing.JPanel(); searchButton = new javax.swing.JButton(); + errorLabel = new javax.swing.JLabel(); setPreferredSize(new java.awt.Dimension(300, 300)); @@ -288,6 +284,14 @@ class FileSearchPanel extends javax.swing.JPanel { filterPanel.setLayout(new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS)); searchButton.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.searchButton.text")); // NOI18N + searchButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + searchButtonActionPerformed(evt); + } + }); + + errorLabel.setForeground(new java.awt.Color(255, 51, 51)); + errorLabel.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.errorLabel.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -295,21 +299,31 @@ class FileSearchPanel extends javax.swing.JPanel { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap() + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(searchButton) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(0, 0, 0) - .addComponent(searchButton) + .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(searchButton) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorLabel))) .addContainerGap()) ); }// //GEN-END:initComponents + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + search(); + }//GEN-LAST:event_searchButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel errorLabel; private javax.swing.JPanel filterPanel; private javax.swing.JButton searchButton; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java index 951f4b206e..d77fe5826e 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchFilter.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionListener; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; /** @@ -60,7 +61,25 @@ class HashSearchFilter extends AbstractFileSearchFilter { } @Override + @Messages({ + "HashSearchFilter.errorMessage.emptyHash=Hash data is empty.", + "# {0} - hash data length", "HashSearchFilter.errorMessage.wrongLength=Input length({0}), doesn''t match the MD5 length(32).", + "HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters." + }) public boolean isValid() { - return !this.getComponent().getSearchTextField().getText().isEmpty(); + String inputHashData = this.getComponent().getSearchTextField().getText(); + if (inputHashData.isEmpty()) { + setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash()); + return false; + } + if (inputHashData.length() != 32) { + setLastError(Bundle.HashSearchFilter_errorMessage_wrongLength(inputHashData.length())); + return false; + } + if (!inputHashData.matches("[0-9a-fA-F]+")) { + setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter()); + return false; + } + return true; } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchFilter.java index 5f52afa035..677f0116e9 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchFilter.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionListener; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.TskData.FileKnown; /** @@ -85,7 +86,14 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter { } @Override + @Messages ({ + "MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected." + }) public boolean isValid() { - return !this.getComponent().getMimeTypesSelected().isEmpty(); + if(this.getComponent().getMimeTypesSelected().isEmpty()){ + setLastError(Bundle.MimeTypeFilter_errorMessage_emptyMimeType()); + return false; + } + return true; } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchFilter.java index ee70ebd5e1..2b204c8ca8 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchFilter.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionListener; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; /** @@ -64,7 +65,14 @@ class NameSearchFilter extends AbstractFileSearchFilter { } @Override + @Messages ({ + "NameSearchFilter.errorMessage.emtpyName=Please input a name to search." + }) public boolean isValid() { - return !this.getComponent().getSearchTextField().getText().isEmpty(); + if(this.getComponent().getSearchTextField().getText().isEmpty()) { + setLastError(Bundle.NameSearchFilter_errorMessage_emtpyName()); + return false; + } + return true; } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchFilter.java index ca43dac7e0..cf5d4f9e73 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchFilter.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionListener; import javax.swing.JComboBox; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; /** @@ -74,7 +75,23 @@ class SizeSearchFilter extends AbstractFileSearchFilter { } @Override + @Messages ({ + "SizeSearchFilter.errorMessage.nonNegativeNumber=Input size data is a negative number.", + "SizeSearchFilter.errorMessage.notANumber=Input size data is not a number." + }) public boolean isValid() { + String input = this.getComponent().getSizeTextField().getText(); + + try { + int inputInt = Integer.parseInt(input); + if (inputInt < 0) { + setLastError(Bundle.SizeSearchFilter_errorMessage_nonNegativeNumber()); + return false; + } + } catch (NumberFormatException | NullPointerException e) { + setLastError(Bundle.SizeSearchFilter_errorMessage_notANumber()); + return false; + } return true; } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java index 3add979247..51aa688619 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java @@ -25,6 +25,8 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JMenuItem; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; /** * @@ -65,6 +67,24 @@ class SizeSearchPanel extends javax.swing.JPanel { copyMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList); selectAllMenuItem.addActionListener(actList); + this.sizeTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null); + } + }); + + } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java index f6580a3cbc..8aeda52597 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java @@ -192,8 +192,6 @@ public final class IngestMonitor { } logMemoryUsage(); - logDiskSpaceUsage(); - if (!enoughDiskSpace()) { /* * Shut down ingest by cancelling all ingest jobs. diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java index 3110947488..249ded2020 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java @@ -33,7 +33,8 @@ import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; */ class ProfilePanel extends IngestModuleGlobalSettingsPanel { - @NbBundle.Messages({"ProfilePanel.profileDescLabel.text=Description:", + @NbBundle.Messages({"ProfilePanel.title.text=Profile", + "ProfilePanel.profileDescLabel.text=Description:", "ProfilePanel.profileNameLabel.text=Profile Name:", "ProfilePanel.newProfileText=NewEmptyProfile", "ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.", @@ -50,6 +51,7 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel { */ ProfilePanel() { initComponents(); + setName(org.openide.util.NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.title.text")); settings = new IngestJobSettings(NEW_PROFILE_NAME); ingestSettingsPanel = new IngestJobSettingsPanel(settings); ingestSettingsPanel.setPastJobsButtonVisible(false); @@ -59,6 +61,7 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel { ProfilePanel(IngestProfile selectedProfile) { initComponents(); + setName(org.openide.util.NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.title.text")); profile = selectedProfile; profileDescArea.setText(profile.getDescription()); profileNameField.setText(profile.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties index df63e98d2e..f38bcbc815 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties @@ -4,8 +4,6 @@ OpenIDE-Module-Short-Description=Interesting Files Identifier ingest module. OpenIDE-Module-Name=Interesting Files Identifier OptionsCategory_Name_InterestingItemDefinitions=Interesting Files OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions -OptionsCategory_Name_FileIngestFilterDefinitions=File Ingest Filter -OptionsCategory_Keywords_FileIngestFilterDefinitions=FileIngestFilterDefinitions InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets. FilesSetPanel.interesting.title=Interesting Files Set @@ -45,7 +43,7 @@ FilesSetRulePanel.fileSizeCheck.text=File Size: FilesSetRulePanel.filesRadioButton.text=Files FilesSetRulePanel.dirsRadioButton.text=Directories FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets: -FilesSetDefsPanel.ingest.setsListLabel.text=File Ingest Filters: +FilesSetDefsPanel.ingest.setsListLabel.text=File Filters: FilesSetDefsPanel.interesting.jTextArea1.text=This module allows you to find files that match specified criteria. Each set has a list of rules, which will match on their chosen file characteristics. A file need only match one rule to be found. FilesSetDefsPanel.ingest.jTextArea1.text=Add rules so that only a subset of the files in a data source are analyzed. Rules are organized into sets and only one set can be used at a time. A file need only match one rule to be analyzed. FilesSetDefsPanel.interesting.editSetButton.text=Edit Set diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java index f989d24971..c67051be97 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java @@ -77,6 +77,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp private final JButton okButton = new JButton("OK"); private final JButton cancelButton = new JButton("Cancel"); private final PANEL_TYPE panelType; + private final String filterDialogTitle; private final String ruleDialogTitle; private boolean canBeEnabled = true; @@ -109,7 +110,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp this.jLabel7.setVisible(false); this.fileSizeUnitComboBox.setVisible(false); this.fileSizeSpinner.setVisible(false); - this.ruleDialogTitle = "FilesSetPanel.ingest.title"; + this.filterDialogTitle = "FilesSetPanel.filter.title"; + this.ruleDialogTitle = "FilesSetPanel.rule.title"; this.jLabel8.setVisible(false); this.equalitySignComboBox.setVisible(false); this.ignoreKnownFilesCheckbox.setVisible(false); @@ -124,13 +126,14 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp org.openide.awt.Mnemonics.setLocalizedText(deleteSetButton, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.ingest.deleteSetButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.ingest.jLabel6.text")); // NOI18N } else { + this.filterDialogTitle = "FilesSetPanel.interesting.title"; this.ruleDialogTitle = "FilesSetPanel.interesting.title"; this.ingoreUnallocCheckbox.setVisible(false); } } @NbBundle.Messages({"FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings", - "FilesSetDefsPanel.Ingest.Title=File Ingest Filter Settings"}) + "FilesSetDefsPanel.Ingest.Title=File Filter Settings"}) private void customInit() { if (panelType == PANEL_TYPE.FILE_INGEST_FILTERS) { setName(Bundle.FilesSetDefsPanel_Ingest_Title()); @@ -408,7 +411,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp // feedback when isValidDefinition() is called. int option = JOptionPane.OK_OPTION; do { - option = JOptionPane.showConfirmDialog(null, panel, NbBundle.getMessage(FilesSetPanel.class, ruleDialogTitle), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); + option = JOptionPane.showConfirmDialog(null, panel, NbBundle.getMessage(FilesSetPanel.class, filterDialogTitle), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); } while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition()); // While adding new ruleset(selectedSet == null), if rule set with same name already exists, do not add to the filesSets hashMap. diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java index 962c18c8e1..51539b5806 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-2016 Basis Technology Corp. + * Copyright 2014-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TY */ public class FilesSetPanel extends javax.swing.JPanel { - @NbBundle.Messages({"FilesSetPanel.ingest.title=File Ingest Filter", "FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...", "FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named."}) + @NbBundle.Messages({"FilesSetPanel.filter.title=File Filter", "FilesSetPanel.rule.title=File Filter Rule", "FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...", "FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named."}) private static final String CREATE_NEW_FILE_INGEST_FILTER = Bundle.FilesSetPanel_ingest_createNewFilter(); private final String mustBeNamedErrorText; diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java index 234ed1509c..3df94c1d1c 100755 --- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java @@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -174,7 +175,14 @@ class TableReportGenerator { * does not require a artifact name, so we make a synthetic * compund name by appending a ":" and the account type. */ - final String compundDataTypeName = BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName() + ": " + accountType; + String accountDisplayname = accountType; + for (Account.Type acct : Account.Type.values()) { + if (acct.equals(Account.Type.valueOf(accountType))) { + accountDisplayname = acct.getDisplayName(); + break; + } + } + final String compundDataTypeName = BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName() + ": " + accountDisplayname; writeTableForDataType(new ArrayList<>(groupedArtifacts.get(accountType)), type, compundDataTypeName, comment); } } else { diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java index 93dad76f89..1066973f58 100755 --- a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java @@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettingsPanel; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; - /** * Instances of this class are used to configure the report module plug in that * provides a convenient way to add content hashes to hash set databases. @@ -94,9 +93,11 @@ class AddTaggedHashesToHashDbConfigPanel extends javax.swing.JPanel { public void mousePressed(MouseEvent evt) { JList list = (JList) evt.getSource(); int index = list.locationToIndex(evt.getPoint()); - String value = tagsNamesListModel.getElementAt(index); - tagNameSelections.put(value, !tagNameSelections.get(value)); - list.repaint(); + if (index > -1) { + String value = tagsNamesListModel.getElementAt(index); + tagNameSelections.put(value, !tagNameSelections.get(value)); + list.repaint(); + } } }); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java new file mode 100755 index 0000000000..3bc76e61fd --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java @@ -0,0 +1,122 @@ +/* + * 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.experimental.autoingest; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.apache.commons.io.FilenameUtils; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; + +/* + * A runnable that adds an archive data source as well as data sources + * contained in the archive to the case database. + */ +class AddArchiveTask implements Runnable { + + private final Logger logger = Logger.getLogger(AddArchiveTask.class.getName()); + private final String deviceId; + private final String archivePath; + private final DataSourceProcessorProgressMonitor progressMonitor; + private final DataSourceProcessorCallback callback; + private boolean criticalErrorOccurred; + + private static final String ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR = "Archive Extractor"; + + /** + * Constructs a runnable task that adds an archive as well as data sources + * contained in the archive to the case database. + * + * @param deviceId An ASCII-printable identifier for the device associated + * with the data source that is intended to be unique across multiple cases + * (e.g., a UUID). + * @param archivePath Path to the archive file. + * @param progressMonitor Progress monitor to report progress during + * processing. + * @param callback Callback to call when processing is done. + */ + AddArchiveTask(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + this.deviceId = deviceId; + this.archivePath = archivePath; + this.callback = callback; + this.progressMonitor = progressMonitor; + } + + /** + * Adds the archive to the case database. + */ + @Override + public void run() { + List errorMessages = new ArrayList<>(); + List newDataSources = new ArrayList<>(); + DataSourceProcessorCallback.DataSourceProcessorResult result; + if (!ArchiveUtil.isArchive(Paths.get(archivePath))) { + criticalErrorOccurred = true; + logger.log(Level.SEVERE, String.format("Input data source is not a valid datasource: %s", archivePath)); //NON-NLS + errorMessages.add("Input data source is not a valid datasource: " + archivePath); + result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; + callback.done(result, errorMessages, newDataSources); + } + + // extract the archive and pass the extracted folder as input + Path destinationFolder = Paths.get(""); + try { + Case currentCase = Case.getCurrentCase(); + + // get file name without full path or extension + String dataSourceFileNameNoExt = FilenameUtils.getBaseName(archivePath); + + // create folder to extract archive to + destinationFolder = Paths.get(currentCase.getModuleDirectory(), ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR, dataSourceFileNameNoExt + "_" + TimeStampUtils.createTimeStamp()); + destinationFolder.toFile().mkdirs(); + + // extract contents of ZIP archive into destination folder + //ArchiveUtil.unpackArchiveFile(archivePath, destinationFolder.toString()); + + // do processing + + } catch (Exception ex) { + criticalErrorOccurred = true; + errorMessages.add(ex.getMessage()); + logger.log(Level.SEVERE, String.format("Critical error occurred while extracting archive %s", archivePath), ex); //NON-NLS + } finally { + if (criticalErrorOccurred) { + result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; + } else if (!errorMessages.isEmpty()) { + result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS; + } else { + result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS; + } + callback.done(result, errorMessages, newDataSources); + } + } + + /* + * Attempts to cancel adding the archive to the case database. + */ + public void cancelTask() { + + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java new file mode 100755 index 0000000000..db19fc2fbc --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java @@ -0,0 +1,98 @@ +/* + * 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.experimental.autoingest; + +import java.util.List; +import java.util.UUID; +import javax.annotation.concurrent.Immutable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.datamodel.Content; + + +/** + * A "callback" that collects the results of running a data source processor on + * a data source and unblocks the job processing thread when the data source + * processor finishes running in its own thread. + */ +@Immutable +class AddDataSourceCallback extends DataSourceProcessorCallback { + + private final Case caseForJob; + private final DataSource dataSourceInfo; + private final UUID taskId; + private final Object lock; + + /** + * Constructs a "callback" that collects the results of running a data + * source processor on a data source and unblocks the job processing thread + * when the data source processor finishes running in its own thread. + * + * @param caseForJob The case for the current job. + * @param dataSourceInfo The data source + * @param taskId The task id to associate with ingest job events. + */ + AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId, Object lock) { + this.caseForJob = caseForJob; + this.dataSourceInfo = dataSourceInfo; + this.taskId = taskId; + this.lock = lock; + } + + /** + * Called by the data source processor when it finishes running in its own + * thread. + * + * @param result The result code for the processing of the data source. + * @param errorMessages Any error messages generated during the processing + * of the data source. + * @param dataSourceContent The content produced by processing the data + * source. + */ + @Override + public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSourceContent) { + if (!dataSourceContent.isEmpty()) { + caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId); + } else { + caseForJob.notifyFailedAddingDataSource(taskId); + } + dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent); + dataSourceContent.addAll(dataSourceContent); + synchronized (lock) { + lock.notify(); + } + } + + /** + * Called by the data source processor when it finishes running in its own + * thread, if that thread is the AWT (Abstract Window Toolkit) event + * dispatch thread (EDT). + * + * @param result The result code for the processing of the data source. + * @param errorMessages Any error messages generated during the processing + * of the data source. + * @param dataSourceContent The content produced by processing the data + * source. + */ + @Override + public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSources) { + done(result, errorMessages, dataSources); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java new file mode 100755 index 0000000000..a649b0f37a --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java @@ -0,0 +1,224 @@ +/* + * 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.experimental.autoingest; + +import java.nio.file.Path; +import java.util.UUID; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.openide.util.lookup.ServiceProviders; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; + +/** + * A data source processor that handles archive files. Implements the + * DataSourceProcessor service provider interface to allow integration with the + * add data source wizard. It also provides a run method overload to allow it to + * be used independently of the wizard. + */ +//@ServiceProviders(value={ +// @ServiceProvider(service=DataSourceProcessor.class), +// @ServiceProvider(service=AutoIngestDataSourceProcessor.class)} +//) +@NbBundle.Messages({ + "ArchiveDSP.dsType.text=Archive file"}) +public class ArchiveExtractorDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor { + + private final static String DATA_SOURCE_TYPE = Bundle.ArchiveDSP_dsType_text(); + + private final ArchiveFilePanel configPanel; + private String deviceId; + private String archivePath; + private boolean setDataSourceOptionsCalled; + + private AddArchiveTask addArchiveTask; + + /** + * Constructs an archive data source processor that + * implements the DataSourceProcessor service provider interface to allow + * integration with the add data source wizard. It also provides a run + * method overload to allow it to be used independently of the wizard. + */ + public ArchiveExtractorDSProcessor() { + configPanel = ArchiveFilePanel.createInstance(ArchiveExtractorDSProcessor.class.getName(), ArchiveUtil.getArchiveFilters()); + } + + @Override + public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { + // check whether this is an archive + if (ArchiveUtil.isArchive(dataSourcePath)){ + // return "high confidence" value + return 100; + } + return 0; + } + + @Override + public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutoIngestDataSourceProcessorException { + run(deviceId, dataSourcePath.toString(), progressMonitor, callBack); + } + + @Override + public String getDataSourceType() { + return DATA_SOURCE_TYPE; + } + + /** + * Gets the panel that allows a user to select a data source and do any + * configuration required by the data source. The panel is less than 544 + * pixels wide and less than 173 pixels high. + * + * @return A selection and configuration panel for this data source + * processor. + */ + @Override + public JPanel getPanel() { + configPanel.readSettings(); + configPanel.select(); + return configPanel; + } + + /** + * Indicates whether the settings in the selection and configuration panel + * are valid and complete. + * + * @return True if the settings are valid and complete and the processor is + * ready to have its run method called, false otherwise. + */ + @Override + public boolean isPanelValid() { + return configPanel.validatePanel(); + } + + /** + * Adds a data source to the case database using a background task in a + * separate thread and the settings provided by the selection and + * configuration panel. Returns as soon as the background task is started. + * The background task uses a callback object to signal task completion and + * return results. + * + * This method should not be called unless isPanelValid returns true. + * + * @param progressMonitor Progress monitor that will be used by the + * background task to report progress. + * @param callback Callback that will be used by the background task + * to return results. + */ + @Override + public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + if (!setDataSourceOptionsCalled) { + configPanel.storeSettings(); + deviceId = UUID.randomUUID().toString(); + archivePath = configPanel.getContentPaths(); + } + run(deviceId, archivePath, progressMonitor, callback); + } + + /** + * Adds a data source to the case database using a background task in a + * separate thread and the given settings instead of those provided by the + * selection and configuration panel. Returns as soon as the background task + * is started and uses the callback object to signal task completion and + * return results. + * + * @param deviceId An ASCII-printable identifier for the device + * associated with the data source that is + * intended to be unique across multiple cases + * (e.g., a UUID). + * @param archivePath Path to the archive file. + * @param progressMonitor Progress monitor for reporting progress + * during processing. + * @param callback Callback to call when processing is done. + */ + public void run(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + addArchiveTask = new AddArchiveTask(deviceId, archivePath, progressMonitor, callback); + new Thread(addArchiveTask).start(); + } + + /** + * Requests cancellation of the background task that adds a data source to + * the case database, after the task is started using the run method. This + * is a "best effort" cancellation, with no guarantees that the case + * database will be unchanged. If cancellation succeeded, the list of new + * data sources returned by the background task will be empty. + */ + @Override + public void cancel() { + if (null != addArchiveTask) { + addArchiveTask.cancelTask(); + } + } + + @Override + public void reset() { + deviceId = null; + archivePath = null; + configPanel.reset(); + setDataSourceOptionsCalled = false; + } + + /** + * Extracts the contents of a ZIP archive submitted as a data source to a + * subdirectory of the auto ingest module output directory. + * + * @throws IOException if there is a problem extracting the data source from + * the archive. + + private static Path extractDataSource(Path outputDirectoryPath, Path dataSourcePath) throws IOException { + String dataSourceFileNameNoExt = FilenameUtils.removeExtension(dataSourcePath.getFileName().toString()); + Path destinationFolder = Paths.get(outputDirectoryPath.toString(), + AUTO_INGEST_MODULE_OUTPUT_DIR, + dataSourceFileNameNoExt + "_" + TimeStampUtils.createTimeStamp()); + Files.createDirectories(destinationFolder); + + int BUFFER_SIZE = 524288; // Read/write 500KB at a time + File sourceZipFile = dataSourcePath.toFile(); + ZipFile zipFile; + zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ); + Enumeration zipFileEntries = zipFile.entries(); + try { + while (zipFileEntries.hasMoreElements()) { + ZipEntry entry = zipFileEntries.nextElement(); + String currentEntry = entry.getName(); + File destFile = new File(destinationFolder.toString(), currentEntry); + destFile = new File(destinationFolder.toString(), destFile.getName()); + File destinationParent = destFile.getParentFile(); + destinationParent.mkdirs(); + if (!entry.isDirectory()) { + BufferedInputStream is = new BufferedInputStream(zipFile.getInputStream(entry)); + int currentByte; + byte data[] = new byte[BUFFER_SIZE]; + try (FileOutputStream fos = new FileOutputStream(destFile); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) { + currentByte = is.read(data, 0, BUFFER_SIZE); + while (currentByte != -1) { + dest.write(data, 0, currentByte); + currentByte = is.read(data, 0, BUFFER_SIZE); + } + } + } + } + } finally { + zipFile.close(); + } + return destinationFolder; + } */ +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form new file mode 100755 index 0000000000..af9347df0c --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form @@ -0,0 +1,94 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java new file mode 100755 index 0000000000..e5285d9e75 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java @@ -0,0 +1,280 @@ +/* + * 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.experimental.autoingest; + +import java.io.File; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileFilter; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import static org.sleuthkit.autopsy.experimental.autoingest.Bundle.*; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.DriveUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.coreutils.PathValidator; + +/** + * Panel for adding an archive file which is supported by 7zip library (e.g. + * "zip", "rar", "arj", "7z", "7zip", "gzip, etc). Allows the user to select a + * file. + */ +class ArchiveFilePanel extends JPanel implements DocumentListener { + + private static final Logger logger = Logger.getLogger(ArchiveFilePanel.class.getName()); + private static final String PROP_LAST_ARCHIVE_PATH = "LBL_LastImage_PATH"; //NON-NLS + + private final JFileChooser fileChooser = new JFileChooser(); + + /** + * Externally supplied name is used to store settings + */ + private final String contextName; + + /** + * Creates new form ArchiveFilePanel + * + * @param context A string context name used to read/store last + * used settings. + * @param fileChooserFilters A list of filters to be used with the + * FileChooser. + */ + private ArchiveFilePanel(String context, List fileChooserFilters) { + this.contextName = context; + initComponents(); + + errorLabel.setVisible(false); + + fileChooser.setDragEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setMultiSelectionEnabled(false); + fileChooserFilters.forEach(fileChooser::addChoosableFileFilter); + if (fileChooserFilters.isEmpty() == false) { + fileChooser.setFileFilter(fileChooserFilters.get(0)); + } + } + + /** + * Creates and returns an instance of a ArchiveFilePanel. + * + * @param context A string context name used to read/store last + * used settings. + * @param fileChooserFilters A list of filters to be used with the + * FileChooser. + * + * @return instance of the ArchiveFilePanel + */ + public static synchronized ArchiveFilePanel createInstance(String context, List fileChooserFilters) { + ArchiveFilePanel instance = new ArchiveFilePanel(context, fileChooserFilters); + // post-constructor initialization of listener support without leaking references of uninitialized objects + instance.pathTextField.getDocument().addDocumentListener(instance); + return instance; + } + + /** + * 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() { + + pathLabel = new javax.swing.JLabel(); + browseButton = new javax.swing.JButton(); + pathTextField = new javax.swing.JTextField(); + errorLabel = new javax.swing.JLabel(); + + setMinimumSize(new java.awt.Dimension(0, 65)); + setPreferredSize(new java.awt.Dimension(403, 65)); + + org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.browseButton.text")); // NOI18N + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + + pathTextField.setText(org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathTextField.text")); // NOI18N + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.errorLabel.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() + .addComponent(pathTextField) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(browseButton) + .addGap(2, 2, 2)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pathLabel) + .addComponent(errorLabel)) + .addGap(0, 277, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(pathLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(browseButton) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(3, 3, 3) + .addComponent(errorLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + String oldText = getContentPaths(); + // set the current directory of the FileChooser if the ArchivePath Field is valid + File currentDir = new File(oldText); + if (currentDir.exists()) { + fileChooser.setCurrentDirectory(currentDir); + } + + if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String path = fileChooser.getSelectedFile().getPath(); + setContentPath(path); + } + + updateHelper(); + }//GEN-LAST:event_browseButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton browseButton; + private javax.swing.JLabel errorLabel; + private javax.swing.JLabel pathLabel; + private javax.swing.JTextField pathTextField; + // End of variables declaration//GEN-END:variables + + /** + * Get the path of the user selected archive. + * + * @return the archive path + */ + public String getContentPaths() { + return pathTextField.getText(); + } + + /** + * Set the path of the archive file. + * + * @param s path of the archive file + */ + public void setContentPath(String s) { + pathTextField.setText(s); + } + + public void reset() { + //reset the UI elements to default + pathTextField.setText(null); + } + + /** + * Should we enable the next button of the wizard? + * + * @return true if a proper archive has been selected, false otherwise + */ + @NbBundle.Messages("DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on \"C:\" drive") + public boolean validatePanel() { + errorLabel.setVisible(false); + String path = getContentPaths(); + if (StringUtils.isBlank(path)) { + return false; + } + + // display warning if there is one (but don't disable "next" button) + if (false == PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.DataSourceOnCDriveError_text()); + } + + return new File(path).isFile() + || DriveUtils.isPhysicalDrive(path) + || DriveUtils.isPartition(path); + } + + public void storeSettings() { + String archivePathName = getContentPaths(); + if (null != archivePathName) { + String archivePath = archivePathName.substring(0, archivePathName.lastIndexOf(File.separator) + 1); + ModuleSettings.setConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH, archivePath); + } + } + + public void readSettings() { + String lastArchivePath = ModuleSettings.getConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH); + if (StringUtils.isNotBlank(lastArchivePath)) { + setContentPath(lastArchivePath); + } + } + + @Override + public void insertUpdate(DocumentEvent e) { + updateHelper(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + updateHelper(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + updateHelper(); + } + + /** + * Update functions are called by the pathTextField which has this set as + * it's DocumentEventListener. Each update function fires a property change + * to be caught by the parent panel. + * + */ + @NbBundle.Messages({"ArchiveFilePanel.moduleErr=Module Error", + "ArchiveFilePanel.moduleErr.msg=A module caused an error listening to ArchiveFilePanel updates." + + " See log to determine which module. Some data could be incomplete.\n"}) + private void updateHelper() { + try { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } catch (Exception e) { + logger.log(Level.SEVERE, "ArchiveFilePanel listener threw exception", e); //NON-NLS + MessageNotifyUtil.Notify.error(ArchiveFilePanel_moduleErr(), ArchiveFilePanel_moduleErr_msg()); + } + } + + /** + * Set the focus to the pathTextField. + */ + public void select() { + pathTextField.requestFocusInWindow(); + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java index 9683deb5c6..b7a2926092 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java @@ -26,7 +26,9 @@ import java.io.RandomAccessFile; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import javax.swing.filechooser.FileFilter; import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.SevenZip; @@ -35,18 +37,46 @@ import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; import net.sf.sevenzipjbinding.simple.ISimpleInArchive; import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.GeneralFilter; /** * Set of utilities that handles archive file extraction. Uses 7zip library. */ final class ArchiveUtil { - static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS - + private static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS + private static final List ARCHIVE_EXTS = Arrays.asList(".zip", ".rar", ".arj", ".7z", ".7zip", ".gzip", ".gz", ".bzip2", ".tar", ".tgz"); //NON-NLS + @NbBundle.Messages("GeneralFilter.archiveDesc.text=Archive Files (.zip, .rar, .arj, .7z, .7zip, .gzip, .gz, .bzip2, .tar, .tgz)") + private static final String ARCHIVE_DESC = Bundle.GeneralFilter_archiveDesc_text(); + private static final GeneralFilter SEVEN_ZIP_FILTER = new GeneralFilter(ARCHIVE_EXTS, ARCHIVE_DESC); + private static final List ARCHIVE_FILTERS = new ArrayList<>(); + static { + ARCHIVE_FILTERS.add(SEVEN_ZIP_FILTER); + } private ArchiveUtil() { } + static List getArchiveFilters() { + return ARCHIVE_FILTERS; + } + + static boolean isArchive(Path dataSourcePath) { + String fileName = dataSourcePath.getFileName().toString(); + // check whether it's a zip archive file that can be extracted + return isAcceptedByFiler(new File(fileName), ARCHIVE_FILTERS); + } + + private static boolean isAcceptedByFiler(File file, List filters) { + for (FileFilter filter : filters) { + if (filter.accept(file)) { + return true; + } + } + return false; + } + /** * Enum of mime types which support archive extraction */ diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index c5f43a8f1f..2f2f22fb0d 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -37,7 +37,6 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.EnumSet; @@ -59,9 +58,6 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.ThreadSafe; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case.CaseType; @@ -101,7 +97,6 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobStartResult; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestModuleError; -import org.sleuthkit.datamodel.Content; /** * An auto ingest manager is responsible for processing auto ingest jobs defined @@ -1454,7 +1449,6 @@ public final class AutoIngestManager extends Observable implements PropertyChang */ private final class JobProcessingTask implements Runnable { - private static final String AUTO_INGEST_MODULE_OUTPUT_DIR = "AutoIngest"; private final Object ingestLock; private final Object pauseLock; @GuardedBy("pauseLock") @@ -2217,7 +2211,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang return; } - DataSource dataSource = identifyDataSource(caseForJob); + DataSource dataSource = identifyDataSource(); if (null == dataSource) { currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now())); return; @@ -2270,7 +2264,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang * interrupted while blocked, i.e., * if auto ingest is shutting down. */ - private DataSource identifyDataSource(Case caseForJob) throws AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException { + private DataSource identifyDataSource() throws AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException { Manifest manifest = currentJob.getManifest(); Path manifestPath = manifest.getFilePath(); SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath); @@ -2289,7 +2283,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang String deviceId = manifest.getDeviceId(); return new DataSource(deviceId, dataSourcePath); } - + /** * Passes the data source for the current job through a data source * processor that adds it to the case database. @@ -2312,28 +2306,21 @@ public final class AutoIngestManager extends Observable implements PropertyChang SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath); currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE, Date.from(Instant.now())); UUID taskId = UUID.randomUUID(); - DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId); + DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock); DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor(); Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath); try { caseForJob.notifyAddingDataSource(taskId); - // lookup all AutomatedIngestDataSourceProcessors - Collection processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); - - Map validDataSourceProcessorsMap = new HashMap<>(); - for (AutoIngestDataSourceProcessor processor : processorCandidates) { - try { - int confidence = processor.canProcess(dataSource.getPath()); - if (confidence > 0) { - validDataSourceProcessorsMap.put(processor, confidence); - } - } catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) { - SYS_LOGGER.log(Level.SEVERE, "Exception while determining whether data source processor {0} can process {1}", new Object[]{processor.getDataSourceType(), dataSource.getPath()}); - // rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause. - throw ex; - } + Map validDataSourceProcessorsMap; + try { + // lookup all AutomatedIngestDataSourceProcessors and poll which ones are able to process the current data source + validDataSourceProcessorsMap = DataSourceProcessorUtility.getDataSourceProcessor(dataSource.getPath()); + } catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) { + SYS_LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath()); + // rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause. + throw ex; } // did we find a data source processor that can process the data source @@ -2590,80 +2577,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang jobLogger.logFileExportError(); } } - - /** - * A "callback" that collects the results of running a data source - * processor on a data source and unblocks the job processing thread - * when the data source processor finishes running in its own thread. - */ - @Immutable - class AddDataSourceCallback extends DataSourceProcessorCallback { - - private final Case caseForJob; - private final DataSource dataSourceInfo; - private final UUID taskId; - - /** - * Constructs a "callback" that collects the results of running a - * data source processor on a data source and unblocks the job - * processing thread when the data source processor finishes running - * in its own thread. - * - * @param caseForJob The case for the current job. - * @param dataSourceInfo The data source - * @param taskId The task id to associate with ingest job - * events. - */ - AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId) { - this.caseForJob = caseForJob; - this.dataSourceInfo = dataSourceInfo; - this.taskId = taskId; - } - - /** - * Called by the data source processor when it finishes running in - * its own thread. - * - * @param result The result code for the processing of - * the data source. - * @param errorMessages Any error messages generated during the - * processing of the data source. - * @param dataSourceContent The content produced by processing the - * data source. - */ - @Override - public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSourceContent) { - if (!dataSourceContent.isEmpty()) { - caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId); - } else { - caseForJob.notifyFailedAddingDataSource(taskId); - } - dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent); - dataSourceContent.addAll(dataSourceContent); - synchronized (ingestLock) { - ingestLock.notify(); - } - } - - /** - * Called by the data source processor when it finishes running in - * its own thread, if that thread is the AWT (Abstract Window - * Toolkit) event dispatch thread (EDT). - * - * @param result The result code for the processing of - * the data source. - * @param errorMessages Any error messages generated during the - * processing of the data source. - * @param dataSourceContent The content produced by processing the - * data source. - */ - @Override - public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSources) { - done(result, errorMessages, dataSources); - } - - } - + /** * A data source processor progress monitor does nothing. There is * currently no mechanism for showing or recording data source processor @@ -3003,49 +2917,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang PARTIALLY_DELETED, FULLY_DELETED } - - @ThreadSafe - private static final class DataSource { - - private final String deviceId; - private final Path path; - private DataSourceProcessorResult resultCode; - private List errorMessages; - private List content; - - DataSource(String deviceId, Path path) { - this.deviceId = deviceId; - this.path = path; - } - - String getDeviceId() { - return deviceId; - } - - Path getPath() { - return this.path; - } - - synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List errorMessages, List content) { - this.resultCode = result; - this.errorMessages = new ArrayList<>(errorMessages); - this.content = new ArrayList<>(content); - } - - synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { - return resultCode; - } - - synchronized List getDataSourceProcessorErrorMessages() { - return new ArrayList<>(errorMessages); - } - - synchronized List getContent() { - return new ArrayList<>(content); - } - - } - + static final class AutoIngestManagerException extends Exception { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index 422c7a56c4..edac418672 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -247,3 +247,7 @@ AutoIngestDashboard.prioritizeJobButton.toolTipText=Move the selected job to the AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue. AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case +ArchiveFilePanel.pathLabel.text=Browse for an archive file: +ArchiveFilePanel.browseButton.text=Browse +ArchiveFilePanel.pathTextField.text= +ArchiveFilePanel.errorLabel.text=Error Label \ No newline at end of file diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSource.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSource.java new file mode 100755 index 0000000000..db9d3d5ad9 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSource.java @@ -0,0 +1,68 @@ +/* + * 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.experimental.autoingest; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.concurrent.ThreadSafe; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; +import org.sleuthkit.datamodel.Content; + +@ThreadSafe +class DataSource { + + private final String deviceId; + private final Path path; + private DataSourceProcessorResult resultCode; + private List errorMessages; + private List content; + + DataSource(String deviceId, Path path) { + this.deviceId = deviceId; + this.path = path; + } + + String getDeviceId() { + return deviceId; + } + + Path getPath() { + return this.path; + } + + synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List errorMessages, List content) { + this.resultCode = result; + this.errorMessages = new ArrayList<>(errorMessages); + this.content = new ArrayList<>(content); + } + + synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { + return resultCode; + } + + synchronized List getDataSourceProcessorErrorMessages() { + return new ArrayList<>(errorMessages); + } + + synchronized List getContent() { + return new ArrayList<>(content); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java new file mode 100755 index 0000000000..6f88d70a28 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java @@ -0,0 +1,63 @@ +/* + * 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.experimental.autoingest; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; + +/** + * A utility class to find Data Source Processors + */ +class DataSourceProcessorUtility { + + private DataSourceProcessorUtility() { + } + + /** + * A utility method to find all Data Source Processors (DSP) that are able + * to process the input data source. Only the DSPs that implement + * AutoIngestDataSourceProcessor interface are used. + * + * @param dataSourcePath Full path to the data source + * @return Hash map of all DSPs that can process the data source along with + * their confidence score + * @throws + * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException + */ + static Map getDataSourceProcessor(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { + + // lookup all AutomatedIngestDataSourceProcessors + Collection processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); + + Map validDataSourceProcessorsMap = new HashMap<>(); + for (AutoIngestDataSourceProcessor processor : processorCandidates) { + int confidence = processor.canProcess(dataSourcePath); + if (confidence > 0) { + validDataSourceProcessorsMap.put(processor, confidence); + } + } + + return validDataSourceProcessorsMap; + } +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index ba2de01a6c..74c28d55b8 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -236,9 +236,7 @@ public final class ImageGalleryController { undoManager.clear(); }); - regroupDisabled.addListener((Observable observable) -> { - checkForGroups(); - }); + regroupDisabled.addListener(observable -> checkForGroups()); IngestManager ingestManager = IngestManager.getInstance(); PropertyChangeListener ingestEventHandler = diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java index 1e914522ac..3ef949c9c9 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java @@ -128,9 +128,7 @@ public class DrawableGroup implements Comparable { LOGGER.log(Level.WARNING, "could not access case during getFilesWithHashSetHitsCount()"); //NON-NLS } } - return hashSetHitsCount.get(); - } public ReadOnlyLongProperty hashSetHitsCountProperty() { @@ -226,7 +224,6 @@ public class DrawableGroup implements Comparable { // By default, sort by group key name @Override public int compareTo(DrawableGroup other) { - return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName()); + return getGroupByValueDislpayName().compareTo(other.getGroupByValueDislpayName()); } - } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index ae9e17bb4a..5c02f5abd1 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -122,7 +122,7 @@ public class GroupManager { /* * --- current grouping/sorting attributes --- */ - private volatile GroupSortBy sortBy = GroupSortBy.NONE; + private volatile GroupSortBy sortBy = GroupSortBy.PRIORITY; private volatile DrawableAttribute groupBy = DrawableAttribute.PATH; private volatile SortOrder sortOrder = SortOrder.ASCENDING; @@ -156,7 +156,6 @@ public class GroupManager { */ public GroupManager(ImageGalleryController controller) { this.controller = controller; - } /** @@ -188,7 +187,7 @@ public class GroupManager { * the groups the given file is a part of * * @return a a set of {@link GroupKey}s representing the group(s) the given - * file is a part of + * file is a part of */ synchronized public Set> getGroupKeysForFileID(Long fileID) { try { @@ -208,7 +207,7 @@ public class GroupManager { * @param groupKey * * @return return the DrawableGroup (if it exists) for the given GroupKey, - * or null if no group exists for that key. + * or null if no group exists for that key. */ @Nullable public DrawableGroup getGroupForKey(@Nonnull GroupKey groupKey) { @@ -284,7 +283,7 @@ public class GroupManager { * no-op * * @param groupKey the value of groupKey - * @param fileID the value of file + * @param fileID the value of file */ public synchronized DrawableGroup removeFromGroup(GroupKey groupKey, final Long fileID) { //get grouping this file would be in @@ -466,7 +465,7 @@ public class GroupManager { } } - public Comparator getSortBy() { + public GroupSortBy getSortBy() { return sortBy; } @@ -512,7 +511,7 @@ public class GroupManager { * @param groupBy * @param sortBy * @param sortOrder - * @param force true to force a full db query regroup + * @param force true to force a full db query regroup */ public synchronized > void regroup(final DrawableAttribute groupBy, final GroupSortBy sortBy, final SortOrder sortOrder, Boolean force) { @@ -530,9 +529,7 @@ public class GroupManager { } groupByTask = new ReGroupTask<>(groupBy, sortBy, sortOrder); - Platform.runLater(() -> { - regroupProgress.bind(groupByTask.progressProperty()); - }); + Platform.runLater(() -> regroupProgress.bind(groupByTask.progressProperty())); regroupExecutor.submit(groupByTask); } else { // resort the list of groups @@ -583,10 +580,7 @@ public class GroupManager { DrawableGroup group = g; if (group != null) { //if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it. - Platform.runLater(() -> { - group.addFile(fileID); - }); - + Platform.runLater(() -> group.addFile(fileID)); } } @@ -682,9 +676,9 @@ public class GroupManager { } else { group = new DrawableGroup(groupKey, fileIDs, groupSeen); controller.getCategoryManager().registerListener(group); - group.seenProperty().addListener((o, oldSeen, newSeen) -> { - Platform.runLater(() -> markGroupSeen(group, newSeen)); - }); + group.seenProperty().addListener((o, oldSeen, newSeen) -> + Platform.runLater(() -> markGroupSeen(group, newSeen)) + ); groupMap.put(groupKey, group); } } @@ -693,7 +687,7 @@ public class GroupManager { analyzedGroups.add(group); if (Objects.isNull(task)) { FXCollections.sort(analyzedGroups, applySortOrder(sortOrder, sortBy)); - } + } } markGroupSeen(group, groupSeen); }); @@ -743,17 +737,17 @@ public class GroupManager { "# {0} - groupBy attribute Name", "# {1} - atribute value", "ReGroupTask.progressUpdate=regrouping files by {0} : {1}"}) - private class ReGroupTask> extends LoggedTask { + private class ReGroupTask> extends LoggedTask { private ProgressHandle groupProgress; - private final DrawableAttribute groupBy; + private final DrawableAttribute groupBy; private final GroupSortBy sortBy; private final SortOrder sortOrder; - ReGroupTask(DrawableAttribute groupBy, GroupSortBy sortBy, SortOrder sortOrder) { + ReGroupTask(DrawableAttribute groupBy, GroupSortBy sortBy, SortOrder sortOrder) { super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), true); this.groupBy = groupBy; @@ -780,13 +774,13 @@ public class GroupManager { }); // Get the list of group keys - final List vals = findValuesForAttribute(groupBy); + final List vals = findValuesForAttribute(groupBy); groupProgress.start(vals.size()); int p = 0; // For each key value, partially create the group and add it to the list. - for (final A val : vals) { + for (final AttrType val : vals) { if (isCancelled()) { return null;//abort } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java index 20d47e2952..65e1870de8 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java @@ -37,24 +37,34 @@ public class GroupSortBy implements Comparator { /** * sort the groups by the number of files in each */ - public final static GroupSortBy FILE_COUNT = new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png", Comparator.comparing(DrawableGroup::getSize)); + public final static GroupSortBy FILE_COUNT = + new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png", + Comparator.comparing(DrawableGroup::getSize)); /** * sort the groups by the natural order of the grouping value ( eg group * them by path alphabetically ) */ - public final static GroupSortBy GROUP_BY_VALUE = new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png", Comparator.comparing(DrawableGroup::getGroupByValueDislpayName)); + public final static GroupSortBy GROUP_BY_VALUE = + new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png", + Comparator.comparing(DrawableGroup::getGroupByValueDislpayName)); /** * don't sort the groups just use what ever order they come in (ingest * order) */ - public final static GroupSortBy NONE = new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png", new AllEqualComparator<>()); + public final static GroupSortBy NONE = + new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png", + new AllEqualComparator<>()); /** * sort the groups by some priority metric to be determined and implemented */ - public final static GroupSortBy PRIORITY = new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png", Comparator.comparing(DrawableGroup::getHashHitDensity).thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount)).reversed()); + public final static GroupSortBy PRIORITY = + new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png", + Comparator.comparing(DrawableGroup::getHashHitDensity) + .thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount)) + .reversed()); @Override public int compare(DrawableGroup o1, DrawableGroup o2) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java index 2dc85cbfd9..bdc43c1c06 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java @@ -180,8 +180,8 @@ public class Toolbar extends ToolBar { sortChooser = new SortChooser<>(GroupSortBy.getValues()); sortChooser.comparatorProperty().addListener((observable, oldComparator, newComparator) -> { - final boolean orderEnabled = newComparator == GroupSortBy.NONE || newComparator == GroupSortBy.PRIORITY; - sortChooser.setSortOrderDisabled(orderEnabled); + final boolean orderDisabled = newComparator == GroupSortBy.NONE || newComparator == GroupSortBy.PRIORITY; + sortChooser.setSortOrderDisabled(orderDisabled); final SortChooser.ValueType valueType = newComparator == GroupSortBy.GROUP_BY_VALUE ? SortChooser.ValueType.LEXICOGRAPHIC : SortChooser.ValueType.NUMERIC; sortChooser.setValueType(valueType); @@ -189,7 +189,7 @@ public class Toolbar extends ToolBar { }); sortChooser.sortOrderProperty().addListener(queryInvalidationListener); - sortChooser.setComparator(GroupSortBy.PRIORITY); + sortChooser.setComparator(controller.getGroupManager().getSortBy()); getItems().add(1, sortChooser); sortHelpImageView.setCursor(Cursor.HAND); diff --git a/KeywordSearch/build.xml b/KeywordSearch/build.xml index d02cb92727..5fe021fff8 100755 --- a/KeywordSearch/build.xml +++ b/KeywordSearch/build.xml @@ -6,8 +6,21 @@ Builds, tests, and runs the project org.sleuthkit.autopsy.keywordsearch. + + + + - + + + + + + + + + + @@ -39,5 +52,12 @@ - + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java index cb7aac9018..e28cd75654 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java @@ -138,12 +138,14 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option @Override public void addPropertyChangeListener(PropertyChangeListener l) { + super.addPropertyChangeListener(l); listsManagementPanel.addPropertyChangeListener(l); editListPanel.addPropertyChangeListener(l); } @Override public void removePropertyChangeListener(PropertyChangeListener l) { + super.removePropertyChangeListener(l); listsManagementPanel.removePropertyChangeListener(l); editListPanel.removePropertyChangeListener(l); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java index 1756bfc1ec..8e820b4c74 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.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"); @@ -28,11 +28,12 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; */ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { - private GlobalListSettingsPanel listsPanel; - private KeywordSearchGlobalLanguageSettingsPanel languagesPanel; - private KeywordSearchGlobalSearchSettingsPanel generalPanel; + private static final long serialVersionUID = 1L; + private final GlobalListSettingsPanel listsPanel = new GlobalListSettingsPanel(); + private final KeywordSearchGlobalLanguageSettingsPanel languagesPanel = new KeywordSearchGlobalLanguageSettingsPanel(); + private final KeywordSearchGlobalSearchSettingsPanel generalPanel = new KeywordSearchGlobalSearchSettingsPanel(); - public KeywordSearchGlobalSettingsPanel() { + KeywordSearchGlobalSettingsPanel() { initComponents(); customizeComponents(); } @@ -40,9 +41,6 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP @NbBundle.Messages({"KeywordSearchGlobalSettingsPanel.Title=Global Keyword Search Settings"}) private void customizeComponents() { setName(Bundle.KeywordSearchGlobalSettingsPanel_Title()); - listsPanel = new GlobalListSettingsPanel(); - languagesPanel = new KeywordSearchGlobalLanguageSettingsPanel(); - generalPanel = new KeywordSearchGlobalSearchSettingsPanel(); tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listTabTitle"), null, listsPanel, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listLabToolTip"), 0); tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.stringExtTitle"), null, @@ -53,6 +51,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP @Override public void addPropertyChangeListener(PropertyChangeListener l) { + super.addPropertyChangeListener(l); listsPanel.addPropertyChangeListener(l); languagesPanel.addPropertyChangeListener(l); generalPanel.addPropertyChangeListener(l); @@ -60,6 +59,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP @Override public void removePropertyChangeListener(PropertyChangeListener l) { + super.removePropertyChangeListener(l); listsPanel.removePropertyChangeListener(l); languagesPanel.removePropertyChangeListener(l); generalPanel.removePropertyChangeListener(l); diff --git a/KeywordSearch/test/unit/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTestSuite.java b/KeywordSearch/test/unit/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTestSuite.java new file mode 100755 index 0000000000..373a2eedd1 --- /dev/null +++ b/KeywordSearch/test/unit/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTestSuite.java @@ -0,0 +1,54 @@ +/* + * 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.keywordsearch; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({CreditCardValidatorTest.class}) +public class KeywordSearchTestSuite { + public KeywordSearchTestSuite() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Ignore + public static void main(String[] args) { + } +}