Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 3126-IngestEventsListenerChanges

This commit is contained in:
William Schaefer 2017-10-30 20:24:48 -04:00
commit b557bd8dca
40 changed files with 1393 additions and 265 deletions

View File

@ -31,9 +31,20 @@ import javax.swing.JComponent;
abstract class AbstractFileSearchFilter<T extends JComponent> implements FileSearchFilter { abstract class AbstractFileSearchFilter<T extends JComponent> implements FileSearchFilter {
final private T component; final private T component;
private String lastErrorMessage;
AbstractFileSearchFilter(T component) { AbstractFileSearchFilter(T component) {
this.component = component; this.component = component;
this.lastErrorMessage = "";
}
void setLastError(String mes){
lastErrorMessage = mes;
}
@Override
public String getLastError(){
return this.lastErrorMessage;
} }
@Override @Override

View File

@ -57,4 +57,5 @@ MimeTypePanel.jLabel1.text=*Note: Multiple MIME types can be selected
FileSearchPanel.searchButton.text=Search FileSearchPanel.searchButton.text=Search
MimeTypePanel.mimeTypeCheckBox.text=MIME Type: MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
HashSearchPanel.md5CheckBox.text=MD5: HashSearchPanel.md5CheckBox.text=MD5:
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search. HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
FileSearchPanel.errorLabel.text=\

View File

@ -42,6 +42,7 @@ import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.openide.util.NbBundle.Messages;
/** /**
* Filters file date properties (modified/created/etc.. times) * Filters file date properties (modified/created/etc.. times)
@ -79,25 +80,10 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
String query = "NULL"; String query = "NULL";
DateSearchPanel panel = this.getComponent(); 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 // convert the date from the selected timezone to get the GMT
long fromDate = 0; long fromDate = 0;
String startDateValue = panel.getDateFromTextField().getText(); String startDateValue = panel.getDateFromTextField().getText();
Calendar startDate = null; Calendar startDate = getCalendarDate(startDateValue);
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
}
if (!startDateValue.isEmpty()) { if (!startDateValue.isEmpty()) {
if (startDate != null) { if (startDate != null) {
fromDate = startDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds fromDate = startDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds
@ -106,31 +92,13 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
long toDate = 0; long toDate = 0;
String endDateValue = panel.getDateToTextField().getText(); String endDateValue = panel.getDateToTextField().getText();
Calendar endDate = null; Calendar endDate = getCalendarDate(endDateValue);
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
}
if (!endDateValue.isEmpty()) { if (!endDateValue.isEmpty()) {
if (endDate != null) { if (endDate != null) {
toDate = endDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds 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 modifiedChecked = panel.getModifiedCheckBox().isSelected();
final boolean changedChecked = panel.getChangedCheckBox().isSelected(); final boolean changedChecked = panel.getChangedCheckBox().isSelected();
final boolean accessedChecked = panel.getAccessedCheckBox().isSelected(); final boolean accessedChecked = panel.getAccessedCheckBox().isSelected();
@ -206,14 +174,56 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
return timeZones; 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 @Override
public void addActionListener(ActionListener l) { public void addActionListener(ActionListener l) {
getComponent().addActionListener(l); getComponent().addActionListener(l);
} }
@Override @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() { 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;
} }
/** /**

View File

@ -149,6 +149,7 @@
<SubComponents> <SubComponents>
<Component class="javax.swing.JFormattedTextField" name="dateToTextField"> <Component class="javax.swing.JFormattedTextField" name="dateToTextField">
<Properties> <Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.dateToTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.dateToTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -208,6 +209,7 @@
</Component> </Component>
<Component class="javax.swing.JFormattedTextField" name="dateFromTextField"> <Component class="javax.swing.JFormattedTextField" name="dateFromTextField">
<Properties> <Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.dateFromTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.dateFromTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>

View File

@ -29,6 +29,8 @@ import javax.swing.JComboBox;
import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/** /**
* Subpanel with controls for file data filtering. * Subpanel with controls for file data filtering.
@ -50,6 +52,7 @@ class DateSearchPanel extends javax.swing.JPanel {
dateFromTextField.setComponentPopupMenu(rightClickMenu); dateFromTextField.setComponentPopupMenu(rightClickMenu);
dateToTextField.setComponentPopupMenu(rightClickMenu); dateToTextField.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() { ActionListener actList = new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -74,6 +77,41 @@ class DateSearchPanel extends javax.swing.JPanel {
copyMenuItem.addActionListener(actList); copyMenuItem.addActionListener(actList);
pasteMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList);
selectAllMenuItem.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(); 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 selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.selectAllMenuItem.text")); // NOI18N
rightClickMenu.add(selectAllMenuItem); rightClickMenu.add(selectAllMenuItem);
dateToTextField.setEditable(false);
dateToTextField.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateToTextField.text")); // NOI18N dateToTextField.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateToTextField.text")); // NOI18N
dateToTextField.addFocusListener(new java.awt.event.FocusAdapter() { dateToTextField.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) { 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.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
jLabel3.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.jLabel3.text")); // 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.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateFromTextField.text")); // NOI18N
dateFromTextField.addFocusListener(new java.awt.event.FocusAdapter() { dateFromTextField.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) { public void focusLost(java.awt.event.FocusEvent evt) {
@ -365,6 +405,7 @@ class DateSearchPanel extends javax.swing.JPanel {
if (evt.getNewValue() instanceof Date) { if (evt.getNewValue() instanceof Date) {
setToDate((Date) evt.getNewValue()); setToDate((Date) evt.getNewValue());
} }
}//GEN-LAST:event_dateToPopupChanged }//GEN-LAST:event_dateToPopupChanged
private void dateCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dateCheckBoxActionPerformed 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) { if (date != null) {
dateStringResult = dateFormat.format(date); dateStringResult = dateFormat.format(date);
} }
dateFromTextField.setText(dateStringResult); dateFromTextField.setText(dateStringResult);
dateFromButtonCalendar.setTargetDate(date); dateFromButtonCalendar.setTargetDate(date);
} }

View File

@ -47,6 +47,13 @@ interface FileSearchFilter {
* @return Whether the panel has valid input for search. * @return Whether the panel has valid input for search.
*/ */
boolean isValid(); 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 * Gets predicate expression to include in the SQL filter expression

View File

@ -23,7 +23,9 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="filterPanel" alignment="0" max="32767" attributes="0"/> <Component id="filterPanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="errorLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/> <Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -32,9 +34,14 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="filterPanel" max="32767" attributes="0"/> <Component id="filterPanel" pref="266" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <Group type="103" groupAlignment="1" attributes="0">
<Component id="searchButton" min="-2" max="-2" attributes="0"/> <Component id="searchButton" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -63,6 +70,19 @@
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="FileSearchPanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="FileSearchPanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="33" green="33" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="FileSearchPanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -27,7 +27,6 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; 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()); searchButton.setEnabled(isValidSearch());
} }
@ -130,11 +123,13 @@ class FileSearchPanel extends javax.swing.JPanel {
if (filter.isEnabled()) { if (filter.isEnabled()) {
enabled = true; enabled = true;
if (!filter.isValid()) { if (!filter.isValid()) {
errorLabel.setText(filter.getLastError());
return false; return false;
} }
} }
} }
errorLabel.setText("");
return enabled; return enabled;
} }
@ -280,6 +275,7 @@ class FileSearchPanel extends javax.swing.JPanel {
filterPanel = new javax.swing.JPanel(); filterPanel = new javax.swing.JPanel();
searchButton = new javax.swing.JButton(); searchButton = new javax.swing.JButton();
errorLabel = new javax.swing.JLabel();
setPreferredSize(new java.awt.Dimension(300, 300)); 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)); 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.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); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
@ -295,21 +299,31 @@ class FileSearchPanel extends javax.swing.JPanel {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .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) .addComponent(searchButton)
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
.addGap(0, 0, 0) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(searchButton) .addComponent(searchButton)
.addGroup(layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(errorLabel)))
.addContainerGap()) .addContainerGap())
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel errorLabel;
private javax.swing.JPanel filterPanel; private javax.swing.JPanel filterPanel;
private javax.swing.JButton searchButton; private javax.swing.JButton searchButton;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
/** /**
@ -60,7 +61,25 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
} }
@Override @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() { 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;
} }
} }

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.datamodel.TskData.FileKnown; import org.sleuthkit.datamodel.TskData.FileKnown;
/** /**
@ -85,7 +86,14 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter<KnownStatusSearch
} }
@Override @Override
@Messages ({
"KnownStatusSearchFilter.errorMessage.noKnownStatusCheckboxSelected=At least one known status checkbox must be selected."
})
public boolean isValid() { public boolean isValid() {
return this.getComponent().isValidSearch(); if (!this.getComponent().isValidSearch()) {
setLastError(Bundle.KnownStatusSearchFilter_errorMessage_noKnownStatusCheckboxSelected());
return false;
}
return true;
} }
} }

View File

@ -6,6 +6,7 @@
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle.Messages;
/** /**
* Filter by mime type used in filter areas of file search by attribute. * Filter by mime type used in filter areas of file search by attribute.
@ -42,7 +43,14 @@ class MimeTypeFilter extends AbstractFileSearchFilter<MimeTypePanel> {
} }
@Override @Override
@Messages ({
"MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected."
})
public boolean isValid() { public boolean isValid() {
return !this.getComponent().getMimeTypesSelected().isEmpty(); if(this.getComponent().getMimeTypesSelected().isEmpty()){
setLastError(Bundle.MimeTypeFilter_errorMessage_emptyMimeType());
return false;
}
return true;
} }
} }

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
/** /**
@ -64,7 +65,14 @@ class NameSearchFilter extends AbstractFileSearchFilter<NameSearchPanel> {
} }
@Override @Override
@Messages ({
"NameSearchFilter.errorMessage.emtpyName=Please input a name to search."
})
public boolean isValid() { public boolean isValid() {
return !this.getComponent().getSearchTextField().getText().isEmpty(); if(this.getComponent().getSearchTextField().getText().isEmpty()) {
setLastError(Bundle.NameSearchFilter_errorMessage_emtpyName());
return false;
}
return true;
} }
} }

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
/** /**
@ -74,7 +75,23 @@ class SizeSearchFilter extends AbstractFileSearchFilter<SizeSearchPanel> {
} }
@Override @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() { 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; return true;
} }
} }

View File

@ -25,6 +25,8 @@ import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField;
import javax.swing.JMenuItem; 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); copyMenuItem.addActionListener(actList);
pasteMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList);
selectAllMenuItem.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);
}
});
} }

View File

@ -192,8 +192,6 @@ public final class IngestMonitor {
} }
logMemoryUsage(); logMemoryUsage();
logDiskSpaceUsage();
if (!enoughDiskSpace()) { if (!enoughDiskSpace()) {
/* /*
* Shut down ingest by cancelling all ingest jobs. * Shut down ingest by cancelling all ingest jobs.

View File

@ -33,7 +33,8 @@ import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile;
*/ */
class ProfilePanel extends IngestModuleGlobalSettingsPanel { 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.profileNameLabel.text=Profile Name:",
"ProfilePanel.newProfileText=NewEmptyProfile", "ProfilePanel.newProfileText=NewEmptyProfile",
"ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.", "ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.",
@ -50,6 +51,7 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel {
*/ */
ProfilePanel() { ProfilePanel() {
initComponents(); initComponents();
setName(org.openide.util.NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.title.text"));
settings = new IngestJobSettings(NEW_PROFILE_NAME); settings = new IngestJobSettings(NEW_PROFILE_NAME);
ingestSettingsPanel = new IngestJobSettingsPanel(settings); ingestSettingsPanel = new IngestJobSettingsPanel(settings);
ingestSettingsPanel.setPastJobsButtonVisible(false); ingestSettingsPanel.setPastJobsButtonVisible(false);
@ -59,6 +61,7 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel {
ProfilePanel(IngestProfile selectedProfile) { ProfilePanel(IngestProfile selectedProfile) {
initComponents(); initComponents();
setName(org.openide.util.NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.title.text"));
profile = selectedProfile; profile = selectedProfile;
profileDescArea.setText(profile.getDescription()); profileDescArea.setText(profile.getDescription());
profileNameField.setText(profile.getName()); profileNameField.setText(profile.getName());

View File

@ -4,8 +4,6 @@ OpenIDE-Module-Short-Description=Interesting Files Identifier ingest module.
OpenIDE-Module-Name=Interesting Files Identifier OpenIDE-Module-Name=Interesting Files Identifier
OptionsCategory_Name_InterestingItemDefinitions=Interesting Files OptionsCategory_Name_InterestingItemDefinitions=Interesting Files
OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions
OptionsCategory_Name_FileIngestFilterDefinitions=File Ingest Filter
OptionsCategory_Keywords_FileIngestFilterDefinitions=FileIngestFilterDefinitions
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets. InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
FilesSetPanel.interesting.title=Interesting Files Set FilesSetPanel.interesting.title=Interesting Files Set
@ -45,7 +43,7 @@ FilesSetRulePanel.fileSizeCheck.text=File Size:
FilesSetRulePanel.filesRadioButton.text=Files FilesSetRulePanel.filesRadioButton.text=Files
FilesSetRulePanel.dirsRadioButton.text=Directories FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets: 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.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.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 FilesSetDefsPanel.interesting.editSetButton.text=Edit Set

View File

@ -77,6 +77,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
private final JButton okButton = new JButton("OK"); private final JButton okButton = new JButton("OK");
private final JButton cancelButton = new JButton("Cancel"); private final JButton cancelButton = new JButton("Cancel");
private final PANEL_TYPE panelType; private final PANEL_TYPE panelType;
private final String filterDialogTitle;
private final String ruleDialogTitle; private final String ruleDialogTitle;
private boolean canBeEnabled = true; private boolean canBeEnabled = true;
@ -109,7 +110,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.jLabel7.setVisible(false); this.jLabel7.setVisible(false);
this.fileSizeUnitComboBox.setVisible(false); this.fileSizeUnitComboBox.setVisible(false);
this.fileSizeSpinner.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.jLabel8.setVisible(false);
this.equalitySignComboBox.setVisible(false); this.equalitySignComboBox.setVisible(false);
this.ignoreKnownFilesCheckbox.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(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 org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.ingest.jLabel6.text")); // NOI18N
} else { } else {
this.filterDialogTitle = "FilesSetPanel.interesting.title";
this.ruleDialogTitle = "FilesSetPanel.interesting.title"; this.ruleDialogTitle = "FilesSetPanel.interesting.title";
this.ingoreUnallocCheckbox.setVisible(false); this.ingoreUnallocCheckbox.setVisible(false);
} }
} }
@NbBundle.Messages({"FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings", @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() { private void customInit() {
if (panelType == PANEL_TYPE.FILE_INGEST_FILTERS) { if (panelType == PANEL_TYPE.FILE_INGEST_FILTERS) {
setName(Bundle.FilesSetDefsPanel_Ingest_Title()); setName(Bundle.FilesSetDefsPanel_Ingest_Title());
@ -408,7 +411,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// feedback when isValidDefinition() is called. // feedback when isValidDefinition() is called.
int option = JOptionPane.OK_OPTION; int option = JOptionPane.OK_OPTION;
do { 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 (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. // While adding new ruleset(selectedSet == null), if rule set with same name already exists, do not add to the filesSets hashMap.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-2016 Basis Technology Corp. * Copyright 2014-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,7 +29,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TY
*/ */
public class FilesSetPanel extends javax.swing.JPanel { 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 static final String CREATE_NEW_FILE_INGEST_FILTER = Bundle.FilesSetPanel_ingest_createNewFilter();
private final String mustBeNamedErrorText; private final String mustBeNamedErrorText;

View File

@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -174,7 +175,14 @@ class TableReportGenerator {
* does not require a artifact name, so we make a synthetic * does not require a artifact name, so we make a synthetic
* compund name by appending a ":" and the account type. * 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); writeTableForDataType(new ArrayList<>(groupedArtifacts.get(accountType)), type, compundDataTypeName, comment);
} }
} else { } else {

View File

@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettingsPanel; import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettingsPanel;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Instances of this class are used to configure the report module plug in that * 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. * 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) { public void mousePressed(MouseEvent evt) {
JList<?> list = (JList) evt.getSource(); JList<?> list = (JList) evt.getSource();
int index = list.locationToIndex(evt.getPoint()); int index = list.locationToIndex(evt.getPoint());
String value = tagsNamesListModel.getElementAt(index); if (index > -1) {
tagNameSelections.put(value, !tagNameSelections.get(value)); String value = tagsNamesListModel.getElementAt(index);
list.repaint(); tagNameSelections.put(value, !tagNameSelections.get(value));
list.repaint();
}
} }
}); });
} }

View File

@ -0,0 +1,122 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<String> errorMessages = new ArrayList<>();
List<Content> 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() {
}
}

View File

@ -0,0 +1,98 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<String> errorMessages, List<Content> 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<String> errorMessages, List<Content> dataSources) {
done(result, errorMessages, dataSources);
}
}

View File

@ -0,0 +1,224 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<? extends ZipEntry> 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;
} */
}

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 65]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[403, 65]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathTextField" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="277" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="pathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,280 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<FileFilter> 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<FileFilter> 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.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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))
);
}// </editor-fold>//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();
}
}

View File

@ -26,7 +26,9 @@ import java.io.RandomAccessFile;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.swing.filechooser.FileFilter;
import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZip;
@ -35,18 +37,46 @@ import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive; import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; 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. * Set of utilities that handles archive file extraction. Uses 7zip library.
*/ */
final class ArchiveUtil { 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<String> 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<FileFilter> ARCHIVE_FILTERS = new ArrayList<>();
static {
ARCHIVE_FILTERS.add(SEVEN_ZIP_FILTER);
}
private ArchiveUtil() { private ArchiveUtil() {
} }
static List<FileFilter> 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<FileFilter> filters) {
for (FileFilter filter : filters) {
if (filter.accept(file)) {
return true;
}
}
return false;
}
/** /**
* Enum of mime types which support archive extraction * Enum of mime types which support archive extraction
*/ */

View File

@ -37,7 +37,6 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.EnumSet; import java.util.EnumSet;
@ -59,9 +58,6 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.concurrent.GuardedBy; 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.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.Case.CaseType; 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.IngestJobStartResult;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleError; import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.datamodel.Content;
/** /**
* An auto ingest manager is responsible for processing auto ingest jobs defined * 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 final class JobProcessingTask implements Runnable {
private static final String AUTO_INGEST_MODULE_OUTPUT_DIR = "AutoIngest";
private final Object ingestLock; private final Object ingestLock;
private final Object pauseLock; private final Object pauseLock;
@GuardedBy("pauseLock") @GuardedBy("pauseLock")
@ -2217,7 +2211,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
return; return;
} }
DataSource dataSource = identifyDataSource(caseForJob); DataSource dataSource = identifyDataSource();
if (null == dataSource) { if (null == dataSource) {
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now())); currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now()));
return; return;
@ -2270,7 +2264,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* interrupted while blocked, i.e., * interrupted while blocked, i.e.,
* if auto ingest is shutting down. * 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(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath); 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(); String deviceId = manifest.getDeviceId();
return new DataSource(deviceId, dataSourcePath); return new DataSource(deviceId, dataSourcePath);
} }
/** /**
* Passes the data source for the current job through a data source * Passes the data source for the current job through a data source
* processor that adds it to the case database. * 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); SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath);
currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE, Date.from(Instant.now())); currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE, Date.from(Instant.now()));
UUID taskId = UUID.randomUUID(); UUID taskId = UUID.randomUUID();
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId); DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock);
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor(); DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath); AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
try { try {
caseForJob.notifyAddingDataSource(taskId); caseForJob.notifyAddingDataSource(taskId);
// lookup all AutomatedIngestDataSourceProcessors Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap;
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); try {
// lookup all AutomatedIngestDataSourceProcessors and poll which ones are able to process the current data source
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>(); validDataSourceProcessorsMap = DataSourceProcessorUtility.getDataSourceProcessor(dataSource.getPath());
for (AutoIngestDataSourceProcessor processor : processorCandidates) { } catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
try { SYS_LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
int confidence = processor.canProcess(dataSource.getPath()); // rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause.
if (confidence > 0) { throw ex;
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;
}
} }
// did we find a data source processor that can process the data source // 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(); 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<String> errorMessages, List<Content> 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<String> errorMessages, List<Content> dataSources) {
done(result, errorMessages, dataSources);
}
}
/** /**
* A data source processor progress monitor does nothing. There is * A data source processor progress monitor does nothing. There is
* currently no mechanism for showing or recording data source processor * currently no mechanism for showing or recording data source processor
@ -3003,49 +2917,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
PARTIALLY_DELETED, PARTIALLY_DELETED,
FULLY_DELETED FULLY_DELETED
} }
@ThreadSafe
private static final class DataSource {
private final String deviceId;
private final Path path;
private DataSourceProcessorResult resultCode;
private List<String> errorMessages;
private List<Content> 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<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content);
}
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode;
}
synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages);
}
synchronized List<Content> getContent() {
return new ArrayList<>(content);
}
}
static final class AutoIngestManagerException extends Exception { static final class AutoIngestManagerException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -247,3 +247,7 @@ AutoIngestDashboard.prioritizeJobButton.toolTipText=Move the selected job to the
AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job
AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue. AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case 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

View File

@ -0,0 +1,68 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<String> errorMessages;
private List<Content> 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<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content);
}
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode;
}
synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages);
}
synchronized List<Content> getContent() {
return new ArrayList<>(content);
}
}

View File

@ -0,0 +1,63 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessor(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
int confidence = processor.canProcess(dataSourcePath);
if (confidence > 0) {
validDataSourceProcessorsMap.put(processor, confidence);
}
}
return validDataSourceProcessorsMap;
}
}

View File

@ -236,9 +236,7 @@ public final class ImageGalleryController {
undoManager.clear(); undoManager.clear();
}); });
regroupDisabled.addListener((Observable observable) -> { regroupDisabled.addListener(observable -> checkForGroups());
checkForGroups();
});
IngestManager ingestManager = IngestManager.getInstance(); IngestManager ingestManager = IngestManager.getInstance();
PropertyChangeListener ingestEventHandler = PropertyChangeListener ingestEventHandler =

View File

@ -128,9 +128,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
LOGGER.log(Level.WARNING, "could not access case during getFilesWithHashSetHitsCount()"); //NON-NLS LOGGER.log(Level.WARNING, "could not access case during getFilesWithHashSetHitsCount()"); //NON-NLS
} }
} }
return hashSetHitsCount.get(); return hashSetHitsCount.get();
} }
public ReadOnlyLongProperty hashSetHitsCountProperty() { public ReadOnlyLongProperty hashSetHitsCountProperty() {
@ -226,7 +224,6 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
// By default, sort by group key name // By default, sort by group key name
@Override @Override
public int compareTo(DrawableGroup other) { public int compareTo(DrawableGroup other) {
return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName()); return getGroupByValueDislpayName().compareTo(other.getGroupByValueDislpayName());
} }
} }

View File

@ -122,7 +122,7 @@ public class GroupManager {
/* /*
* --- current grouping/sorting attributes --- * --- current grouping/sorting attributes ---
*/ */
private volatile GroupSortBy sortBy = GroupSortBy.NONE; private volatile GroupSortBy sortBy = GroupSortBy.PRIORITY;
private volatile DrawableAttribute<?> groupBy = DrawableAttribute.PATH; private volatile DrawableAttribute<?> groupBy = DrawableAttribute.PATH;
private volatile SortOrder sortOrder = SortOrder.ASCENDING; private volatile SortOrder sortOrder = SortOrder.ASCENDING;
@ -156,7 +156,6 @@ public class GroupManager {
*/ */
public GroupManager(ImageGalleryController controller) { public GroupManager(ImageGalleryController controller) {
this.controller = controller; this.controller = controller;
} }
/** /**
@ -188,7 +187,7 @@ public class GroupManager {
* the groups the given file is a part of * the groups the given file is a part of
* *
* @return a a set of {@link GroupKey}s representing the group(s) the given * @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<GroupKey<?>> getGroupKeysForFileID(Long fileID) { synchronized public Set<GroupKey<?>> getGroupKeysForFileID(Long fileID) {
try { try {
@ -208,7 +207,7 @@ public class GroupManager {
* @param groupKey * @param groupKey
* *
* @return return the DrawableGroup (if it exists) for the given 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 @Nullable
public DrawableGroup getGroupForKey(@Nonnull GroupKey<?> groupKey) { public DrawableGroup getGroupForKey(@Nonnull GroupKey<?> groupKey) {
@ -284,7 +283,7 @@ public class GroupManager {
* no-op * no-op
* *
* @param groupKey the value of groupKey * @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) { public synchronized DrawableGroup removeFromGroup(GroupKey<?> groupKey, final Long fileID) {
//get grouping this file would be in //get grouping this file would be in
@ -466,7 +465,7 @@ public class GroupManager {
} }
} }
public Comparator<DrawableGroup> getSortBy() { public GroupSortBy getSortBy() {
return sortBy; return sortBy;
} }
@ -512,7 +511,7 @@ public class GroupManager {
* @param groupBy * @param groupBy
* @param sortBy * @param sortBy
* @param sortOrder * @param sortOrder
* @param force true to force a full db query regroup * @param force true to force a full db query regroup
*/ */
public synchronized <A extends Comparable<A>> void regroup(final DrawableAttribute<A> groupBy, final GroupSortBy sortBy, final SortOrder sortOrder, Boolean force) { public synchronized <A extends Comparable<A>> void regroup(final DrawableAttribute<A> groupBy, final GroupSortBy sortBy, final SortOrder sortOrder, Boolean force) {
@ -530,9 +529,7 @@ public class GroupManager {
} }
groupByTask = new ReGroupTask<>(groupBy, sortBy, sortOrder); groupByTask = new ReGroupTask<>(groupBy, sortBy, sortOrder);
Platform.runLater(() -> { Platform.runLater(() -> regroupProgress.bind(groupByTask.progressProperty()));
regroupProgress.bind(groupByTask.progressProperty());
});
regroupExecutor.submit(groupByTask); regroupExecutor.submit(groupByTask);
} else { } else {
// resort the list of groups // resort the list of groups
@ -583,10 +580,7 @@ public class GroupManager {
DrawableGroup group = g; DrawableGroup group = g;
if (group != null) { if (group != null) {
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it. //if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
Platform.runLater(() -> { Platform.runLater(() -> group.addFile(fileID));
group.addFile(fileID);
});
} }
} }
@ -682,9 +676,9 @@ public class GroupManager {
} else { } else {
group = new DrawableGroup(groupKey, fileIDs, groupSeen); group = new DrawableGroup(groupKey, fileIDs, groupSeen);
controller.getCategoryManager().registerListener(group); controller.getCategoryManager().registerListener(group);
group.seenProperty().addListener((o, oldSeen, newSeen) -> { group.seenProperty().addListener((o, oldSeen, newSeen) ->
Platform.runLater(() -> markGroupSeen(group, newSeen)); Platform.runLater(() -> markGroupSeen(group, newSeen))
}); );
groupMap.put(groupKey, group); groupMap.put(groupKey, group);
} }
} }
@ -693,7 +687,7 @@ public class GroupManager {
analyzedGroups.add(group); analyzedGroups.add(group);
if (Objects.isNull(task)) { if (Objects.isNull(task)) {
FXCollections.sort(analyzedGroups, applySortOrder(sortOrder, sortBy)); FXCollections.sort(analyzedGroups, applySortOrder(sortOrder, sortBy));
} }
} }
markGroupSeen(group, groupSeen); markGroupSeen(group, groupSeen);
}); });
@ -743,17 +737,17 @@ public class GroupManager {
"# {0} - groupBy attribute Name", "# {0} - groupBy attribute Name",
"# {1} - atribute value", "# {1} - atribute value",
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"}) "ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
private class ReGroupTask<A extends Comparable<A>> extends LoggedTask<Void> { private class ReGroupTask<AttrType extends Comparable<AttrType>> extends LoggedTask<Void> {
private ProgressHandle groupProgress; private ProgressHandle groupProgress;
private final DrawableAttribute<A> groupBy; private final DrawableAttribute<AttrType> groupBy;
private final GroupSortBy sortBy; private final GroupSortBy sortBy;
private final SortOrder sortOrder; private final SortOrder sortOrder;
ReGroupTask(DrawableAttribute<A> groupBy, GroupSortBy sortBy, SortOrder sortOrder) { ReGroupTask(DrawableAttribute<AttrType> groupBy, GroupSortBy sortBy, SortOrder sortOrder) {
super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), true); super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), true);
this.groupBy = groupBy; this.groupBy = groupBy;
@ -780,13 +774,13 @@ public class GroupManager {
}); });
// Get the list of group keys // Get the list of group keys
final List<A> vals = findValuesForAttribute(groupBy); final List<AttrType> vals = findValuesForAttribute(groupBy);
groupProgress.start(vals.size()); groupProgress.start(vals.size());
int p = 0; int p = 0;
// For each key value, partially create the group and add it to the list. // 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()) { if (isCancelled()) {
return null;//abort return null;//abort
} }

View File

@ -37,24 +37,34 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
/** /**
* sort the groups by the number of files in each * 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 * sort the groups by the natural order of the grouping value ( eg group
* them by path alphabetically ) * 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 * don't sort the groups just use what ever order they come in (ingest
* order) * 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 * 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 @Override
public int compare(DrawableGroup o1, DrawableGroup o2) { public int compare(DrawableGroup o1, DrawableGroup o2) {

View File

@ -180,8 +180,8 @@ public class Toolbar extends ToolBar {
sortChooser = new SortChooser<>(GroupSortBy.getValues()); sortChooser = new SortChooser<>(GroupSortBy.getValues());
sortChooser.comparatorProperty().addListener((observable, oldComparator, newComparator) -> { sortChooser.comparatorProperty().addListener((observable, oldComparator, newComparator) -> {
final boolean orderEnabled = newComparator == GroupSortBy.NONE || newComparator == GroupSortBy.PRIORITY; final boolean orderDisabled = newComparator == GroupSortBy.NONE || newComparator == GroupSortBy.PRIORITY;
sortChooser.setSortOrderDisabled(orderEnabled); sortChooser.setSortOrderDisabled(orderDisabled);
final SortChooser.ValueType valueType = newComparator == GroupSortBy.GROUP_BY_VALUE ? SortChooser.ValueType.LEXICOGRAPHIC : SortChooser.ValueType.NUMERIC; final SortChooser.ValueType valueType = newComparator == GroupSortBy.GROUP_BY_VALUE ? SortChooser.ValueType.LEXICOGRAPHIC : SortChooser.ValueType.NUMERIC;
sortChooser.setValueType(valueType); sortChooser.setValueType(valueType);
@ -189,7 +189,7 @@ public class Toolbar extends ToolBar {
}); });
sortChooser.sortOrderProperty().addListener(queryInvalidationListener); sortChooser.sortOrderProperty().addListener(queryInvalidationListener);
sortChooser.setComparator(GroupSortBy.PRIORITY); sortChooser.setComparator(controller.getGroupManager().getSortBy());
getItems().add(1, sortChooser); getItems().add(1, sortChooser);
sortHelpImageView.setCursor(Cursor.HAND); sortHelpImageView.setCursor(Cursor.HAND);

View File

@ -6,8 +6,21 @@
<description>Builds, tests, and runs the project org.sleuthkit.autopsy.keywordsearch.</description> <description>Builds, tests, and runs the project org.sleuthkit.autopsy.keywordsearch.</description>
<import file="nbproject/build-impl.xml"/> <import file="nbproject/build-impl.xml"/>
<import file="../BootstrapIvy.xml"/> <import file="../BootstrapIvy.xml"/>
<property name="lib" location="release/modules/ext"/>
<property name="autopsy-lib" location="../CoreLibs/release/modules/ext"/>
<property name="build" location="build/classes"/>
<property name="build-test" location="build/test/unit/classes"/>
<path id="libs">
<fileset dir="${lib}">
<include name="*.jar"/>
</fileset>
<fileset dir="${autopsy-lib}">
<include name="*.jar"/>
</fileset>
<pathelement path="${build}"/>
<pathelement path="${build-test}"/>
</path>
<target name="get-solr-deployment" description="copy the solr deployment into release"> <target name="get-solr-deployment" description="copy the solr deployment into release">
<copy todir="${basedir}/release/solr" > <copy todir="${basedir}/release/solr" >
<fileset dir="solr"/> <fileset dir="solr"/>
@ -39,5 +52,12 @@
<target name="clean-all" depends="clean" description="Clear Ivy cache."> <target name="clean-all" depends="clean" description="Clear Ivy cache.">
<ivy:cleancache/> <ivy:cleancache/>
</target> </target>
<target name="test-unit" depends="init, test-init, test-build">
<junit fork="on" haltonfailure="yes" dir=".">
<formatter type="plain" usefile="false" />
<classpath refid="libs"/>
<test name="org.sleuthkit.autopsy.keywordsearch.KeywordSearchTestSuite" />
</junit>
</target>
</project> </project>

View File

@ -138,12 +138,14 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
@Override @Override
public void addPropertyChangeListener(PropertyChangeListener l) { public void addPropertyChangeListener(PropertyChangeListener l) {
super.addPropertyChangeListener(l);
listsManagementPanel.addPropertyChangeListener(l); listsManagementPanel.addPropertyChangeListener(l);
editListPanel.addPropertyChangeListener(l); editListPanel.addPropertyChangeListener(l);
} }
@Override @Override
public void removePropertyChangeListener(PropertyChangeListener l) { public void removePropertyChangeListener(PropertyChangeListener l) {
super.removePropertyChangeListener(l);
listsManagementPanel.removePropertyChangeListener(l); listsManagementPanel.removePropertyChangeListener(l);
editListPanel.removePropertyChangeListener(l); editListPanel.removePropertyChangeListener(l);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -28,11 +28,12 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
*/ */
final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private GlobalListSettingsPanel listsPanel; private static final long serialVersionUID = 1L;
private KeywordSearchGlobalLanguageSettingsPanel languagesPanel; private final GlobalListSettingsPanel listsPanel = new GlobalListSettingsPanel();
private KeywordSearchGlobalSearchSettingsPanel generalPanel; private final KeywordSearchGlobalLanguageSettingsPanel languagesPanel = new KeywordSearchGlobalLanguageSettingsPanel();
private final KeywordSearchGlobalSearchSettingsPanel generalPanel = new KeywordSearchGlobalSearchSettingsPanel();
public KeywordSearchGlobalSettingsPanel() { KeywordSearchGlobalSettingsPanel() {
initComponents(); initComponents();
customizeComponents(); customizeComponents();
} }
@ -40,9 +41,6 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
@NbBundle.Messages({"KeywordSearchGlobalSettingsPanel.Title=Global Keyword Search Settings"}) @NbBundle.Messages({"KeywordSearchGlobalSettingsPanel.Title=Global Keyword Search Settings"})
private void customizeComponents() { private void customizeComponents() {
setName(Bundle.KeywordSearchGlobalSettingsPanel_Title()); setName(Bundle.KeywordSearchGlobalSettingsPanel_Title());
listsPanel = new GlobalListSettingsPanel();
languagesPanel = new KeywordSearchGlobalLanguageSettingsPanel();
generalPanel = new KeywordSearchGlobalSearchSettingsPanel();
tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listTabTitle"), null, tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listTabTitle"), null,
listsPanel, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listLabToolTip"), 0); listsPanel, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.listLabToolTip"), 0);
tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.stringExtTitle"), null, tabbedPane.insertTab(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.stringExtTitle"), null,
@ -53,6 +51,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
@Override @Override
public void addPropertyChangeListener(PropertyChangeListener l) { public void addPropertyChangeListener(PropertyChangeListener l) {
super.addPropertyChangeListener(l);
listsPanel.addPropertyChangeListener(l); listsPanel.addPropertyChangeListener(l);
languagesPanel.addPropertyChangeListener(l); languagesPanel.addPropertyChangeListener(l);
generalPanel.addPropertyChangeListener(l); generalPanel.addPropertyChangeListener(l);
@ -60,6 +59,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
@Override @Override
public void removePropertyChangeListener(PropertyChangeListener l) { public void removePropertyChangeListener(PropertyChangeListener l) {
super.removePropertyChangeListener(l);
listsPanel.removePropertyChangeListener(l); listsPanel.removePropertyChangeListener(l);
languagesPanel.removePropertyChangeListener(l); languagesPanel.removePropertyChangeListener(l);
generalPanel.removePropertyChangeListener(l); generalPanel.removePropertyChangeListener(l);

View File

@ -0,0 +1,54 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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) {
}
}