diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java index ad02d666a9..adf61a8f32 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java @@ -39,6 +39,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; /** * visual component for the first panel of add image wizard. Allows the user to @@ -92,7 +93,8 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); } coreDSPTypes.add(LocalFilesDSProcessor.getType()); - + coreDSPTypes.add(RawDSProcessor.getType()); + for (String dspType : coreDSPTypes) { typeComboBox.addItem(dspType); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 044559e0f8..d38eaa77ae 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -78,7 +78,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener { this.contextName = context; - createTimeZoneList(); } /** @@ -89,6 +88,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { ImageFilePanel instance = new ImageFilePanel(context, fileChooserFilters); instance.postInit(); + instance.createTimeZoneList(); return instance; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form index 1f81b4d3aa..3f33f848c0 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.form @@ -21,10 +21,7 @@ - - - - + @@ -39,11 +36,11 @@ - + - - + + @@ -55,7 +52,7 @@ - + @@ -69,132 +66,138 @@ - - - - - + - + - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java index 4f761062e8..c1c9beb5b2 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java @@ -67,6 +67,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { jPanel1 = new javax.swing.JPanel(); panelDescriptionLabel = new javax.swing.JLabel(); + jScrollPane2 = new javax.swing.JScrollPane(); jSplitPane1 = new javax.swing.JSplitPane(); modifyTagTypesListPanel = new javax.swing.JPanel(); tagTypesListLabel = new javax.swing.JLabel(); @@ -111,13 +112,13 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(tagTypesListLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(modifyTagTypesListPanelLayout.createSequentialGroup() .addComponent(newTagNameButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(deleteTagNameButton) - .addGap(0, 113, Short.MAX_VALUE)) - .addComponent(jScrollPane1)) + .addGap(0, 113, Short.MAX_VALUE))) .addContainerGap()) ); modifyTagTypesListPanelLayout.setVerticalGroup( @@ -126,7 +127,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { .addContainerGap() .addComponent(tagTypesListLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 381, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(modifyTagTypesListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newTagNameButton) @@ -140,24 +141,26 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { tagTypesAdditionalPanel.setLayout(tagTypesAdditionalPanelLayout); tagTypesAdditionalPanelLayout.setHorizontalGroup( tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 356, Short.MAX_VALUE) + .addGap(0, 354, Short.MAX_VALUE) ); tagTypesAdditionalPanelLayout.setVerticalGroup( tagTypesAdditionalPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 456, Short.MAX_VALUE) + .addGap(0, 454, Short.MAX_VALUE) ); jSplitPane1.setRightComponent(tagTypesAdditionalPanel); + jScrollPane2.setViewportView(jSplitPane1); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jSplitPane1) - .addComponent(panelDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(panelDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jScrollPane2)) .addContainerGap()) ); jPanel1Layout.setVerticalGroup( @@ -166,7 +169,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { .addContainerGap() .addComponent(panelDescriptionLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSplitPane1) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 458, Short.MAX_VALUE) .addContainerGap()) ); @@ -178,9 +181,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -220,6 +221,7 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { private javax.swing.JButton deleteTagNameButton; private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JPanel modifyTagTypesListPanel; private javax.swing.JButton newTagNameButton; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 1c20079763..07cc1b159e 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -140,7 +140,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file)); - String md5 = file.getMd5Hash(); if (md5 == null) { @@ -149,7 +148,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString()); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(file.getId())); if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath()); } diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index bc14db3b7d..e2036e6f25 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -64,12 +64,54 @@ public class Installer extends ModuleInstall { //Note: if shipping with a different CRT version, this will only print a warning //and try to use linker mechanism to find the correct versions of libs. //We should update this if we officially switch to a new version of CRT/compiler - System.loadLibrary("msvcr100"); //NON-NLS - System.loadLibrary("msvcp100"); //NON-NLS + System.loadLibrary("api-ms-win-core-console-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-datetime-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-debug-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-errorhandling-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-file-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-file-l1-2-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-file-l2-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-handle-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-heap-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-interlocked-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-libraryloader-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-localization-l1-2-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-memory-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-namedpipe-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-processenvironment-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-processthreads-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-processthreads-l1-1-1"); //NON-NLS + System.loadLibrary("api-ms-win-core-profile-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-rtlsupport-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-string-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-synch-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-synch-l1-2-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-sysinfo-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-timezone-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-core-util-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-conio-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-convert-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-environment-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-filesystem-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-heap-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-locale-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-math-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-multibyte-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-private-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-process-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-runtime-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-stdio-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-string-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-time-l1-1-0"); //NON-NLS + System.loadLibrary("api-ms-win-crt-utility-l1-1-0"); //NON-NLS - logger.log(Level.INFO, "MSVCR100 and MSVCP100 libraries loaded"); //NON-NLS + System.loadLibrary("ucrtbase"); //NON-NLS + System.loadLibrary("vcruntime140"); //NON-NLS + System.loadLibrary("msvcp140"); //NON-NLS + + logger.log(Level.INFO, "Visual C Runtime libraries loaded"); //NON-NLS } catch (UnsatisfiedLinkError e) { - logger.log(Level.SEVERE, "Error loading MSVCR100 and MSVCP100 libraries, ", e); //NON-NLS + logger.log(Level.SEVERE, "Error loading Visual C Runtime libraries, ", e); //NON-NLS } try { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index d312af106a..0d6f057f9b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -34,7 +34,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.logging.Level; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; @@ -56,11 +55,9 @@ import org.openide.nodes.NodeEvent; import org.openide.nodes.NodeListener; import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeReorderEvent; -import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; -import org.sleuthkit.autopsy.coreutils.Logger; /** * DataResult sortable table viewer @@ -80,10 +77,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private static final String DUMMY_NODE_DISPLAY_NAME = NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.dummyNodeDisplayName"); private static final Color TAGGED_COLOR = new Color(230, 235, 240); private Node currentRoot; - // The following two variables keep track of whether the user is trying - // to move the first column, which is not allowed. - private int oldColumnIndex = -1; - private int newColumnIndex = -1; + // When a column in the table is moved, these two variables keep track of where + // the column started and where it ended up. + private int startColumnIndex = -1; + private int endColumnIndex = -1; /** * Creates a DataResultViewerTable object that is compatible with node @@ -137,11 +134,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer { if (fromIndex == toIndex) { return; } - // to keep track of attempts to move the first column - if (oldColumnIndex == -1) { - oldColumnIndex = fromIndex; + + /* Because a column may be dragged to several different positions before + * the mouse is released (thus causing multiple TableColumnModelEvents to + * be fired), we want to keep track of the starting column index in this + * potential series of movements. Therefore we only keep track of the + * original fromIndex in startColumnIndex, but we always update + * endColumnIndex to know the final position of the moved column. + * See the MouseListener mouseReleased method. + */ + if (startColumnIndex == -1) { + startColumnIndex = fromIndex; } - newColumnIndex = toIndex; + endColumnIndex = toIndex; List> props = new ArrayList<>(propertiesAcc); Node.Property prop = props.remove(fromIndex); @@ -158,10 +163,20 @@ public class DataResultViewerTable extends AbstractDataResultViewer { ov.getOutline().getTableHeader().addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { - if (oldColumnIndex != -1 && (oldColumnIndex == 0 || newColumnIndex == 0)) { - ov.getOutline().moveColumn(newColumnIndex, oldColumnIndex); + /* If the startColumnIndex is not -1 (which is the reset value), that + * means columns have been moved around. We then check to see if either + * the starting or end position is 0 (the first column), and then swap + * them back if that is the case because we don't want to allow movement + * of the first column. We then reset startColumnIndex to -1, the reset + * value. + * We check if startColumnIndex is at reset or not because it is + * possible for the mouse to be released and a MouseEvent to be fired + * without having moved any columns. + */ + if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) { + ov.getOutline().moveColumn(endColumnIndex, startColumnIndex); } - oldColumnIndex = -1; + startColumnIndex = -1; } }); } @@ -217,68 +232,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private javax.swing.JScrollPane tableScrollPanel; // End of variables declaration//GEN-END:variables - /** - * Gets regular Bean property set properties from first child of Node. - * - * @param parent Node with at least one child to get properties from - * - * @return Properties, - */ - private Node.Property[] getChildPropertyHeaders(Node parent) { - Node firstChild = parent.getChildren().getNodeAt(0); - - if (firstChild == null) { - throw new IllegalArgumentException( - NbBundle.getMessage(this.getClass(), "DataResultViewerTable.illegalArgExc.noChildFromParent")); - } else { - for (PropertySet ps : firstChild.getPropertySets()) { - if (ps.getName().equals(Sheet.PROPERTIES)) { - return ps.getProperties(); - } - } - - throw new IllegalArgumentException( - NbBundle.getMessage(this.getClass(), "DataResultViewerTable.illegalArgExc.childWithoutPropertySet")); - } - } - - /** - * Gets regular Bean property set properties from all first children and, - * recursively, subchildren of Node. Note: won't work out the box for lazy - * load - you need to set all children props for the parent by hand - * - * @param parent Node with at least one child to get properties from - * - * @return Properties, - */ - @SuppressWarnings("rawtypes") - private Node.Property[] getAllChildPropertyHeaders(Node parent) { - Node firstChild = parent.getChildren().getNodeAt(0); - - Property[] properties = null; - - if (firstChild == null) { - throw new IllegalArgumentException( - NbBundle.getMessage(this.getClass(), "DataResultViewerTable.illegalArgExc.noChildFromParent")); - } else { - Set allProperties = new LinkedHashSet<>(); - while (firstChild != null) { - for (PropertySet ps : firstChild.getPropertySets()) { - final Property[] props = ps.getProperties(); - final int propsNum = props.length; - for (int i = 0; i < propsNum; ++i) { - allProperties.add(props[i]); - } - } - firstChild = firstChild.getChildren().getNodeAt(0); - } - - properties = allProperties.toArray(new Property[0]); - } - return properties; - - } - /** * Gets regular Bean property set properties from all children and, * recursively, subchildren of Node. Note: won't work out the box for lazy @@ -320,6 +273,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { @Override public void setNode(Node selectedNode) { final OutlineView ov = ((OutlineView) this.tableScrollPanel); + /* The quick filter must be reset because when determining column width, + * ETable.getRowCount is called, and the documentation states that quick + * filters must be unset for the method to work + * "If the quick-filter is applied the number of rows do not match the number of rows in the model." + */ ov.getOutline().unsetQuickFilter(); // change the cursor to "waiting cursor" for this operation this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); @@ -405,9 +363,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // Just let the table resize itself. ov.getOutline().setAutoResizeMode((props.size() > 0) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS); - // get first row's values for the table if (root.getChildren().getNodesCount() != 0) { - final Graphics graphics = ov.getGraphics(); if (graphics != null) { final FontMetrics metrics = graphics.getFontMetrics(); @@ -420,9 +376,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { int columnWidthLimit = (column == 0) ? 350 : 300; int valuesWidth = 0; - // find the maximum width needed to fit the values for the first 15 rows, at most - // *15 is an arbitrary number - for (int row = 0; row < Math.min(15, ov.getOutline().getRowCount()); row++) { + // find the maximum width needed to fit the values for the first 100 rows, at most + for (int row = 0; row < Math.min(100, ov.getOutline().getRowCount()); row++) { TableCellRenderer renderer = ov.getOutline().getCellRenderer(row, column); Component comp = ov.getOutline().prepareRenderer(renderer, row, column); valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth); @@ -438,51 +393,52 @@ public class DataResultViewerTable extends AbstractDataResultViewer { ov.getOutline().getColumnModel().getColumn(column).setPreferredWidth(columnWidth); } } + } else { + // if there's no content just auto resize all columns + ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + } - /** - * This custom renderer extends the renderer that was already being - * used by the outline table. This renderer colors a row if the - * tags property of the node is not empty. - */ - class ColorTagCustomRenderer extends DefaultOutlineCellRenderer { - private static final long serialVersionUID = 1L; - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, int col) { - - Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); - // only override the color if a node is not selected - if (!isSelected) { - Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row)); - boolean tagFound = false; - if (node != null) { - Node.PropertySet[] propSets = node.getPropertySets(); - if (propSets.length != 0) { - // currently, a node has only one property set, named Sheet.PROPERTIES ("properties") - Node.Property[] props = propSets[0].getProperties(); - for (Property prop : props) { - if (prop.getName().equals("Tags")) { - try { - tagFound = !prop.getValue().equals(""); - } catch (IllegalAccessException | InvocationTargetException ignore) { - } - break; + /** + * This custom renderer extends the renderer that was already being + * used by the outline table. This renderer colors a row if the + * tags property of the node is not empty. + */ + class ColorTagCustomRenderer extends DefaultOutlineCellRenderer { + private static final long serialVersionUID = 1L; + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, int col) { + + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + // only override the color if a node is not selected + if (!isSelected) { + Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row)); + boolean tagFound = false; + if (node != null) { + Node.PropertySet[] propSets = node.getPropertySets(); + if (propSets.length != 0) { + // currently, a node has only one property set, named Sheet.PROPERTIES ("properties") + Node.Property[] props = propSets[0].getProperties(); + for (Property prop : props) { + if (prop.getName().equals("Tags")) { + try { + tagFound = !prop.getValue().equals(""); + } catch (IllegalAccessException | InvocationTargetException ignore) { } + break; } } } - //if the node does have associated tags, set its background color - if (tagFound) { - component.setBackground(TAGGED_COLOR); - } } - return component; + //if the node does have associated tags, set its background color + if (tagFound) { + component.setBackground(TAGGED_COLOR); + } } + return component; } - ov.getOutline().setDefaultRenderer(Object.class, new ColorTagCustomRenderer()); - } else { - ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); } + ov.getOutline().setDefaultRenderer(Object.class, new ColorTagCustomRenderer()); } /** @@ -504,7 +460,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { List> props = new ArrayList<>(propertiesAcc); for (int i = 0; i < props.size(); i++) { Property prop = props.get(i); - NbPreferences.forModule(this.getClass()).put(getPreferenceKey(prop, tfn.getItemType()), String.valueOf(i)); + NbPreferences.forModule(this.getClass()).put(getColumnPreferenceKey(prop, tfn.getColumnOrderKey()), String.valueOf(i)); } } @@ -523,8 +479,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { if (currentRoot instanceof TableFilterNode) { tfn = (TableFilterNode) currentRoot; } else { - Logger.getLogger(DataResultViewerTable.class.getName()).log(Level.INFO, - "Node {0} is not a TableFilterNode, columns are going to be in default order", currentRoot.getName()); + // The node is not a TableFilterNode, columns are going to be in default order return props; } @@ -535,8 +490,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { Map> propsFromPreferences = new TreeMap<>(); int offset = props.size(); for (Property prop : props) { - Integer value = Integer.valueOf(NbPreferences.forModule(this.getClass()).get(getPreferenceKey(prop, tfn.getItemType()), "-1")); - if (value >= 0) { + Integer value = Integer.valueOf(NbPreferences.forModule(this.getClass()).get(getColumnPreferenceKey(prop, tfn.getColumnOrderKey()), "-1")); + if (value >= 0 && value < offset) { propsFromPreferences.put(value, prop); } else { propsFromPreferences.put(offset, prop); @@ -559,7 +514,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * @param type The type of the current node * @return A generated key for the preference file */ - private String getPreferenceKey(Property prop, String type) { + private String getColumnPreferenceKey(Property prop, String type) { return type.replaceAll("[^a-zA-Z0-9_]", "") + "." + prop.getName().replaceAll("[^a-zA-Z0-9_]", "") + ".column"; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java index 0de1cf6487..9c04a003ff 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java @@ -25,11 +25,13 @@ import org.openide.util.NbBundle; /** * A filter node that creates at most one layer of child nodes for the node it * wraps. It is designed to be used for nodes displayed in Autopsy table views. + * This ensures that the table view for the node will not recursively display + * child nodes and display only the first layer of child nodes. */ public class TableFilterNode extends FilterNode { private final boolean createChildren; - private String itemType = "NONE"; + private String columnOrderKey = "NONE"; /** * Constructs a filter node that creates at most one layer of child nodes @@ -39,6 +41,7 @@ public class TableFilterNode extends FilterNode { * @param wrappedNode The node to wrap in the filter node. * @param createChildren True if a children (child factory) object should be * created for the wrapped node. + * The constructor should include column order key. (See getColumnOrderKey) */ public TableFilterNode(Node wrappedNode, boolean createChildren) { super(wrappedNode, TableFilterChildren.createInstance(wrappedNode, createChildren)); @@ -51,13 +54,14 @@ public class TableFilterNode extends FilterNode { * @param wrappedNode The node to wrap in the filter node. * @param createChildren True if a children (child factory) object should be * created for the wrapped node. - * @param itemType A name of the node, based on its class name and - * filter or artifact type if it holds those. + * @param columnOrderKey A key that represents the type of the original + * wrapped node and what is being displayed under that + * node. */ - public TableFilterNode(Node wrappedNode, boolean createChildren, String itemType) { + public TableFilterNode(Node wrappedNode, boolean createChildren, String columnOrderKey) { super(wrappedNode, TableFilterChildren.createInstance(wrappedNode, createChildren)); this.createChildren = createChildren; - this.itemType = itemType; + this.columnOrderKey = columnOrderKey; } /** @@ -76,10 +80,13 @@ public class TableFilterNode extends FilterNode { } /** - * @return itemType of associated DisplayableItemNode to allow for custom - * column orderings in the DataResultViewerTable + * @return the column order key, which allows custom column ordering to be + * written into a properties file and be reloaded for future use in + * a table with the same root node or for different cases. This is + * done by DataResultViewerTable. The key should represent what + * kinds of items the table is showing. */ - String getItemType() { - return itemType; + String getColumnOrderKey() { + return columnOrderKey; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 0ea877d9a5..90a9a38b85 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -130,14 +130,6 @@ public class ArtifactStringContent implements StringContent { buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS - // add artifact ID (useful for debugging) - buffer.append(""); //NON-NLS - buffer.append(NbBundle.getMessage(this.getClass(), "ArtifactStringContent.getStr.artifactId.text")); - buffer.append(""); //NON-NLS - buffer.append(artifact.getArtifactID()); - buffer.append(""); //NON-NLS - buffer.append("\n"); //NON-NLS - buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index f1205211a4..7d2bd61100 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel.accounts; -import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; -import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem; import com.google.common.collect.Range; import com.google.common.collect.RangeMap; import com.google.common.collect.TreeRangeMap; @@ -59,6 +57,8 @@ import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; +import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; +import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.CreditCards; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; @@ -76,6 +76,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData.DbType; /** * AutopsyVisitableItem for the Accounts section of the tree. All nodes, @@ -146,6 +147,14 @@ final public class Accounts implements AutopsyVisitableItem { */ private abstract class ObservingChildren extends Children.Keys { + /** + * Override of default constructor to force lazy creation of nodes, by + * concrete instances of ObservingChildren + */ + ObservingChildren() { + super(true); + } + /** * Create of keys used by this Children object to represent the child * nodes. @@ -167,6 +176,9 @@ final public class Accounts implements AutopsyVisitableItem { @Subscribe abstract void handleReviewStatusChange(ReviewStatusChangeEvent event); + @Subscribe + abstract void handleDataAdded(ModuleDataEvent event); + @Override protected void removeNotify() { super.removeNotify(); @@ -219,7 +231,7 @@ final public class Accounts implements AutopsyVisitableItem { ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { - refreshKeys(); + reviewStatusBus.post(eventData); } } catch (IllegalStateException notUsed) { // Case is closed, do nothing. @@ -250,7 +262,13 @@ final public class Accounts implements AutopsyVisitableItem { @Subscribe @Override - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + refreshKeys(); + } + + @Subscribe + @Override + void handleDataAdded(ModuleDataEvent event) { refreshKeys(); } @@ -377,10 +395,17 @@ final public class Accounts implements AutopsyVisitableItem { } } + @Subscribe @Override void handleReviewStatusChange(ReviewStatusChangeEvent event) { refreshKeys(); } + + @Subscribe + @Override + void handleDataAdded(ModuleDataEvent event) { + refreshKeys(); + } } private DefaultAccountTypeNode(String accountTypeName) { @@ -426,10 +451,12 @@ final public class Accounts implements AutopsyVisitableItem { */ final private class ViewModeFactory extends ObservingChildren { - @Subscribe @Override - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { - refreshKeys(); + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + } + + @Override + void handleDataAdded(ModuleDataEvent event) { } /** @@ -490,7 +517,13 @@ final public class Accounts implements AutopsyVisitableItem { @Subscribe @Override - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + refreshKeys(); + } + + @Subscribe + @Override + void handleDataAdded(ModuleDataEvent event) { refreshKeys(); } @@ -499,10 +532,15 @@ final public class Accounts implements AutopsyVisitableItem { List list = new ArrayList<>(); String query = "SELECT blackboard_artifacts.obj_id," //NON-NLS - + " solr_attribute.value_text AS solr_document_id, " //NON-NLS - + " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS - + " COUNT( blackboard_artifacts.artifact_id) AS hits, " //NON-NLS - + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids " + + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS + if(skCase.getDatabaseType().equals(DbType.POSTGRESQL)){ + query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS + + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, "; + } else { + query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS + + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, "; + } + query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS + " FROM blackboard_artifacts " //NON-NLS + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS @@ -571,11 +609,15 @@ final public class Accounts implements AutopsyVisitableItem { + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.name() + "'" //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS + getRejectedArtifactFilterClause() - + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text )"; + + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo"; try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query); ResultSet rs = results.getResultSet();) { while (rs.next()) { - setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count(*)"))); + if(skCase.getDatabaseType().equals(DbType.POSTGRESQL)){ + setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count"))); + } else { + setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count(*)"))); + } } } catch (TskCoreException | SQLException ex) { LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS @@ -599,7 +641,12 @@ final public class Accounts implements AutopsyVisitableItem { } @Subscribe - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + updateDisplayName(); + } + + @Subscribe + void handleDataAdded(ModuleDataEvent event) { updateDisplayName(); } } @@ -617,7 +664,13 @@ final public class Accounts implements AutopsyVisitableItem { @Subscribe @Override - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + refreshKeys(); + } + + @Subscribe + @Override + void handleDataAdded(ModuleDataEvent event) { refreshKeys(); } @@ -718,7 +771,12 @@ final public class Accounts implements AutopsyVisitableItem { } @Subscribe - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + updateDisplayName(); + } + + @Subscribe + void handleDataAdded(ModuleDataEvent event) { updateDisplayName(); } } @@ -960,15 +1018,18 @@ final public class Accounts implements AutopsyVisitableItem { @Subscribe @Override - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { refreshKeys(); - //make sure to refresh the nodes for artifacts that changed statuses. + //make sure to refresh the nodes for artifacts that changed statuses. event.artifacts.stream().map(BlackboardArtifact::getArtifactID).forEach(this::refreshKey); } - /** - * - */ + @Subscribe + @Override + void handleDataAdded(ModuleDataEvent event) { + refreshKeys(); + } + @Override protected List createKeys() { List list = new ArrayList<>(); @@ -979,7 +1040,7 @@ final public class Accounts implements AutopsyVisitableItem { + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS - + " AND blackboard_attributes.value_text >= \"" + bin.getBINStart() + "\" AND blackboard_attributes.value_text < \"" + (bin.getBINEnd() + 1) + "\"" //NON-NLS + + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS + getRejectedArtifactFilterClause() + " ORDER BY blackboard_attributes.value_text"; //NON-NLS try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query); @@ -1010,12 +1071,11 @@ final public class Accounts implements AutopsyVisitableItem { } } private final BinResult bin; -// private final CreditCardNumberFactory accountFactory; private BINNode(BinResult bin) { super(Children.LEAF); - setChildren(Children.createLazy(CreditCardNumberFactory::new)); this.bin = bin; + setChildren(Children.createLazy(CreditCardNumberFactory::new)); setName(getBinRangeString()); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS @@ -1023,7 +1083,12 @@ final public class Accounts implements AutopsyVisitableItem { } @Subscribe - public void handleReviewStatusChange(ReviewStatusChangeEvent event) { + void handleReviewStatusChange(ReviewStatusChangeEvent event) { + updateDisplayName(); + } + + @Subscribe + void handleDataAdded(ModuleDataEvent event) { updateDisplayName(); } @@ -1034,7 +1099,7 @@ final public class Accounts implements AutopsyVisitableItem { + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS - + " AND blackboard_attributes.value_text >= \"" + bin.getBINStart() + "\" AND blackboard_attributes.value_text < \"" + (bin.getBINEnd() + 1) + "\"" //NON-NLS + + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS + getRejectedArtifactFilterClause(); try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query); ResultSet rs = results.getResultSet();) { @@ -1334,18 +1399,27 @@ final public class Accounts implements AutopsyVisitableItem { */ if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) { List siblings = Arrays.asList(node.getParentNode().getChildren().getNodes()); - int indexOf = siblings.indexOf(node); - //there is no previous for the first node, so instead we select the next one - Node sibling = indexOf > 0 - ? siblings.get(indexOf - 1) - : siblings.get(indexOf + 1); - createPath = NodeOp.createPath(sibling, null); + if (siblings.size() > 1) { + int indexOf = siblings.indexOf(node); + //there is no previous for the first node, so instead we select the next one + Node sibling = indexOf > 0 + ? siblings.get(indexOf - 1) + : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1)); + createPath = NodeOp.createPath(sibling, null); + } else { + /* if there are no other siblings to select, + * just return null, but note we need to filter + * this out of stream below */ + return null; + } } else { createPath = NodeOp.createPath(node, null); } //for the reselect to work we need to strip off the first part of the path. return Arrays.copyOfRange(createPath, 1, createPath.length); - }).collect(Collectors.toList()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); //change status of selected artifacts final Collection artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class); @@ -1392,7 +1466,7 @@ final public class Accounts implements AutopsyVisitableItem { } } - class ReviewStatusChangeEvent { + private class ReviewStatusChangeEvent { Collection artifacts; BlackboardArtifact.ReviewStatus newReviewStatus; diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java new file mode 100644 index 0000000000..c85eedaa06 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java @@ -0,0 +1,183 @@ +package org.sleuthkit.autopsy.datasourceprocessors; + +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +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; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskFileRange; +import org.openide.util.NbBundle.Messages; + +/* + * A runnable that adds a raw data source to a case database. + */ +final class AddRawImageTask implements Runnable { + + private static final Logger logger = Logger.getLogger(AddRawImageTask.class.getName()); + private final String deviceId; + private final String imageFilePath; + private final String timeZone; + private final long chunkSize; + private final DataSourceProcessorProgressMonitor progressMonitor; + private final DataSourceProcessorCallback callback; + private boolean criticalErrorOccurred; + private static final long TWO_GB = 2000000000L; + + /** + * Constructs a runnable that adds a raw data source to a 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 imageFilePath Path to a Raw data source file. + * @param timeZone The time zone to use when processing dates + * and times for the image, obtained from + * java.util.TimeZone.getID. + * @param breakupChunks 2GB or not breakup. + * @param progressMonitor Progress monitor for reporting + * progressMonitor during processing. + * @param callback Callback to call when processing is done. + */ + AddRawImageTask(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + this.deviceId = deviceId; + this.imageFilePath = imageFilePath; + this.timeZone = timeZone; + this.chunkSize = chunkSize; + this.callback = callback; + this.progressMonitor = progressMonitor; + } + + /** + * Adds a raw data source to a case database. + */ + @Override + public void run() { + /* + * Process the input image file. + */ + progressMonitor.setIndeterminate(true); + progressMonitor.setProgress(0); + List newDataSources = new ArrayList<>(); + List errorMessages = new ArrayList<>(); + addImageToCase(newDataSources, errorMessages); + + progressMonitor.setProgress(100); + + /** + * Return the results via the callback passed to the constructor. + */ + DataSourceProcessorCallback.DataSourceProcessorResult result; + 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); + criticalErrorOccurred = false; + } + + /** + * Attempts to add the input image to the case. + * + * @param newDataSources If the image is added, a data source is added to + * this list for eventual return to the caller via the + * callback. + * @param errorMessages If there are any error messages, the error messages + * are added to this list for eventual return to the + * caller via the callback. + */ + @Messages({"AddRawImageTask.progress.add.text=Adding raw image: ", + "AddRawImageTask.image.critical.error.adding=Critical error adding ", + "AddRawImageTask.for.device=for device ", + "AddRawImageTask.image.notExisting=is not existing.", + "AddRawImageTask.image.noncritical.error.adding=Non-critical error adding "}) + private void addImageToCase(List dataSources, List errorMessages) { + progressMonitor.setProgressText(Bundle.AddRawImageTask_progress_add_text() + imageFilePath); + List imageFilePaths = new ArrayList<>(); + SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase(); + caseDatabase.acquireExclusiveLock(); + + File imageFile = Paths.get(imageFilePath).toFile(); + if (!imageFile.exists()) { + errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddRawImageTask_for_device() + + deviceId + Bundle.AddRawImageTask_image_notExisting()); + criticalErrorOccurred = true; + return; + } + + imageFilePaths.add(imageFilePath); + + try { + /* + * Get Image that will be added to case + */ + Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone); //TODO: change hard coded deviceId. + dataSources.add(dataSource); + List fileRanges = new ArrayList<>(); + + /* + * Verify the size of the new image. Note that it may not be what is + * expected, but at least part of it was added to the case. + */ + String verificationError = dataSource.verifyImageSize(); + if (!verificationError.isEmpty()) { + errorMessages.add(Bundle.AddRawImageTask_image_noncritical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + verificationError); + } + + long imageSize = dataSource.getSize(); + int sequence = 0; + //start byte and end byte + long start = 0; + if (chunkSize > 0 && imageSize >= TWO_GB) { + for (double size = TWO_GB; size < dataSource.getSize(); size += TWO_GB) { + fileRanges.add(new TskFileRange(start, TWO_GB, sequence)); + start += TWO_GB; + sequence++; + } + + } + double leftoverSize = imageSize - sequence * TWO_GB; + fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence)); + + + caseDatabase.addLayoutFiles(dataSource, fileRanges); + + } catch (TskCoreException ex) { + errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); + criticalErrorOccurred = true; + } finally { + caseDatabase.releaseExclusiveLock(); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties new file mode 100755 index 0000000000..4a8de9a48b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties @@ -0,0 +1,12 @@ +# To change this license header, choose License Headers in Project Properties. +# To change this template file, choose Tools | Templates +# and open the template in the editor. + +RawDSInputPanel.pathLabel.text=Browse for an unallocated space image file: +RawDSInputPanel.errorLabel.text=Error Label +RawDSInputPanel.browseButton.text=Browse +RawDSInputPanel.pathTextField.text= +RawDSInputPanel.jBreakFileUpLabel.text=Break image up into: +RawDSInputPanel.jNoBreakupRadioButton.text=Do not break up +RawDSInputPanel.j2GBBreakupRadioButton.text=2GB chunks +RawDSInputPanel.timeZoneLabel.text=Please select the input timezone: diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form new file mode 100755 index 0000000000..ff07eaa18c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form @@ -0,0 +1,170 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java new file mode 100755 index 0000000000..8fb6fe5ccf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java @@ -0,0 +1,352 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datasourceprocessors; + +import java.io.File; +import java.util.Calendar; +import java.util.SimpleTimeZone; +import java.util.TimeZone; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.coreutils.PathValidator; + +final class RawDSInputPanel extends JPanel implements DocumentListener { + private static final long TWO_GB = 2000000000L; + private static final long serialVersionUID = 1L; //default + private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH"; + private final JFileChooser fc = new JFileChooser(); + // Externally supplied name is used to store settings + private final String contextName; + /** + * Creates new form RawDSInputPanel + */ + private RawDSInputPanel(String context) { + initComponents(); + + errorLabel.setVisible(false); + + fc.setDragEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + + this.contextName = context; + } + + /** + * Creates and returns an instance of a RawDSInputPanel. + */ + static synchronized RawDSInputPanel createInstance(String context) { + RawDSInputPanel instance = new RawDSInputPanel(context); + + instance.postInit(); + instance.createTimeZoneList(); + + return instance; + } + + //post-constructor initialization to properly initialize listener support + //without leaking references of uninitialized objects + private void postInit() { + pathTextField.getDocument().addDocumentListener(this); + } + + /** + * Creates the drop down list for the time zones and then makes the local + * machine time zone to be selected. + */ + private void createTimeZoneList() { + // load and add all timezone + String[] ids = SimpleTimeZone.getAvailableIDs(); + for (String id : ids) { + TimeZone zone = TimeZone.getTimeZone(id); + int offset = zone.getRawOffset() / 1000; + int hour = offset / 3600; + int minutes = (offset % 3600) / 60; + String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); + + timeZoneComboBox.addItem(item); + } + // get the current timezone + TimeZone thisTimeZone = Calendar.getInstance().getTimeZone(); + int thisOffset = thisTimeZone.getRawOffset() / 1000; + int thisHour = thisOffset / 3600; + int thisMinutes = (thisOffset % 3600) / 60; + String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); + + // set the selected timezone + timeZoneComboBox.setSelectedItem(formatted); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + infileTypeButtonGroup = new javax.swing.ButtonGroup(); + pathLabel = new javax.swing.JLabel(); + pathTextField = new javax.swing.JTextField(); + browseButton = new javax.swing.JButton(); + j2GBBreakupRadioButton = new javax.swing.JRadioButton(); + jBreakFileUpLabel = new javax.swing.JLabel(); + jNoBreakupRadioButton = new javax.swing.JRadioButton(); + errorLabel = new javax.swing.JLabel(); + timeZoneLabel = new javax.swing.JLabel(); + timeZoneComboBox = new javax.swing.JComboBox<>(); + + org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.pathLabel.text")); // NOI18N + + pathTextField.setText(org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.pathTextField.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.browseButton.text")); // NOI18N + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + + infileTypeButtonGroup.add(j2GBBreakupRadioButton); + j2GBBreakupRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(j2GBBreakupRadioButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.j2GBBreakupRadioButton.text")); // NOI18N + j2GBBreakupRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + j2GBBreakupRadioButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jBreakFileUpLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.jBreakFileUpLabel.text")); // NOI18N + + infileTypeButtonGroup.add(jNoBreakupRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(jNoBreakupRadioButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.jNoBreakupRadioButton.text")); // NOI18N + jNoBreakupRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jNoBreakupRadioButtonActionPerformed(evt); + } + }); + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.errorLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.timeZoneLabel.text")); // NOI18N + + timeZoneComboBox.setMaximumRowCount(30); + + 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, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pathLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 218, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(timeZoneLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 168, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(0, 19, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jBreakFileUpLabel) + .addComponent(errorLabel) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(j2GBBreakupRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jNoBreakupRadioButton))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, 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(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(timeZoneLabel) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorLabel) + .addGap(5, 5, 5) + .addComponent(jBreakFileUpLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jNoBreakupRadioButton) + .addComponent(j2GBBreakupRadioButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + @SuppressWarnings("deprecation") + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + String oldText = pathTextField.getText(); + // set the current directory of the FileChooser if the ImagePath Field is valid + File currentDir = new File(oldText); + if (currentDir.exists()) { + fc.setCurrentDirectory(currentDir); + } + + int retval = fc.showOpenDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + pathTextField.setText(path); + } + }//GEN-LAST:event_browseButtonActionPerformed + + private void j2GBBreakupRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_j2GBBreakupRadioButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_j2GBBreakupRadioButtonActionPerformed + + private void jNoBreakupRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jNoBreakupRadioButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jNoBreakupRadioButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton browseButton; + private javax.swing.JLabel errorLabel; + private javax.swing.ButtonGroup infileTypeButtonGroup; + private javax.swing.JRadioButton j2GBBreakupRadioButton; + private javax.swing.JLabel jBreakFileUpLabel; + private javax.swing.JRadioButton jNoBreakupRadioButton; + private javax.swing.JLabel pathLabel; + private javax.swing.JTextField pathTextField; + private javax.swing.JComboBox timeZoneComboBox; + private javax.swing.JLabel timeZoneLabel; + // End of variables declaration//GEN-END:variables + /** + * Get the path of the user selected image. + * + * @return the image path + */ + String getImageFilePath() { + return pathTextField.getText(); + } + + void reset() { + //reset the UI elements to default + pathTextField.setText(null); + j2GBBreakupRadioButton.setSelected(true); + } + + long getChunkSize() { + if (jNoBreakupRadioButton.isSelected()) { + return -1; + } else { //if have more choices here, the selection of each radiobutton should be checked + return TWO_GB; + } + } + + String getTimeZone() { + String tz = timeZoneComboBox.getSelectedItem().toString(); + return tz.substring(tz.indexOf(")") + 2).trim(); + } + + /** + * Should we enable the next button of the wizard? + * + * @return true if a proper image has been selected, false otherwise + */ + boolean validatePanel() { + errorLabel.setVisible(false); + String path = getImageFilePath(); + if (path == null || path.isEmpty()) { + return false; + } + + // display warning if there is one (but don't disable "next" button) + warnIfPathIsInvalid(path); + + boolean isExist = new File(path).exists(); + + return (isExist); + } + + /** + * Validates path to selected data source and displays warning if it is + * invalid. + * + * @param path Absolute path to the selected data source + */ + @Messages({"RawDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive"}) + private void warnIfPathIsInvalid(String path) { + if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.RawDSInputPanel_error_text()); + } + } + + void storeSettings() { + String inFilePath = getImageFilePath(); + if (null != inFilePath) { + String imagePath = inFilePath.substring(0, inFilePath.lastIndexOf(File.separator) + 1); + ModuleSettings.setConfigSetting(contextName, PROP_LASTINPUT_PATH, imagePath); + } + } + + void readSettings() { + String inFilePath = ModuleSettings.getConfigSetting(contextName, PROP_LASTINPUT_PATH); + if (null != inFilePath) { + if (!inFilePath.isEmpty()) { + pathTextField.setText(inFilePath); + } + } + } + + /** + * 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. + * + * @param e the event, which is ignored + */ + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + /** + * Set the focus to the pathTextField. + */ + void select() { + pathTextField.requestFocusInWindow(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java new file mode 100644 index 0000000000..68908ae7a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java @@ -0,0 +1,157 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datasourceprocessors; + +import java.util.UUID; +import javax.swing.JPanel; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; + +/** + * A Raw 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. + */ +@ServiceProvider(service = DataSourceProcessor.class) +public class RawDSProcessor implements DataSourceProcessor { + private final RawDSInputPanel configPanel; + private AddRawImageTask addImageTask; + + /* + * Constructs a Raw 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 RawDSProcessor() { + configPanel = RawDSInputPanel.createInstance(RawDSProcessor.class.getName()); + } + +/** + * Gets a string that describes the type of data sources this processor is + * able to add to the case database. The string is suitable for display in a + * type selection UI component (e.g., a combo box). + * + * @return A data source type display string for this data source processor. + */ + @Messages({"RawDSProcessor.dataSourceType=Unallocated Space Image File"}) + public static String getType() { + return Bundle.RawDSProcessor_dataSourceType(); + } + + /** + * Gets a string that describes the type of data sources this processor is + * able to add to the case database. The string is suitable for display in a + * type selection UI component (e.g., a combo box). + * + * @return A data source type display string for this data source processor. + */ + @Override + public String getDataSourceType() { + return Bundle.RawDSProcessor_dataSourceType(); + } + + /** + * 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) { + configPanel.storeSettings(); + run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), 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 rawDSInputFilePath Path to a Raw data source file. + * @param isHandsetFile Indicates whether the XML file is for a + * handset or a SIM. + * @param progressMonitor Progress monitor for reporting progress + * during processing. + */ + private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, callback); + new Thread(addImageTask).start(); + } + + + @Override + public void cancel() { + } + + /** + * Resets the selection and configuration panel for this data source + * processor. + */ + @Override + public void reset() { + configPanel.reset(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.form index 3f09e7e871..e00c4a966f 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.form @@ -21,209 +21,244 @@ - - - - - - - - + - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - -
- -
- - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - -
- - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java index adca3c0849..b2497baed8 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java @@ -78,7 +78,9 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme // //GEN-BEGIN:initComponents private void initComponents() { + jPanel1 = new javax.swing.JPanel(); externalViewerTitleLabel = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); jSplitPane1 = new javax.swing.JSplitPane(); exePanel = new javax.swing.JPanel(); exePathLabel = new javax.swing.JLabel(); @@ -111,7 +113,7 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme .addGroup(exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(exePathLabel) .addComponent(exePathNameLabel)) - .addContainerGap(114, Short.MAX_VALUE)) + .addContainerGap(159, Short.MAX_VALUE)) ); exePanelLayout.setVerticalGroup( exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -120,7 +122,7 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme .addComponent(exePathLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(exePathNameLabel) - .addContainerGap(411, Short.MAX_VALUE)) + .addContainerGap(408, Short.MAX_VALUE)) ); jSplitPane1.setRightComponent(exePanel); @@ -160,18 +162,20 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme .addGroup(rulesPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(ruleListLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(rulesPanelLayout.createSequentialGroup() .addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(rulesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 311, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(ruleListLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(rulesPanelLayout.createSequentialGroup() - .addComponent(newRuleButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(editRuleButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteRuleButton))) - .addGap(0, 18, Short.MAX_VALUE))) - .addContainerGap()) + .addComponent(rulesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 311, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + .addGroup(rulesPanelLayout.createSequentialGroup() + .addComponent(newRuleButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(editRuleButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteRuleButton) + .addGap(0, 0, Short.MAX_VALUE)))) ); rulesPanelLayout.setVerticalGroup( rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -179,36 +183,55 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme .addContainerGap() .addComponent(ruleListLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(rulesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 387, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(rulesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newRuleButton) .addComponent(editRuleButton) .addComponent(deleteRuleButton)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); jSplitPane1.setLeftComponent(rulesPanel); + jScrollPane1.setViewportView(jSplitPane1); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(externalViewerTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 777, Short.MAX_VALUE) + .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 777, Short.MAX_VALUE) + .addContainerGap())) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(externalViewerTitleLabel) + .addContainerGap(475, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(32, 32, 32) + .addComponent(jScrollPane1) + .addContainerGap())) + ); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jSplitPane1) - .addComponent(externalViewerTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) + .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(externalViewerTitleLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 458, Short.MAX_VALUE) - .addContainerGap()) + .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -334,6 +357,8 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme private javax.swing.JLabel exePathLabel; private javax.swing.JLabel exePathNameLabel; private javax.swing.JLabel externalViewerTitleLabel; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JButton newRuleButton; private javax.swing.JLabel ruleListLabel; diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index 656e22ef9e..23730c99c1 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -165,8 +165,9 @@ class FileSearchPanel extends javax.swing.JPanel { contentList = Collections.emptyList(); } + SearchNode sn = new SearchNode(contentList); final TopComponent searchResultWin = DataResultTopComponent.createInstance(title, pathText, - new TableFilterNode(new SearchNode(contentList), true), contentList.size()); + new TableFilterNode(sn, true, sn.getName()), contentList.size()); searchResultWin.requestActive(); // make it the active top component diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.form index 77189d6ef0..8c34097b98 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.form @@ -16,12 +16,12 @@ - + - + @@ -38,7 +38,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -79,6 +79,7 @@ + @@ -90,7 +91,6 @@ - @@ -102,7 +102,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -188,7 +188,7 @@ - + @@ -201,7 +201,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java index eb9f6d1b11..1d5470b4df 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java @@ -188,6 +188,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel .addGroup(mimePanelLayout.createSequentialGroup() .addContainerGap() .addGroup(mimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addGroup(mimePanelLayout.createSequentialGroup() .addGroup(mimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel1) @@ -195,8 +196,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel .addComponent(newTypeButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(removeTypeButton))) - .addGap(0, 191, Short.MAX_VALUE)) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 409, Short.MAX_VALUE)) + .addGap(0, 191, Short.MAX_VALUE))) .addContainerGap()) ); mimePanelLayout.setVerticalGroup( @@ -205,7 +205,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel .addContainerGap() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 401, Short.MAX_VALUE) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(mimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newTypeButton) @@ -251,7 +251,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel .addComponent(newExtButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(removeExtButton))) - .addGap(0, 40, Short.MAX_VALUE))) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); extensionPanelLayout.setVerticalGroup( @@ -260,7 +260,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel .addContainerGap() .addComponent(extHeaderLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 401, Short.MAX_VALUE) + .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(extensionPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newExtButton) @@ -278,14 +278,14 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addComponent(jScrollPane1) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 797, Short.MAX_VALUE) .addContainerGap()) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addComponent(jScrollPane1) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE) .addContainerGap()) ); @@ -293,11 +293,11 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 817, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 526, Short.MAX_VALUE) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties index 4dfed1d15b..7225dbedf6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties @@ -28,12 +28,10 @@ FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=Missing Interesting Files Set Name FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=Save Failed FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=Load Failed -FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to file type definitions when ingest is running! FileTypeIdGlobalSettingsPanel.loadFileTypes.errorMessage=Failed to load existing file type definitions. FileTypeIdGlobalSettingsPanel.saveFileTypes.errorMessage=Failed to save file type definitions. FileTypeIdGlobalSettingsPanel.newTypeButton.text=New Type FileTypeIdGlobalSettingsPanel.jLabel2.text=Custom MIME Types: -FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy can automatically detect many file types. Add your custom file types here. FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=Error initializing the file type detector. AddFileTypeSignaturePanel.offsetLabel.text=Byte Offset AddFileTypeSignaturePanel.signatureTextField.text= @@ -53,3 +51,5 @@ AddFileTypePanel.addSigButton.text=Add Signature AddFileTypePanel.postHitCheckBox.text=Alert as an "Interesting File" when found AddFileTypePanel.setNameLabel.text=Set Name AddFileTypePanel.setNameTextField.text= +FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to file type definitions when ingest is running! +FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy can automatically detect many file types. Add your custom file types here. diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties index ef48c700dc..61626e69aa 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties @@ -9,7 +9,6 @@ FileTypeIdModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\ FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u6570\u306b\u3088\u3063\u3066\u306f\u3001\u3053\u306e\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3059\u308b\u3053\u3068\u306b\u3088\u308a\u3001\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a\u3092\u52a0\u901f\u3057\u307e\u3059\u3002 FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NSRL\uff09\u3092\u30b9\u30ad\u30c3\u30d7 FileTypeIdGlobalSettingsPanel.deleteTypeButton.text=\u524a\u9664 -FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u306b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\uff01 FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\u540d\u304c\u6b20\u3051\u3066\u3044\u307e\u3059 FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.message=MIME\u30bf\u30a4\u30d7\u304c\u5fc5\u8981\u3067\u3059\u3002 FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.title=MIME\u30bf\u30a4\u30d7\u304c\u6b20\u3051\u3066\u3044\u307e\u3059 @@ -33,10 +32,11 @@ FileTypeIdGlobalSettingsPanel.offsetComboBox.startItem=\u958b\u59cb FileTypeIdGlobalSettingsPanel.offsetComboBox.endItem=\u505c\u6b62 FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.length=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u30b7\u30b0\u30cd\u30c1\u30e3\u30b5\u30a4\u30ba\u3088\u308a\u5c0f\u3055\u304f\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u3002 FileTypeIdGlobalSettingsPanel.jLabel2.text=MIME\u30bf\u30a4\u30d7\uff1a -FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy\u306f\u81ea\u52d5\u7684\u306b\u591a\u304f\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u691c\u77e5\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u306b\u306f\u3042\u306a\u305f\u306e\u30ab\u30b9\u30bf\u30e0\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u30c7\u30a3\u30c6\u30af\u30bf\u3092\u8d77\u52d5\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 AddFileTypeSignaturePanel.signatureTypeLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3\u30bf\u30a4\u30d7 AddFileTypeSignaturePanel.signatureLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3 AddFileTypeSignaturePanel.offsetRelativeToLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b21\u3068\u76f8\u5bfe\u7684 AddFileTypeSignaturePanel.offsetLabel.text=\u30d0\u30a4\u30c8\u30aa\u30d5\u30bb\u30c3\u30c8 AddFileTypePanel.mimeTypeLabel.text=MIME\u30bf\u30a4\u30d7 +FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3092\u5b9f\u884c\u4e2d\u306b\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\uff01 +FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy\u306f\u81ea\u52d5\u7684\u306b\u591a\u304f\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u691c\u77e5\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u306b\u306f\u3042\u306a\u305f\u306e\u30ab\u30b9\u30bf\u30e0\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form index 9e4f54df43..c24125cec4 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.form @@ -51,14 +51,6 @@ - - - - - - - - @@ -74,15 +66,12 @@ - + - - - - + @@ -156,7 +145,7 @@ - + @@ -167,7 +156,6 @@ - @@ -225,8 +213,9 @@ + - + @@ -234,7 +223,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java index 83c142bef7..6954a9d77f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java @@ -527,9 +527,6 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan jButton3.setFont(jButton3.getFont().deriveFont(jButton3.getFont().getStyle() & ~java.awt.Font.BOLD, 14)); org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(HashLookupSettingsPanel.class, "HashLookupSettingsPanel.jButton3.text")); // NOI18N - setMinimumSize(new java.awt.Dimension(700, 500)); - setPreferredSize(new java.awt.Dimension(750, 500)); - ingestWarningLabel.setFont(ingestWarningLabel.getFont().deriveFont(ingestWarningLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); ingestWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/hashdatabase/warning16.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(ingestWarningLabel, org.openide.util.NbBundle.getMessage(HashLookupSettingsPanel.class, "HashLookupSettingsPanel.ingestWarningLabel.text")); // NOI18N @@ -712,8 +709,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 132, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) + .addGap(0, 0, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -759,14 +755,15 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan .addGap(18, 18, 18) .addComponent(sendIngestMessagesCheckBox) .addGap(18, 18, 18) - .addComponent(ingestWarningLabel)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 423, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(ingestWarningLabel) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jScrollPane1)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(createDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(29, Short.MAX_VALUE)) + .addContainerGap()) ); jScrollPane2.setViewportView(jPanel1); @@ -775,13 +772,11 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane2) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 789, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jScrollPane2) - .addContainerGap()) + .addComponent(jScrollPane2) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.form index 483d6e386e..b4c3076206 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.form @@ -29,7 +29,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.java index df527b887d..ea1b2f2fdb 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemDefsPanel.java @@ -901,7 +901,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 762, Short.MAX_VALUE) + .addComponent(jScrollPane1) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties index 76c16fd56f..b454208635 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties @@ -1,6 +1,5 @@ VMExtractorIngestModuleFactory.moduleDisplayName=Virtual Machine Extractor VMExtractorIngestModuleFactory.moduleDescription=Extracts virtual machine files and adds them to a case as data sources. -VMExtractorIngestModuleFactory.version=1.0 VMExtractorIngestModule.addedVirtualMachineImage.message=Added virtual machine image {0} VMExtractorIngestModule.searchingImage.message=Searching image for virtual machine files VMExtractorIngestModule.exportingToDisk.message=Exporting virtual machine files to disk diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java index e26bb81e51..c525e87dad 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.modules.vmextractor; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; @@ -53,7 +54,7 @@ public final class VMExtractorIngestModuleFactory extends IngestModuleFactoryAda @Override public String getModuleVersionNumber() { - return NbBundle.getMessage(this.getClass(), "VMExtractorIngestModuleFactory.version"); + return Version.getVersion(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index 6ed903c7b2..6f32fd2f65 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -170,6 +170,8 @@ class ReportGenerator { TableReportGenerator generator = new TableReportGenerator(artifactTypeSelections, tagNameSelections, progressPanel, tableReport); generator.execute(); tableReport.endReport(); + // finish progress, wrap up + progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); errorList = generator.getErrorList(); }); worker.execute(); diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java index c6e0f8d0b1..d3a782948c 100755 --- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java @@ -114,9 +114,6 @@ class TableReportGenerator { // report on the tagged images makeThumbnailTable(); } - - // finish progress, wrap up - progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); } /** @@ -1495,6 +1492,9 @@ class TableReportGenerator { } else if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { columns.add(new StatusColumn()); attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); + attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); + attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID)); } else { // This is the case that it is a custom type. The reason an else is // necessary is to make sure that the source file column is added diff --git a/CoreLibs/ivy.xml b/CoreLibs/ivy.xml index 098bca5b26..26859599e2 100644 --- a/CoreLibs/ivy.xml +++ b/CoreLibs/ivy.xml @@ -16,7 +16,7 @@ - + diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties index a3306356a1..3f1452877a 100644 --- a/CoreLibs/nbproject/project.properties +++ b/CoreLibs/nbproject/project.properties @@ -22,7 +22,7 @@ file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar file.reference.gson-1.4.jar=release/modules/ext/gson-1.4.jar file.reference.gstreamer-java-1.5.jar=release/modules/ext/gstreamer-java-1.5.jar -file.reference.guava-18.0.jar=release/modules/ext/guava-18.0.jar +file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar file.reference.imageio-icns-3.2.jar=release/modules/ext/imageio-icns-3.2.jar @@ -75,7 +75,7 @@ javac.compilerargs=-Xlint -Xlint:-serial javadoc.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-javadoc.jar javadoc.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-javadoc.jar javadoc.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-javadoc.jar -javadoc.reference.guava-18.0.jar=release/modules/ext/guava-18.0-javadoc.jar +javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar javadoc.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4-javadoc.jar javadoc.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4-javadoc.jar javadoc.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-javadoc.jar @@ -83,7 +83,7 @@ nbm.needs.restart=true source.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-sources.jar source.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-sources.jar source.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-sources.jar -source.reference.guava-18.0.jar=release/modules/ext/guava-18.0-sources.jar +source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar source.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4-sources.jar source.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4-sources.jar source.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-sources.jar diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index 25b062dcd9..358637d979 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -743,10 +743,6 @@ ext/mail-1.4.3.jar release/modules/ext/mail-1.4.3.jar - - ext/guava-18.0.jar - release/modules/ext/guava-18.0.jar - ext/imageio-tga-3.2.jar release/modules/ext/imageio-tga-3.2.jar @@ -851,6 +847,10 @@ ext/slf4j-simple-1.6.1.jar release/modules/ext/slf4j-simple-1.6.1.jar + + ext/guava-19.0.jar + release/modules/ext/guava-19.0.jar + ext/imageio-bmp-3.2.jar release/modules/ext/imageio-bmp-3.2.jar diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileChunk.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileChunk.java index 83dac4645b..5253e5e240 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileChunk.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileChunk.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,47 +19,73 @@ package org.sleuthkit.autopsy.keywordsearch; import java.nio.charset.Charset; -import org.openide.util.NbBundle; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; /** - * Represents each string chunk to be indexed, a derivative of TextExtractor - * file + * A representation of a chunk of text from a file that can be used, when + * supplied with an Ingester, to index the chunk for search. */ -class AbstractFileChunk { +final class AbstractFileChunk { - private int chunkID; - private TextExtractor parent; + private final int chunkNumber; + private final TextExtractor textExtractor; - AbstractFileChunk(TextExtractor parent, int chunkID) { - this.parent = parent; - this.chunkID = chunkID; - } - - public TextExtractor getParent() { - return parent; - } - - public int getChunkId() { - return chunkID; + /** + * Constructs a representation of a chunk of text from a file that can be + * used, when supplied with an Ingester, to index the chunk for search. + * + * @param textExtractor A TextExtractor for the file. + * @param chunkNumber A sequence number for the chunk. + */ + AbstractFileChunk(TextExtractor textExtractor, int chunkNumber) { + this.textExtractor = textExtractor; + this.chunkNumber = chunkNumber; } /** - * return String representation of the absolute id (parent and child) + * Gets the TextExtractor for the source file of the text chunk. * - * @return + * @return A reference to the TextExtractor. */ - String getIdString() { - return Server.getChunkIdString(this.parent.getSourceFile().getId(), this.chunkID); + TextExtractor getTextExtractor() { + return textExtractor; } - void index(Ingester ingester, byte[] content, long contentSize, Charset indexCharset) throws IngesterException { - ByteContentStream bcs = new ByteContentStream(content, contentSize, parent.getSourceFile(), indexCharset); + /** + * Gets the sequence number of the text chunk. + * + * @return The chunk number. + */ + int getChunkNumber() { + return chunkNumber; + } + + /** + * Gets the id of the text chunk. + * + * @return An id of the form [source file object id]_[chunk number] + */ + String getChunkId() { + return Server.getChunkIdString(this.textExtractor.getSourceFile().getId(), this.chunkNumber); + } + + /** + * Indexes the text chunk. + * + * @param ingester An Ingester to do the indexing. + * @param chunkBytes The raw bytes of the text chunk. + * @param chunkSize The size of the text chunk in bytes. + * @param charSet The char set to use during indexing. + * + * @throws org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException + */ + void index(Ingester ingester, byte[] chunkBytes, long chunkSize, Charset charSet) throws IngesterException { + ByteContentStream bcs = new ByteContentStream(chunkBytes, chunkSize, textExtractor.getSourceFile(), charSet); try { - ingester.ingest(this, bcs, content.length); - } catch (Exception ingEx) { - throw new IngesterException(NbBundle.getMessage(this.getClass(), "AbstractFileChunk.index.exception.msg", - parent.getSourceFile().getId(), chunkID), ingEx); + ingester.ingest(this, bcs, chunkBytes.length); + } catch (Exception ex) { + throw new IngesterException(String.format("Error ingesting (indexing) file chunk: %s", getChunkId()), ex); } } + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileStringContentStream.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileStringContentStream.java index b62cc46bba..e8a7efdde0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileStringContentStream.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileStringContentStream.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,6 @@ import java.io.Reader; import java.nio.charset.Charset; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; import org.apache.solr.common.util.ContentStream; import org.sleuthkit.datamodel.AbstractContent; import org.sleuthkit.datamodel.AbstractFile; @@ -36,11 +35,10 @@ import org.sleuthkit.datamodel.AbstractFile; class AbstractFileStringContentStream implements ContentStream { //input - private AbstractFile content; - private Charset charset; + private final AbstractFile content; + private final Charset charset; //converted - private InputStream stream; - private static Logger logger = Logger.getLogger(AbstractFileStringContentStream.class.getName()); + private final InputStream stream; public AbstractFileStringContentStream(AbstractFile content, Charset charset, InputStream inputStream) { this.content = content; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 66a007a67c..1ac7f0fbbd 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -157,7 +157,6 @@ DropdownSearchPanel.cutMenuItem.text=Cut DropdownSearchPanel.selectAllMenuItem.text=Select All DropdownSearchPanel.pasteMenuItem.text=Paste DropdownSearchPanel.copyMenuItem.text=Copy -AbstractFileChunk.index.exception.msg=Problem ingesting file string chunk\: {0}, chunk\: {1} AbstractFileStringContentStream.getSize.exception.msg=Cannot tell how many chars in converted string, until entire string is converted AbstractFileStringContentStream.getSrcInfo.text=File\:{0} ByteContentStream.getSrcInfo.text=File\:{0} @@ -187,7 +186,6 @@ Ingester.FscContentStream.getSrcInfo=File\:{0} Ingester.FscContentStream.getReader=Not supported yet. Ingester.NullContentStream.getSrcInfo.text=File\:{0} Ingester.NullContentStream.getReader=Not supported yet. -Keyword.toString.text=Keyword'{'query\={0}, isLiteral\={1}, keywordType\={2}'}' KeywordSearch.moduleErr=Module Error KeywordSearch.fireNumIdxFileChg.moduleErr.msg=A module caused an error listening to KeywordSearch updates. See log to determine which module. Some data could be incomplete. KeywordSearchListsEncase.save.exception.msg=Not supported yet. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties index 827842afd2..b96be19429 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties @@ -133,7 +133,6 @@ OptionsCategory_Keywords_KeywordSearchOptions=\u30ad\u30fc\u30ef\u30fc\u30c9\u69 ExtractedContentPanel.pageOfLabel.text=of ExtractedContentPanel.pageCurLabel.text=- ExtractedContentPanel.pageTotalLabel.text=- -AbstractFileChunk.index.exception.msg=\u30d5\u30a1\u30a4\u30eb\u30b9\u30c8\u30ea\u30f3\u30b0\u30c1\u30e3\u30f3\u30af\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u4e2d\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a {0}, \u30c1\u30e3\u30f3\u30af\: {1} AbstractFileStringContentStream.getSize.exception.msg=\u30b9\u30c8\u30ea\u30f3\u30b0\u5168\u4f53\u304c\u5909\u63db\u3055\u308c\u306a\u3051\u308c\u3070\u3001\u5909\u63db\u3055\u308c\u305f\u30b9\u30c8\u30ea\u30f3\u30b0\u5185\u306e\u30ad\u30e3\u30e9\u30af\u30bf\u30fc\u6570\u306f\u4e0d\u660e\u3067\u3059\u3002 AbstractFileStringContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb\uff1a{0} ByteContentStream.getSrcInfo.text=\u30d5\u30a1\u30a4\u30eb\uff1a{0} @@ -208,7 +207,6 @@ KeywordSearchIngestModule.doInBackGround.pendingMsg=\uff08\u30da\u30f3\u30c7\u30 SearchRunner.doInBackGround.cancelMsg=\uff08\u30ad\u30e3\u30f3\u30bb\u30eb\u4e2d\u2026\uff09 Server.addDoc.exception.msg2=\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30cf\u30f3\u30c9\u30e9\u30fc\u3092\u4f7f\u7528\u3057\u307e\u3057\u305f\u304c\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u6b21\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} ExtractedContentViewer.getSolrContent.txtBodyItal={0} -Keyword.toString.text=Keyword'{'query\={0}, isLiteral\={1}, keywordType\={2}'}' KeywordSearchJobSettingsPanel.keywordSearchEncodings.text=- KeywordSearchJobSettingsPanel.languagesValLabel.text=- KeywordSearchJobSettingsPanel.encodingsLabel.text=\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\uff1a diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java index a58328c113..7a1986d341 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java @@ -562,8 +562,8 @@ class DropdownListSearchPanel extends KeywordSearchPanel { Boolean regex; KeywordTableEntry(Keyword keyword) { - this.name = keyword.getQuery(); - this.regex = !keyword.isLiteral(); + this.name = keyword.getSearchTerm(); + this.regex = !keyword.searchTermIsLiteral(); } @Override diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleKeywordSearchPanel.form similarity index 100% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form rename to KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleKeywordSearchPanel.form diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleKeywordSearchPanel.java similarity index 68% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java rename to KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleKeywordSearchPanel.java index 4f5ffe545b..9201bcfddf 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleKeywordSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.keywordsearch; -import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -27,11 +26,15 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.swing.JMenuItem; - import org.sleuthkit.autopsy.coreutils.Logger; /** - * A simple UI for finding text after ingest + * A dropdown panel that provides GUI components that allow a user to do three + * types of ad hoc single keyword searches. The first option is a standard + * Lucene query for one or more terms, with or without wildcards and explicit + * Boolean operators, or a phrase. The second option is a Lucene query for a + * substring of a single term. The third option is a regex query using first the + * terms component, followed by standard Lucene queries for any terms found. * * The toolbar uses a different font from the rest of the application, * Monospaced 14, due to the necessity to find a font that displays both Arabic @@ -39,48 +42,62 @@ import org.sleuthkit.autopsy.coreutils.Logger; * perform this task at the desired size, and neither could numerous other * fonts. */ -public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { +public class DropdownSingleKeywordSearchPanel extends KeywordSearchPanel { - private static final Logger logger = Logger.getLogger(DropdownSingleTermSearchPanel.class.getName()); - private static DropdownSingleTermSearchPanel instance = null; + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(DropdownSingleKeywordSearchPanel.class.getName()); + private static DropdownSingleKeywordSearchPanel defaultInstance = null; /** - * Creates new form DropdownSingleTermSearchPanel + * Gets the default instance of a dropdown panel that provides GUI + * components that allow a user to do three types of ad hoc single keyword + * searches. */ - public DropdownSingleTermSearchPanel() { + public static synchronized DropdownSingleKeywordSearchPanel getDefault() { + if (null == defaultInstance) { + defaultInstance = new DropdownSingleKeywordSearchPanel(); + } + return defaultInstance; + } + + /** + * Constructs a dropdown panel that provides GUI components that allow a + * user to do three types of ad hoc single keyword searches. + */ + public DropdownSingleKeywordSearchPanel() { initComponents(); customizeComponents(); } + /** + * Does additional initialization of the GUI components created by the + * initComponents method. + */ private void customizeComponents() { keywordTextField.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { - //do nothing } @Override public void focusLost(FocusEvent e) { if (keywordTextField.getText().equals("")) { - resetSearchBox(); + clearSearchBox(); } } }); keywordTextField.setComponentPopupMenu(rightClickMenu); - ActionListener actList = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JMenuItem jmi = (JMenuItem) e.getSource(); - if (jmi.equals(cutMenuItem)) { - keywordTextField.cut(); - } else if (jmi.equals(copyMenuItem)) { - keywordTextField.copy(); - } else if (jmi.equals(pasteMenuItem)) { - keywordTextField.paste(); - } else if (jmi.equals(selectAllMenuItem)) { - keywordTextField.selectAll(); - } + ActionListener actList = (ActionEvent e) -> { + JMenuItem jmi = (JMenuItem) e.getSource(); + if (jmi.equals(cutMenuItem)) { + keywordTextField.cut(); + } else if (jmi.equals(copyMenuItem)) { + keywordTextField.copy(); + } else if (jmi.equals(pasteMenuItem)) { + keywordTextField.paste(); + } else if (jmi.equals(selectAllMenuItem)) { + keywordTextField.selectAll(); } }; cutMenuItem.addActionListener(actList); @@ -89,36 +106,43 @@ public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { selectAllMenuItem.addActionListener(actList); } - public static synchronized DropdownSingleTermSearchPanel getDefault() { - if (instance == null) { - instance = new DropdownSingleTermSearchPanel(); - } - return instance; - } - + /** + * Add an action listener to the Search buttom component of the panel. + * + * @param actionListener The actin listener. + */ void addSearchButtonActionListener(ActionListener actionListener) { searchButton.addActionListener(actionListener); } - void resetSearchBox() { + /** + * Clears the text in the query text field, i.e., sets it to the emtpy + * string. + */ + void clearSearchBox() { keywordTextField.setText(""); } + /** + * Gets a single keyword list consisting of a single keyword encapsulating + * the input term(s)/phrase/substring/regex. + * + * @return The keyword list. + */ @Override List getKeywordLists() { List keywords = new ArrayList<>(); - keywords.add(new Keyword(keywordTextField.getText(), - !regexRadioButton.isSelected(), exactRadioButton.isSelected())); - + keywords.add(new Keyword(keywordTextField.getText(), !regexRadioButton.isSelected(), exactRadioButton.isSelected())); List keywordLists = new ArrayList<>(); keywordLists.add(new KeywordList(keywords)); - return keywordLists; } + /** + * Not implemented. + */ @Override protected void postFilesIndexedChange() { - //nothing to update } /** @@ -142,20 +166,20 @@ public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { substringRadioButton = new javax.swing.JRadioButton(); regexRadioButton = new javax.swing.JRadioButton(); - org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N rightClickMenu.add(cutMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(copyMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.copyMenuItem.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(copyMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.copyMenuItem.text")); // NOI18N rightClickMenu.add(copyMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(pasteMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.pasteMenuItem.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(pasteMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.pasteMenuItem.text")); // NOI18N rightClickMenu.add(pasteMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(selectAllMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.selectAllMenuItem.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(selectAllMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.selectAllMenuItem.text")); // NOI18N rightClickMenu.add(selectAllMenuItem); - keywordTextField.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N NON-NLS - keywordTextField.setText(org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.keywordTextField.text")); // NOI18N + keywordTextField.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N + keywordTextField.setText(org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.keywordTextField.text")); // NOI18N keywordTextField.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(192, 192, 192), 1, true)); keywordTextField.setMinimumSize(new java.awt.Dimension(2, 25)); keywordTextField.setPreferredSize(new java.awt.Dimension(2, 25)); @@ -170,8 +194,8 @@ public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { } }); - searchButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/search-icon.png"))); // NOI18N NON-NLS - org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.searchButton.text")); // NOI18N + searchButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/search-icon.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.searchButton.text")); // NOI18N searchButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { searchButtonActionPerformed(evt); @@ -180,13 +204,13 @@ public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { queryTypeButtonGroup.add(exactRadioButton); exactRadioButton.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(exactRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.exactRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(exactRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.exactRadioButton.text")); // NOI18N queryTypeButtonGroup.add(substringRadioButton); - org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.substringRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.substringRadioButton.text")); // NOI18N queryTypeButtonGroup.add(regexRadioButton); - org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.regexRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleKeywordSearchPanel.class, "DropdownSearchPanel.regexRadioButton.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -224,18 +248,33 @@ public class DropdownSingleTermSearchPanel extends KeywordSearchPanel { ); }// //GEN-END:initComponents + /** + * Action performed by the action listener for the search button. + * + * @param evt The action event. + */ private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed keywordTextFieldActionPerformed(evt); }//GEN-LAST:event_searchButtonActionPerformed + /** + * Action performed by the action listener for the keyword text field. + * + * @param evt The action event. + */ private void keywordTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keywordTextFieldActionPerformed try { search(); } catch (Exception e) { - logger.log(Level.SEVERE, "search() threw exception", e); //NON-NLS + LOGGER.log(Level.SEVERE, "Error performing ad hoc single keyword search", e); //NON-NLS } }//GEN-LAST:event_keywordTextFieldActionPerformed + /** + * Mouse event handler for the keyword text field. + * + * @param evt The mouse event. + */ private void keywordTextFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_keywordTextFieldMouseClicked if (evt.isPopupTrigger()) { rightClickMenu.show(evt.getComponent(), evt.getX(), evt.getY()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java index 7c24b99337..70219e3f3e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java @@ -1,26 +1,21 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011 Basis Technology Corp. + * + * Copyright 2003-2016 Basis Technology Corp. * Contact: carrier sleuthkit org - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * DropdownToolbar - * - */ package org.sleuthkit.autopsy.keywordsearch; import java.awt.event.ActionEvent; @@ -37,22 +32,26 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.core.RuntimeProperties; /** - * Keyword search tool bar (in upper right, by default) with drop down panels - * for ad hoc searches by list or expression. + * A panel that provides a toolbar button for the dropdown keyword list search + * panel and dropdown single keyword search panel. Displayed in the upper right + * hand corner of the application by default. */ class DropdownToolbar extends javax.swing.JPanel { - private static final Logger logger = Logger.getLogger(DropdownToolbar.class.getName()); - private KeywordPropertyChangeListener listener; - private boolean active = false; + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(DropdownToolbar.class.getName()); private static DropdownToolbar instance; - private DropdownSingleTermSearchPanel dropPanel = null; - - private DropdownToolbar() { - initComponents(); - customizeComponents(); - } + private SearchSettingsChangeListener searchSettingsChangeListener; + private boolean active = false; + private DropdownSingleKeywordSearchPanel dropPanel = null; + /** + * Gets the singleton panel that provides a toolbar button for the dropdown + * keyword list search panel and dropdown single keyword search panel. + * Displayed in the upper right hand corner of the application by default. + * + * @return The panel. + */ public synchronized static DropdownToolbar getDefault() { if (instance == null) { instance = new DropdownToolbar(); @@ -60,18 +59,30 @@ class DropdownToolbar extends javax.swing.JPanel { return instance; } + /** + * Constructs a panel that provides a toolbar button for the dropdown + * keyword list search panel and dropdown single keyword search panel. + * Displayed in the upper right hand corner of the application by default. + */ + private DropdownToolbar() { + initComponents(); + customizeComponents(); + } + + /** + * Does additional initialization of the GUI components created by the + * initComponents method. + */ private void customizeComponents() { - listener = new KeywordPropertyChangeListener(); - KeywordSearch.getServer().addServerActionListener(listener); - Case.addPropertyChangeListener(listener); + searchSettingsChangeListener = new SearchSettingsChangeListener(); + KeywordSearch.getServer().addServerActionListener(searchSettingsChangeListener); + Case.addPropertyChangeListener(searchSettingsChangeListener); DropdownListSearchPanel listsPanel = DropdownListSearchPanel.getDefault(); - listsPanel.addSearchButtonActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - listsMenu.setVisible(false); - } + listsPanel.addSearchButtonActionListener((ActionEvent e) -> { + listsMenu.setVisible(false); }); + // Adding border of six to account for menu border listsMenu.setSize(listsPanel.getPreferredSize().width + 6, listsPanel.getPreferredSize().height + 6); listsMenu.add(listsPanel); @@ -92,7 +103,7 @@ class DropdownToolbar extends javax.swing.JPanel { } }); - dropPanel = DropdownSingleTermSearchPanel.getDefault(); + dropPanel = DropdownSingleKeywordSearchPanel.getDefault(); dropPanel.addSearchButtonActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -120,6 +131,27 @@ class DropdownToolbar extends javax.swing.JPanel { } + private void maybeShowListsPopup(MouseEvent evt) { + if (!active) { + return; + } + if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { + return; + } + listsMenu.show(listsButton, listsButton.getWidth() - listsMenu.getWidth(), listsButton.getHeight() - 1); + } + + private void maybeShowSearchPopup(MouseEvent evt) { + if (!active) { + return; + } + if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { + return; + } + searchMenu.show(searchDropButton, searchDropButton.getWidth() - searchMenu.getWidth(), searchDropButton.getHeight() - 1); + } + + /** * 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 @@ -216,13 +248,13 @@ class DropdownToolbar extends javax.swing.JPanel { private javax.swing.JPopupMenu searchMenu; // End of variables declaration//GEN-END:variables - private class KeywordPropertyChangeListener implements PropertyChangeListener { + private class SearchSettingsChangeListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { String changed = evt.getPropertyName(); if (changed.equals(Case.Events.CURRENT_CASE.toString())) { - dropPanel.resetSearchBox(); + dropPanel.clearSearchBox(); setFields(null != evt.getNewValue() && RuntimeProperties.coreComponentsAreActive()); } else if (changed.equals(Server.CORE_EVT)) { final Server.CORE_EVT_STATES state = (Server.CORE_EVT_STATES) evt.getNewValue(); @@ -232,9 +264,9 @@ class DropdownToolbar extends javax.swing.JPanel { final int numIndexedFiles = KeywordSearch.getServer().queryNumIndexedFiles(); KeywordSearch.fireNumIndexedFilesChange(null, numIndexedFiles); } catch (NoOpenCoreException ex) { - logger.log(Level.SEVERE, "Error executing Solr query, {0}", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error executing Solr query, {0}", ex); //NON-NLS } catch (KeywordSearchModuleException se) { - logger.log(Level.SEVERE, "Error executing Solr query, {0}", se.getMessage()); //NON-NLS + LOGGER.log(Level.SEVERE, "Error executing Solr query, {0}", se.getMessage()); //NON-NLS } break; case STOPPED: @@ -251,23 +283,4 @@ class DropdownToolbar extends javax.swing.JPanel { } } - private void maybeShowListsPopup(MouseEvent evt) { - if (!active) { - return; - } - if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { - return; - } - listsMenu.show(listsButton, listsButton.getWidth() - listsMenu.getWidth(), listsButton.getHeight() - 1); - } - - private void maybeShowSearchPopup(MouseEvent evt) { - if (!active) { - return; - } - if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { - return; - } - searchMenu.show(searchDropButton, searchDropButton.getWidth() - searchMenu.getWidth(), searchDropButton.getHeight() - 1); - } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java index 4935861899..5ec5463d8e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java @@ -100,7 +100,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis lsm.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { - if (lsm.isSelectionEmpty() || currentKeywordList.isLocked() || IngestManager.getInstance().isIngestRunning()) { + if (lsm.isSelectionEmpty() || currentKeywordList.isEditable() || IngestManager.getInstance().isIngestRunning()) { deleteWordButton.setEnabled(false); } else { deleteWordButton.setEnabled(true); @@ -140,7 +140,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis listOptionsSeparator.setEnabled(canEditList); // items that need an unlocked list w/out ingest running - boolean isListLocked = ((isListSelected == false) || (currentKeywordList.isLocked())); + boolean isListLocked = ((isListSelected == false) || (currentKeywordList.isEditable())); boolean canAddWord = isListSelected && !isIngestRunning && !isListLocked; newWordButton.setEnabled(canAddWord); keywordOptionsLabel.setEnabled(canAddWord); @@ -581,10 +581,10 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis Keyword word = currentKeywordList.getKeywords().get(rowIndex); switch (columnIndex) { case 0: - ret = (Object) word.getQuery(); + ret = (Object) word.getSearchTerm(); break; case 1: - ret = (Object) !word.isLiteral(); + ret = (Object) !word.searchTermIsLiteral(); break; default: logger.log(Level.SEVERE, "Invalid table column index: {0}", columnIndex); //NON-NLS diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java index 48893e213c..6bae3b666c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java @@ -82,7 +82,7 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option } XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent(); - if (writer.listExists(listName) && writer.getList(listName).isLocked()) { + if (writer.listExists(listName) && writer.getList(listName).isEditable()) { KeywordSearchUtil.displayDialog(FEATURE_NAME, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); return; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java index 8d04e2a8f6..031ec3199f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java @@ -176,7 +176,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa } boolean shouldAdd = false; if (writer.listExists(listName)) { - if (writer.getList(listName).isLocked()) { + if (writer.getList(listName).isEditable()) { boolean replace = KeywordSearchUtil.displayConfirmDialog( NbBundle.getMessage(this.getClass(), "KeywordSearch.newKeywordListMsg"), NbBundle.getMessage(this.getClass(), "KeywordSearchListsManagementPanel.newKeywordListDescription", listName), diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index e7325f776c..169e9cb0ec 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,7 +59,7 @@ class Ingester { //for ingesting chunk as SolrInputDocument (non-content-streaming, by-pass tika) //TODO use a streaming way to add content to /update handler private static final int MAX_DOC_CHUNK_SIZE = 1024 * 1024; - private static final String docContentEncoding = "UTF-8"; //NON-NLS + private static final String ENCODING = "UTF-8"; //NON-NLS private Ingester() { } @@ -134,7 +134,7 @@ class Ingester { //overwrite id with the chunk id params.put(Server.Schema.ID.toString(), - Server.getChunkIdString(sourceContent.getId(), fec.getChunkId())); + Server.getChunkIdString(sourceContent.getId(), fec.getChunkNumber())); ingest(bcs, params, size); } @@ -298,7 +298,7 @@ class Ingester { if (read != 0) { String s = ""; try { - s = new String(docChunkContentBuf, 0, read, docContentEncoding); + s = new String(docChunkContentBuf, 0, read, ENCODING); // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index char[] chars = null; for (int i = 0; i < s.length(); i++) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java index 293244c6f1..93acd0ee78 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,81 +18,136 @@ */ package org.sleuthkit.autopsy.keywordsearch; -import org.openide.util.NbBundle; import org.sleuthkit.datamodel.BlackboardAttribute; /** - * Representation of single keyword to search for + * A representation of a keyword for which to search. The search term for the + * keyword may be either a literal term, to be treated as either a whole word or + * a substring, or a regex. + * + * It is currently possible to optionally associate an artifact attribute type + * with a keyword. This feature was added to support an initial implementation + * of account number search and may be removed in the future. */ class Keyword { - private String keywordString; // keyword to search for - private boolean isLiteral; // false if reg exp - private boolean isWholeword; // false if match a substring - private BlackboardAttribute.ATTRIBUTE_TYPE keywordType = null; + private String searchTerm; + private boolean isLiteral; + private boolean isWholeWord; + private BlackboardAttribute.ATTRIBUTE_TYPE artifactAtrributeType; /** + * Constructs a representation of a keyword for which to search. The search + * term for the keyword may be either a literal term that will be treated as + * a whole word, or a regex. * - * @param query Keyword to search for - * @param isLiteral false if reg exp + * @param searchTerm The search term for the keyword. + * @param isLiteral Whether or not the search term is a literal term that + * will be treated as a whole word, instead of a regex. */ - Keyword(String query, boolean isLiteral) { - this.keywordString = query; + Keyword(String searchTerm, boolean isLiteral) { + this.searchTerm = searchTerm; this.isLiteral = isLiteral; - this.isWholeword = true; + this.isWholeWord = true; } /** + * Constructs a representation of a keyword for which to search. The search + * term may be either a literal term, to be treated as either a whole word + * or as a substring, or a regex. * - * @param query Keyword to search for - * @param isLiteral false if reg exp - * @param isWholeword false to match substring (undefined behavior if regexp - * is true) + * @param searchTerm The search term. + * @param isLiteral Whether or not the search term is a literal term, + * instead of a regex. + * @param isWholeWord Whether or not the search term, if it is a literal + * search term, should be treated as a whole word rather + * than a substring. */ - Keyword(String query, boolean isLiteral, boolean isWholeword) { - this.keywordString = query; + Keyword(String searchTerm, boolean isLiteral, boolean isWholeWord) { + this.searchTerm = searchTerm; this.isLiteral = isLiteral; - this.isWholeword = isWholeword; + this.isWholeWord = isWholeWord; } /** + * Constructs a representation of a keyword for which to search, for the + * purpose of finding a specific artifact attribute. The search term may be + * either a literal term, to be treated as a whole word, or a regex. * - * @param query Keyword to search for - * @param isLiteral false if reg exp - * @param keywordType + * The association of an artifact attribute type with a keyword was added to + * support an initial implementation of account number search and may be + * removed in the future. + * + * @param searchTerm The search term. + * @param isLiteral Whether or not the search term is a literal term, to + * be treated as a whole word, instead of a regex. + * @param keywordType The artifact attribute type. */ - Keyword(String query, boolean isLiteral, BlackboardAttribute.ATTRIBUTE_TYPE keywordType) { - this(query, isLiteral); - this.keywordType = keywordType; - } - - void setType(BlackboardAttribute.ATTRIBUTE_TYPE keywordType) { - this.keywordType = keywordType; - } - - BlackboardAttribute.ATTRIBUTE_TYPE getType() { - return this.keywordType; + Keyword(String searchTerm, boolean isLiteral, BlackboardAttribute.ATTRIBUTE_TYPE artifactAtrributeType) { + this(searchTerm, isLiteral); + this.artifactAtrributeType = artifactAtrributeType; } /** + * Gets the search term for the keyword, which may be either a literal term + * or a regex. * - * @return Keyword to search for + * @return The search term. */ - String getQuery() { - return keywordString; + String getSearchTerm() { + return searchTerm; } - boolean isLiteral() { + /** + * Indicates whether the search term for the keyword is a literal term or a + * regex. + * + * @return True or false. + */ + boolean searchTermIsLiteral() { return isLiteral; } - boolean isWholeword() { - return isWholeword; + /** + * Indicates whether or not the search term for the keyword, if it is a + * literal term and not a regex, will be treated as a whole word or as a + * substring. + * + * @return True or false. + */ + boolean searchTermIsWholeWord() { + return isWholeWord; + } + + /** + * Sets the artifact attribute type associated with the keyword, if any. + * + * The association of an artifact attribute type with the keyword was added + * to support an initial implementation of account number search and may be + * removed in the future. + * + * @param artifactAtrributeType + */ + void setArtifactAttributeType(BlackboardAttribute.ATTRIBUTE_TYPE artifactAtrributeType) { + this.artifactAtrributeType = artifactAtrributeType; + } + + /** + * Gets the artifact attribute type associated with the keyword, if any. + * + * The association of an artifact attribute type with the keyword was added + * to support an initial implementation of account number search and may be + * removed in the future. + * + * @return A attribute type object or null. + */ + BlackboardAttribute.ATTRIBUTE_TYPE getArtifactAttributeType() { + return this.artifactAtrributeType; } @Override public String toString() { - return NbBundle.getMessage(this.getClass(), "Keyword.toString.text", keywordString, isLiteral, keywordType); + return String.format("Keyword{searchTerm='%s', isLiteral=%s, isWholeWord=%s}", searchTerm, isLiteral, isWholeWord); } @Override @@ -103,21 +158,19 @@ class Keyword { if (getClass() != obj.getClass()) { return false; } - final Keyword other = (Keyword) obj; - if ((this.keywordString == null) ? (other.keywordString != null) : !this.keywordString.equals(other.keywordString)) { - return false; - } - if (this.isLiteral != other.isLiteral) { - return false; - } - return true; + Keyword other = (Keyword) obj; + return (this.searchTerm.equals(other.searchTerm) + && this.isLiteral == other.isLiteral + && this.isWholeWord == other.isWholeWord); } @Override public int hashCode() { int hash = 7; - hash = 17 * hash + (this.keywordString != null ? this.keywordString.hashCode() : 0); + hash = 17 * hash + this.searchTerm.hashCode(); hash = 17 * hash + (this.isLiteral ? 1 : 0); + hash = 17 * hash + (this.isWholeWord ? 1 : 0); return hash; } + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordList.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordList.java index 2dd2933fa9..13f094a89d 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordList.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordList.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2014 Basis Technology Corp. + * + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,32 +21,75 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.Date; import java.util.List; +/** + * A list of keywords for which to search. Includes list creation and + * modification metadata and settings that determine how the list is to be used + * when ingesting a data source. Standard lists provided by Autopsy may be + * marked as not editable. + */ public class KeywordList { private String name; private Date created; private Date modified; private Boolean useForIngest; - private Boolean ingestMessages; + private Boolean postIngestMessages; private List keywords; - private Boolean locked; + private Boolean isEditable; - KeywordList(String name, Date created, Date modified, Boolean useForIngest, Boolean ingestMessages, List keywords, boolean locked) { + /** + * Constructs a list of keywords for which to search. Includes list creation + * and modification metadata and settings that determine how the list is to + * be used when ingesting a data source. Standard lists provided by Autopsy + * may be marked as not editable. + * + * @param name The name to asociate with the list. + * @param created When the list was created. + * @param modified When the list was last modified. + * @param useForIngest Whether or not the list is to be used when + * ingesting a data source. + * @param postIngestMessages Whether or not to post ingest inbox messages + * when a keyword within the list is found while + * ingesting a data source. + * @param keywords The keywords that make up the list. + * @param isEditable Whether or not the list may be edited by a + * user; standard lists provided by Autopsy should + * not be edited. + */ + KeywordList(String name, Date created, Date modified, Boolean useForIngest, Boolean postIngestMessages, List keywords, boolean isEditable) { this.name = name; this.created = created; this.modified = modified; this.useForIngest = useForIngest; - this.ingestMessages = ingestMessages; + this.postIngestMessages = postIngestMessages; this.keywords = keywords; - this.locked = locked; + this.isEditable = isEditable; } + /** + * Constructs a list of keywords for which to search. Includes list creation + * and modification metadata and settings that determine how the list is to + * be used when ingesting a data source. The list will be marked as a + * standard lists provided by Autopsy that should not be treated as + * editable. + * + * @param name The name to asociate with the list. + * @param created When the list was created. + * @param modified When the list was last modified. + * @param useForIngest Whether or not the list is to be used when + * ingesting a data source. + * @param postIngestMessages Whether or not to post ingest inbox messages + * when a keyword within the list is found while + * ingesting a data source. + * @param keywords The keywords that make up the list. + */ KeywordList(String name, Date created, Date modified, Boolean useForIngest, Boolean ingestMessages, List keywords) { this(name, created, modified, useForIngest, ingestMessages, keywords, false); } /** - * Create an unnamed list. Usually used for ad-hoc searches + * Constructs a temporary list of keywords to be used for ad hoc keyword + * search and then discarded. * * @param keywords */ @@ -54,74 +97,116 @@ public class KeywordList { this("", new Date(0), new Date(0), false, false, keywords, false); } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final KeywordList other = (KeywordList) obj; - if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 5; - return hash; - } - + /** + * Get the name assigned to the keyword list. + * + * @return The list name. + */ String getName() { return name; } + /** + * Gets the date the keyword list was created. + * + * @return The date. + */ Date getDateCreated() { return created; } + /** + * Gets the date the keyword list was last modified. + * + * @return The date. + */ Date getDateModified() { return modified; } + /** + * Gets whether or not the list should be used when ingesting a data source. + * + * @return True or false. + */ Boolean getUseForIngest() { return useForIngest; } - void setUseForIngest(boolean use) { - this.useForIngest = use; + /** + * Sets whether or not the list should be used when ingesting a data source. + * + * @param useForIngest True or false. + */ + void setUseForIngest(boolean useForIngest) { + this.useForIngest = useForIngest; } + /** + * Gets whether or not to post ingest inbox messages when a keyword within + * the list is found while ingesting a data source. + * + * @return true or false + */ Boolean getIngestMessages() { - return ingestMessages; + return postIngestMessages; } - void setIngestMessages(boolean ingestMessages) { - this.ingestMessages = ingestMessages; + /** + * Sets whether or not to post ingest inbox messages when a keyword within + * the list is found while ingesting a data source. + * + * @param postIngestMessages True or false. + */ + void setIngestMessages(boolean postIngestMessages) { + this.postIngestMessages = postIngestMessages; } + /** + * Gets the keywords included in the list + * + * @return A colleciton of Keyword objects. + */ List getKeywords() { return keywords; } + /** + * Indicates whether or not a given keyword is included in the list. + * + * @param keyword The keyword of interest. + * + * @return + */ boolean hasKeyword(Keyword keyword) { return keywords.contains(keyword); } - boolean hasKeyword(String keyword) { - //note, this ignores isLiteral - for (Keyword k : keywords) { - if (k.getQuery().equals(keyword)) { + /** + * Indicates whether or not a given search term is included in the list. + * + * @param searchTerm The search term. + * + * @return True or false. + */ + boolean hasSearchTerm(String searchTerm) { + for (Keyword word : keywords) { + if (word.getSearchTerm().equals(searchTerm)) { return true; } } return false; } - Boolean isLocked() { - return locked; + /** + * Indicates Whether or not the list should be editable by a user; standard + * lists provided by Autopsy should be marked as not editable when they are + * contructed. + * + * @return True or false. + */ + Boolean isEditable() { + return isEditable; } + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchList.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchList.java index 028908dc34..79e09cd788 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchList.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchList.java @@ -124,7 +124,7 @@ abstract class KeywordSearchList { List ccns = new ArrayList<>(); ccns.add(new Keyword(CCN_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); lockedLists.add("Credit Card Numbers"); - addList("Credit Card Numbers", ccns, true, false, true); + addList("Credit Card Numbers", ccns, false, false, true); } /** @@ -141,7 +141,7 @@ abstract class KeywordSearchList { //we want to preserve state of locked lists List toClear = new ArrayList<>(); for (String list : theLists.keySet()) { - if (theLists.get(list).isLocked() == false) { + if (theLists.get(list).isEditable() == false) { toClear.add(list); } } @@ -173,7 +173,7 @@ abstract class KeywordSearchList { public List getListsL(boolean locked) { List ret = new ArrayList<>(); for (KeywordList list : theLists.values()) { - if (list.isLocked().equals(locked)) { + if (list.isEditable().equals(locked)) { ret.add(list); } } @@ -200,7 +200,7 @@ abstract class KeywordSearchList { ArrayList lists = new ArrayList<>(); for (String listName : theLists.keySet()) { KeywordList list = theLists.get(listName); - if (locked == list.isLocked()) { + if (locked == list.isEditable()) { lists.add(listName); } } @@ -218,7 +218,7 @@ abstract class KeywordSearchList { public KeywordList getListWithKeyword(String keyword) { KeywordList found = null; for (KeywordList list : theLists.values()) { - if (list.hasKeyword(keyword)) { + if (list.hasSearchTerm(keyword)) { found = list; break; } @@ -246,7 +246,7 @@ abstract class KeywordSearchList { int numLists = 0; for (String listName : theLists.keySet()) { KeywordList list = theLists.get(listName); - if (locked == list.isLocked()) { + if (locked == list.isEditable()) { ++numLists; } } @@ -330,7 +330,7 @@ abstract class KeywordSearchList { } boolean addList(KeywordList list) { - return addList(list.getName(), list.getKeywords(), list.getUseForIngest(), list.getIngestMessages(), list.isLocked()); + return addList(list.getName(), list.getKeywords(), list.getUseForIngest(), list.getIngestMessages(), list.isEditable()); } /** @@ -437,7 +437,7 @@ abstract class KeywordSearchList { */ boolean deleteList(String name) { KeywordList delList = getList(name); - if (delList != null && !delList.isLocked()) { + if (delList != null && !delList.isEditable()) { theLists.remove(name); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java index 0750fb86c8..17c760323a 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchModuleFactory.java @@ -39,7 +39,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; @ServiceProvider(service = IngestModuleFactory.class) public class KeywordSearchModuleFactory extends IngestModuleFactoryAdapter { - private static final HashSet defaultDisabledKeywordListNames = new HashSet<>(Arrays.asList("Phone Numbers", "IP Addresses", "URLs")); //NON-NLS + private static final HashSet defaultDisabledKeywordListNames = new HashSet<>(Arrays.asList("Phone Numbers", "IP Addresses", "URLs", "Credit Card Numbers")); //NON-NLS private KeywordSearchJobSettingsPanel jobSettingsPanel = null; @Override diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index 2073a25df3..f373c2d162 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +18,8 @@ */ package org.sleuthkit.autopsy.keywordsearch; -import org.sleuthkit.datamodel.AbstractFile; - /** - * Interface for a search query. Implemented by various engines or methods of - * using the same engine. One of these is created for each query. + * Interface for kewyord search queries. */ interface KeywordSearchQuery { @@ -33,7 +30,7 @@ interface KeywordSearchQuery { * * @return true if the query passed validation */ - public boolean validate(); + boolean validate(); /** * execute query and return results without publishing them return results @@ -43,7 +40,7 @@ interface KeywordSearchQuery { * could be a notification to stop processing * @return */ - public QueryResults performQuery() throws NoOpenCoreException; + QueryResults performQuery() throws NoOpenCoreException; /** * Set an optional filter to narrow down the search Adding multiple filters @@ -51,14 +48,14 @@ interface KeywordSearchQuery { * * @param filter filter to set on the query */ - public void addFilter(KeywordQueryFilter filter); + void addFilter(KeywordQueryFilter filter); /** * Set an optional SOLR field to narrow down the search * * @param field field to set on the query */ - public void setField(String field); + void setField(String field); /** * Modify the query string to be searched as a substring instead of a whole @@ -66,39 +63,39 @@ interface KeywordSearchQuery { * * @param isSubstring */ - public void setSubstringQuery(); + void setSubstringQuery(); /** * escape the query string and use the escaped string in the query */ - public void escape(); + void escape(); /** * * @return true if query was escaped */ - public boolean isEscaped(); + boolean isEscaped(); /** * * @return true if query is a literal query (non regex) */ - public boolean isLiteral(); + boolean isLiteral(); /** * return original keyword/query string * * @return the query String supplied originally */ - public String getQueryString(); + String getQueryString(); /** * return escaped keyword/query string if escaping was done * * @return the escaped query string, or original string if no escaping done */ - public String getEscapedQueryString(); + String getEscapedQueryString(); - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName); + KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java index 8d73afad65..a6713d3145 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryDelegator.java @@ -29,6 +29,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; /** @@ -55,20 +56,20 @@ class KeywordSearchQueryDelegator { for (KeywordList keywordList : keywordLists) { for (Keyword keyword : keywordList.getKeywords()) { KeywordSearchQuery query; - if (keyword.isLiteral()) { + if (keyword.searchTermIsLiteral()) { // literal, exact match - if (keyword.isWholeword()) { + if (keyword.searchTermIsWholeWord()) { query = new LuceneQuery(keywordList, keyword); query.escape(); } // literal, substring match else { - query = new TermComponentQuery(keywordList, keyword); + query = new TermsComponentQuery(keywordList, keyword); query.escape(); query.setSubstringQuery(); } } // regexp else { - query = new TermComponentQuery(keywordList, keyword); + query = new TermsComponentQuery(keywordList, keyword); } queryDelegates.add(query); } @@ -108,7 +109,8 @@ class KeywordSearchQueryDelegator { final String pathText = NbBundle.getMessage(this.getClass(), "KeywordSearchQueryManager.pathText.text"); - DataResultTopComponent.initInstance(pathText, rootNode, queryRequests.size(), searchResultWin); + DataResultTopComponent.initInstance(pathText, new TableFilterNode(rootNode, true, KeywordSearch.class.getName()), + queryRequests.size(), searchResultWin); searchResultWin.requestActive(); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index 835fe539ee..97beb9e9c6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -255,8 +255,8 @@ class KeywordSearchResultFactory extends ChildFactory { //the query is executed later on demand if (queryResults.getKeywords().size() == 1) { //simple case, no need to process subqueries and do special escaping - Keyword term = queryResults.getKeywords().iterator().next(); - return constructEscapedSolrQuery(term.getQuery(), literal_query); + Keyword keyword = queryResults.getKeywords().iterator().next(); + return constructEscapedSolrQuery(keyword.getSearchTerm(), literal_query); } else { //find terms for this content hit List hitTerms = new ArrayList<>(); @@ -274,7 +274,7 @@ class KeywordSearchResultFactory extends ChildFactory { int curTerm = 0; for (Keyword term : hitTerms) { //escape subqueries, MAKE SURE they are not escaped again later - highlightQuery.append(constructEscapedSolrQuery(term.getQuery(), literal_query)); + highlightQuery.append(constructEscapedSolrQuery(term.getSearchTerm(), literal_query)); if (lastTerm != curTerm) { highlightQuery.append(" "); //acts as OR || } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 0242d57027..5702a952f4 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -55,7 +55,7 @@ class LuceneQuery implements KeywordSearchQuery { private final String keywordString; //original unescaped query private String keywordStringEscaped; private boolean isEscaped; - private Keyword keywordQuery = null; + private Keyword keyword = null; private KeywordList keywordList = null; private final List filters = new ArrayList<>(); private String field = null; @@ -72,15 +72,15 @@ class LuceneQuery implements KeywordSearchQuery { /** * Constructor with query to process. * - * @param keywordQuery + * @param keyword */ - public LuceneQuery(KeywordList keywordList, Keyword keywordQuery) { + public LuceneQuery(KeywordList keywordList, Keyword keyword) { this.keywordList = keywordList; - this.keywordQuery = keywordQuery; + this.keyword = keyword; // @@@ BC: Long-term, we should try to get rid of this string and use only the // keyword object. Refactoring did not make its way through this yet. - this.keywordString = keywordQuery.getQuery(); + this.keywordString = keyword.getSearchTerm(); this.keywordStringEscaped = this.keywordString; } @@ -168,8 +168,8 @@ class LuceneQuery implements KeywordSearchQuery { //bogus - workaround the dir tree table issue //attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(), MODULE_NAME, "", "")); //selector - if (keywordQuery != null) { - BlackboardAttribute.ATTRIBUTE_TYPE selType = keywordQuery.getType(); + if (keyword != null) { + BlackboardAttribute.ATTRIBUTE_TYPE selType = keyword.getArtifactAttributeType(); if (selType != null) { attributes.add(new BlackboardAttribute(selType, MODULE_NAME, termHit)); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 8c48b51f41..316e4f3717 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -115,7 +115,7 @@ class QueryResults { for (final Keyword keyword : getKeywords()) { if (worker.isCancelled()) { - logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getQuery()); //NON-NLS + logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm()); //NON-NLS break; } @@ -124,7 +124,7 @@ class QueryResults { progress.progress(keyword.toString(), unitProgress); } if (subProgress != null) { - String hitDisplayStr = keyword.getQuery(); + String hitDisplayStr = keyword.getSearchTerm(); if (hitDisplayStr.length() > 50) { hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; } @@ -132,7 +132,7 @@ class QueryResults { } for (KeywordHit hit : getOneHitPerObject(keyword)) { - String termString = keyword.getQuery(); + String termString = keyword.getSearchTerm(); final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(termString); String snippet; try { @@ -250,11 +250,12 @@ class QueryResults { //list attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl")); - detailsSb.append("").append(attr.getValueString()).append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - + if (attr != null) { + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl")); + detailsSb.append("").append(attr.getValueString()).append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + } //regex if (!keywordSearchQuery.isLiteral()) { attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 3c234c4fa3..00fd1db2a7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -403,7 +403,7 @@ public final class SearchRunner { ProgressContributor[] subProgresses = new ProgressContributor[keywords.size()]; int i = 0; for (Keyword keywordQuery : keywords) { - subProgresses[i] = AggregateProgressFactory.createProgressContributor(keywordQuery.getQuery()); + subProgresses[i] = AggregateProgressFactory.createProgressContributor(keywordQuery.getSearchTerm()); progressGroup.addContributor(subProgresses[i]); i++; } @@ -419,11 +419,11 @@ public final class SearchRunner { for (Keyword keywordQuery : keywords) { if (this.isCancelled()) { - logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keywordQuery.getQuery()); //NON-NLS + logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keywordQuery.getSearchTerm()); //NON-NLS return null; } - final String queryStr = keywordQuery.getQuery(); + final String queryStr = keywordQuery.getSearchTerm(); final KeywordList list = keywordToList.get(queryStr); //new subProgress will be active after the initial query @@ -434,9 +434,9 @@ public final class SearchRunner { KeywordSearchQuery keywordSearchQuery = null; - boolean isRegex = !keywordQuery.isLiteral(); + boolean isRegex = !keywordQuery.searchTermIsLiteral(); if (isRegex) { - keywordSearchQuery = new TermComponentQuery(list, keywordQuery); + keywordSearchQuery = new TermsComponentQuery(list, keywordQuery); } else { keywordSearchQuery = new LuceneQuery(list, keywordQuery); keywordSearchQuery.escape(); @@ -454,16 +454,16 @@ public final class SearchRunner { try { queryResults = keywordSearchQuery.performQuery(); } catch (NoOpenCoreException ex) { - logger.log(Level.WARNING, "Error performing query: " + keywordQuery.getQuery(), ex); //NON-NLS + logger.log(Level.WARNING, "Error performing query: " + keywordQuery.getSearchTerm(), ex); //NON-NLS //no reason to continue with next query if recovery failed //or wait for recovery to kick in and run again later //likely case has closed and threads are being interrupted return null; } catch (CancellationException e) { - logger.log(Level.INFO, "Cancel detected, bailing during keyword query: {0}", keywordQuery.getQuery()); //NON-NLS + logger.log(Level.INFO, "Cancel detected, bailing during keyword query: {0}", keywordQuery.getSearchTerm()); //NON-NLS return null; } catch (Exception e) { - logger.log(Level.WARNING, "Error performing query: " + keywordQuery.getQuery(), e); //NON-NLS + logger.log(Level.WARNING, "Error performing query: " + keywordQuery.getSearchTerm(), e); //NON-NLS continue; } @@ -481,7 +481,7 @@ public final class SearchRunner { int totalUnits = newResults.getKeywords().size(); subProgresses[keywordsSearched].start(totalUnits); int unitProgress = 0; - String queryDisplayStr = keywordQuery.getQuery(); + String queryDisplayStr = keywordQuery.getSearchTerm(); if (queryDisplayStr.length() > 50) { queryDisplayStr = queryDisplayStr.substring(0, 49) + "..."; } @@ -547,7 +547,7 @@ public final class SearchRunner { keywordLists.add(list); for (Keyword k : list.getKeywords()) { keywords.add(k); - keywordToList.put(k.getQuery(), list); + keywordToList.put(k.getSearchTerm(), list); } } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java deleted file mode 100644 index 761eb3c208..0000000000 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2016 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// -package org.sleuthkit.autopsy.keywordsearch; - -import com.google.common.base.CharMatcher; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit; -import org.apache.solr.client.solrj.SolrQuery; -import org.apache.solr.client.solrj.response.TermsResponse.Term; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.Version; -import org.sleuthkit.autopsy.datamodel.CreditCards; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Account; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; - -/** - * Performs a regular expression query to the SOLR/Lucene instance. - */ -final class TermComponentQuery implements KeywordSearchQuery { - - private static final Logger LOGGER = Logger.getLogger(TermComponentQuery.class.getName()); - private static final boolean DEBUG = Version.Type.DEVELOPMENT.equals(Version.getBuildType()); - - private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); - private static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID); - - //TODO: move these regex and the luhn check to a new class, something like: CreditCardNumberValidator - /* - * Track 2 is numeric plus six punctuation symbolls :;<=>? - * - * This regex matches 12-19 digit ccns embeded in a track 2 formated string. - * This regex matches (and extracts groups) even if the entire track is not - * present as long as the part that is conforms to the track format. - * - */ - private static final Pattern TRACK2_PATTERN = Pattern.compile( - "[:;<=>?]?" //(optional)start sentinel //NON-NLS - + "(?[3456]([ -]?\\d){11,18})" //12-19 digits, with possible single spaces or dashes in between. first digit is 3,4,5, or 6 //NON-NLS - + "(?:[:;<=>?]" //separator //NON-NLS - + "(?:(?\\d{4})" //4 digit expiration date YYMM //NON-NLS - + "(?:(?\\d{3})" //3 digit service code //NON-NLS - + "(?:(?[^:;<=>?]*)" //discretionary data, not containing punctuation marks //NON-NLS - + "(?:[:;<=>?]" //end sentinel //NON-NLS - + "(?.)" //longitudinal redundancy check //NON-NLS - + "?)?)?)?)?)?"); //close nested optional groups //NON-NLS - - /* - * Track 1 is alphanumeric. - * - * This regex matches 12-19 digit ccns embeded in a track 1 formated string. - * This regex matches (and extracts groups) even if the entire track is not - * present as long as the part that is conforms to the track format. - */ - private static final Pattern TRACK1_PATTERN = Pattern.compile( - "(?:" //begin nested optinal group //NON-NLS - + "%?" //optional start sentinal: % //NON-NLS - + "B)?" //format code //NON-NLS - + "(?[3456]([ -]?\\d){11,18})" //12-19 digits, with possible single spaces or dashes in between. first digit is 3,4,5, or 6 //NON-NLS - + "\\^" //separator //NON-NLS - + "(?[^^]{2,26})" //2-26 charachter name, not containing ^ //NON-NLS - + "(?:\\^" //separator //NON-NLS - + "(?:(?:\\^|(?\\d{4}))" //separator or 4 digit expiration YYMM //NON-NLS - + "(?:(?:\\^|(?\\d{3}))"//separator or 3 digit service code //NON-NLS - + "(?:(?[^?]*)" // discretionary data not containing separator //NON-NLS - + "(?:\\?" // end sentinal: ? //NON-NLS - + "(?.)" //longitudinal redundancy check //NON-NLS - + "?)?)?)?)?)?");//close nested optional groups //NON-NLS - private static final Pattern CCN_PATTERN = Pattern.compile("(?[3456]([ -]?\\d){11,18})"); //12-19 digits, with possible single spaces or dashes in between. first digit is 3,4,5, or 6 //NON-NLS - private static final LuhnCheckDigit LUHN_CHECK = new LuhnCheckDigit(); - - //corresponds to field in Solr schema, analyzed with white-space tokenizer only - private static final String TERMS_SEARCH_FIELD = Server.Schema.CONTENT_WS.toString(); - private static final String TERMS_HANDLER = "/terms"; //NON-NLS - private static final int TERMS_TIMEOUT = 90 * 1000; //in ms - private static final String CASE_INSENSITIVE = "case_insensitive"; //NON-NLS - private static final int MAX_TERMS_RESULTS = 20000; - - private String escapedQuery; - private final KeywordList keywordList; - private final Keyword keyword; - private boolean isEscaped; - private final List filters = new ArrayList<>(); - - TermComponentQuery(KeywordList keywordList, Keyword keyword) { - this.keyword = keyword; - - this.keywordList = keywordList; - this.escapedQuery = keyword.getQuery(); - } - - @Override - public void addFilter(KeywordQueryFilter filter) { - this.filters.add(filter); - } - - /** - * @param field - * - * @deprecated This method is unused and no-op - */ - @Override - @Deprecated - public void setField(String field) { - } - - @Override - public void setSubstringQuery() { - escapedQuery = ".*" + escapedQuery + ".*"; - } - - @Override - public void escape() { - escapedQuery = Pattern.quote(keyword.getQuery()); - isEscaped = true; - } - - @Override - public boolean validate() { - if (escapedQuery.isEmpty()) { - return false; - } - - try { - Pattern.compile(escapedQuery); - return true; - } catch (IllegalArgumentException ex) { - return false; - } - } - - @Override - public boolean isEscaped() { - return isEscaped; - } - - @Override - public boolean isLiteral() { - return false; - } - - @Override - public String getEscapedQueryString() { - return this.escapedQuery; - } - - @Override - public String getQueryString() { - return keyword.getQuery(); - } - - @Override - public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String termHit, KeywordHit hit, String snippet, String listName) { - BlackboardArtifact newArtifact; - - Collection attributes = new ArrayList<>(); - if (keyword.getType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, MODULE_NAME, Account.Type.CREDIT_CARD.name())); - - Map parsedTrackAttributeMap = new HashMap<>(); - - //try to match it against the track 1 regex - Matcher matcher = TRACK1_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack1Data(parsedTrackAttributeMap, matcher); - } - - //then try to match it against the track 2 regex - matcher = TRACK2_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack2Data(parsedTrackAttributeMap, matcher); - } - - //if we couldn't parse the CCN abort this artifact - final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); - if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { - LOGGER.log(Level.SEVERE, "Failed to parse CCN from hit: " + hit.getSnippet()); - return null; - } - - attributes.addAll(parsedTrackAttributeMap.values()); - - //look up the bank name, schem, etc from the BIN - final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); - CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); - if (binInfo != null) { - binInfo.getScheme().ifPresent(scheme - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); - binInfo.getCardType().ifPresent(cardType - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); - binInfo.getBrand().ifPresent(brand - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); - binInfo.getBankName().ifPresent(bankName - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); - binInfo.getBankPhoneNumber().ifPresent(phoneNumber - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); - binInfo.getBankURL().ifPresent(url - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); - binInfo.getCountry().ifPresent(country - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); - binInfo.getBankCity().ifPresent(city - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); - } - - /* if the hit is from unused or unalocated blocks, record the - * KEYWORD_SEARCH_DOCUMENT_ID, so we can show just that chunk in the - * UI - */ - if (hit.getContent() instanceof AbstractFile) { - AbstractFile file = (AbstractFile) hit.getContent(); - if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS - || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { - attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); - } - } - - // make account artifact - try { - newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT); - } catch (TskCoreException tskCoreException) { - LOGGER.log(Level.SEVERE, "Error adding bb artifact for account", tskCoreException); //NON-NLS - return null; - } - } else { - - //regex match - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, termHit)); - //regex keyword - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, keyword.getQuery())); - - if (StringUtils.isNotEmpty(listName)) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); - } - - //make keyword hit artifact - try { - newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); - - } catch (TskCoreException tskCoreException) { - LOGGER.log(Level.SEVERE, "Error adding bb artifact for keyword hit", tskCoreException); //NON-NLS - return null; - } - } - //preview - if (snippet != null) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); - } - - if (hit.isArtifactHit()) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID())); - } - - try { - //TODO: do we still/really need this KeywordCachedArtifact class? - newArtifact.addAttributes(attributes); - KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); - writeResult.add(attributes); - return writeResult; - } catch (TskCoreException e) { - LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS - return null; - } - } - - @Override - public QueryResults performQuery() throws NoOpenCoreException { - /* - * Execute the regex query to get a list of terms that match the regex. - * Note that the field that is being searched is tokenized based on - * whitespace. - */ - //create the query - final SolrQuery q = new SolrQuery(); - q.setRequestHandler(TERMS_HANDLER); - q.setTerms(true); - q.setTermsRegexFlag(CASE_INSENSITIVE); - q.setTermsRegex(escapedQuery); - q.addTermsField(TERMS_SEARCH_FIELD); - q.setTimeAllowed(TERMS_TIMEOUT); - q.setShowDebugInfo(DEBUG); - q.setTermsLimit(MAX_TERMS_RESULTS); - LOGGER.log(Level.INFO, "Query: {0}", q.toString()); //NON-NLS - - //execute the query - List terms = null; - try { - terms = KeywordSearch.getServer().queryTerms(q).getTerms(TERMS_SEARCH_FIELD); - } catch (KeywordSearchModuleException ex) { - LOGGER.log(Level.SEVERE, "Error executing the regex terms query: " + keyword.getQuery(), ex); //NON-NLS - //TODO: this is almost certainly wrong and guaranteed to throw a NPE at some point!!!! - } - - /* - * For each term that matched the regex, query for full set of document - * hits for that term. - */ - QueryResults results = new QueryResults(this, keywordList); - int resultSize = 0; - - for (Term term : terms) { - final String termStr = KeywordSearchUtil.escapeLuceneQuery(term.getTerm()); - - if (keyword.getType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { - //If the keyword is a credit card number, pass it through luhn validator - Matcher matcher = CCN_PATTERN.matcher(term.getTerm()); - matcher.find(); - final String ccn = CharMatcher.anyOf(" -").removeFrom(matcher.group("ccn")); - if (false == LUHN_CHECK.isValid(ccn)) { - continue; //if the hit does not pass the luhn check, skip it. - } - } - - /* - * Note: we can't set filter query on terms query but setting filter - * query on fileResults query will yield the same result - */ - LuceneQuery filesQuery = new LuceneQuery(keywordList, new Keyword(termStr, true)); - filters.forEach(filesQuery::addFilter); - - try { - QueryResults fileQueryResults = filesQuery.performQuery(); - Set filesResults = new HashSet<>(); - for (Keyword key : fileQueryResults.getKeywords()) { //flatten results into a single list - List keyRes = fileQueryResults.getResults(key); - resultSize += keyRes.size(); - filesResults.addAll(keyRes); - } - results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(filesResults)); - } catch (NoOpenCoreException | RuntimeException e) { - LOGGER.log(Level.WARNING, "Error executing Solr query,", e); //NON-NLS - throw e; - } - } - - //TODO limit how many results we store, not to hit memory limits - LOGGER.log(Level.INFO, "Regex # results: {0}", resultSize); //NON-NLS - - return results; - } - - @Override - public KeywordList getKeywordList() { - return keywordList; - } - - /** - * Add an attribute of the the given type to the given artifact with the - * value taken from the matcher. If an attribute of the given type already - * exists on the artifact or if the value is null, no attribute is added. - * - * @param attributeMap - * @param attrType - * @param groupName - * @param matcher * - */ - static private void addAttributeIfNotAlreadyCaptured(Map attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) { - BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType); - - attributeMap.computeIfAbsent(type, (BlackboardAttribute.Type t) -> { - String value = matcher.group(groupName); - if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) { - value = CharMatcher.anyOf(" -").removeFrom(value); - } - if (StringUtils.isNotBlank(value)) { - return new BlackboardAttribute(attrType, MODULE_NAME, value); - } - return null; - }); - } - - /** - * Parse the track 2 data from a KeywordHit and add it to the given - * artifact. - * - * @param attributeMAp - * @param matcher - */ - static private void parseTrack2Data(Map attributeMAp, Matcher matcher) { - //try to add all the attrributes common to track 1 and 2 - addAttributeIfNotAlreadyCaptured(attributeMAp, ATTRIBUTE_TYPE.TSK_CARD_NUMBER, "accountNumber", matcher); - addAttributeIfNotAlreadyCaptured(attributeMAp, ATTRIBUTE_TYPE.TSK_CARD_EXPIRATION, "expiration", matcher); - addAttributeIfNotAlreadyCaptured(attributeMAp, ATTRIBUTE_TYPE.TSK_CARD_SERVICE_CODE, "serviceCode", matcher); - addAttributeIfNotAlreadyCaptured(attributeMAp, ATTRIBUTE_TYPE.TSK_CARD_DISCRETIONARY, "discretionary", matcher); - addAttributeIfNotAlreadyCaptured(attributeMAp, ATTRIBUTE_TYPE.TSK_CARD_LRC, "LRC", matcher); - - } - - /** - * Parse the track 1 data from a KeywordHit and add it to the given - * artifact. - * - * @param attributeMap - * @param matcher - */ - static private void parseTrack1Data(Map attributeMap, Matcher matcher) { - // track 1 has all the fields present in track 2 - parseTrack2Data(attributeMap, matcher); - //plus it also has the account holders name - addAttributeIfNotAlreadyCaptured(attributeMap, ATTRIBUTE_TYPE.TSK_NAME_PERSON, "name", matcher); - } -} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java new file mode 100644 index 0000000000..49343798b7 --- /dev/null +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -0,0 +1,517 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.keywordsearch; + +import com.google.common.base.CharMatcher; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.response.TermsResponse.Term; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.Version; +import org.sleuthkit.autopsy.datamodel.CreditCards; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Implements a regex query that will be performed as a two step operation. In + * the first step, the Solr terms component is used to find any terms in the + * index that match the regex. In the second step, term queries are executed for + * each matched term to produce the set of keyword hits for the regex. + */ +final class TermsComponentQuery implements KeywordSearchQuery { + + private static final Logger LOGGER = Logger.getLogger(TermsComponentQuery.class.getName()); + private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); + private static final String SEARCH_HANDLER = "/terms"; //NON-NLS + private static final String SEARCH_FIELD = Server.Schema.CONTENT_WS.toString(); + private static final int TERMS_SEARCH_TIMEOUT = 90 * 1000; // Milliseconds + private static final String CASE_INSENSITIVE = "case_insensitive"; //NON-NLS + private static final boolean DEBUG_FLAG = Version.Type.DEVELOPMENT.equals(Version.getBuildType()); + private static final int MAX_TERMS_QUERY_RESULTS = 20000; + private final KeywordList keywordList; + private final Keyword keyword; + private String searchTerm; + private boolean searchTermIsEscaped; + private final List filters = new ArrayList<>(); // THIS APPEARS TO BE UNUSED + + /* + * The following fields are part of the initial implementation of credit + * card account search and should be factored into another class when time + * permits. + */ + private static final Pattern CREDIT_CARD_NUM_PATTERN = Pattern.compile("(?[3456]([ -]?\\d){11,18})"); //12-19 digits, with possible single spaces or dashes in between. First digit is 3,4,5, or 6 //NON-NLS + private static final LuhnCheckDigit CREDIT_CARD_NUM_LUHN_CHECK = new LuhnCheckDigit(); + private static final Pattern CREDIT_CARD_TRACK1_PATTERN = Pattern.compile( + /* + * Track 1 is alphanumeric. + * + * This regex matches 12-19 digit ccns embeded in a track 1 formated + * string. This regex matches (and extracts groups) even if the + * entire track is not present as long as the part that is conforms + * to the track format. + */ + "(?:" //begin nested optinal group //NON-NLS + + "%?" //optional start sentinal: % //NON-NLS + + "B)?" //format code //NON-NLS + + "(?[3456]([ -]?\\d){11,18})" //12-19 digits, with possible single spaces or dashes in between. first digit is 3,4,5, or 6 //NON-NLS + + "\\^" //separator //NON-NLS + + "(?[^^]{2,26})" //2-26 charachter name, not containing ^ //NON-NLS + + "(?:\\^" //separator //NON-NLS + + "(?:(?:\\^|(?\\d{4}))" //separator or 4 digit expiration YYMM //NON-NLS + + "(?:(?:\\^|(?\\d{3}))"//separator or 3 digit service code //NON-NLS + + "(?:(?[^?]*)" // discretionary data not containing separator //NON-NLS + + "(?:\\?" // end sentinal: ? //NON-NLS + + "(?.)" //longitudinal redundancy check //NON-NLS + + "?)?)?)?)?)?");//close nested optional groups //NON-NLS + private static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile( + /* + * Track 2 is numeric plus six punctuation symbolls :;<=>? + * + * This regex matches 12-19 digit ccns embeded in a track 2 formated + * string. This regex matches (and extracts groups) even if the + * entire track is not present as long as the part that is conforms + * to the track format. + * + */ + "[:;<=>?]?" //(optional)start sentinel //NON-NLS + + "(?[3456]([ -]?\\d){11,18})" //12-19 digits, with possible single spaces or dashes in between. first digit is 3,4,5, or 6 //NON-NLS + + "(?:[:;<=>?]" //separator //NON-NLS + + "(?:(?\\d{4})" //4 digit expiration date YYMM //NON-NLS + + "(?:(?\\d{3})" //3 digit service code //NON-NLS + + "(?:(?[^:;<=>?]*)" //discretionary data, not containing punctuation marks //NON-NLS + + "(?:[:;<=>?]" //end sentinel //NON-NLS + + "(?.)" //longitudinal redundancy check //NON-NLS + + "?)?)?)?)?)?"); //close nested optional groups //NON-NLS + private static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID); + + /** + * Constructs an object that implements a regex query that will be performed + * as a two step operation. In the first step, the Solr terms component is + * used to find any terms in the index that match the regex. In the second + * step, term queries are executed for each matched term to produce the set + * of keyword hits for the regex. + * + * @param keywordList A keyword list that contains the keyword that provides + * the regex search term for the query. + * @param keyword The keyword that provides the regex search term for + * the query. + */ + // TODO: Why is both the list and the keyword added to the state of this + // object? + // TODO: Why is the search term not escaped and given substring wildcards, + // if needed, here in the constructor? + TermsComponentQuery(KeywordList keywordList, Keyword keyword) { + this.keywordList = keywordList; + this.keyword = keyword; + this.searchTerm = keyword.getSearchTerm(); + } + + /** + * Gets the keyword list that contains the keyword that provides the regex + * search term for the query. + * + * @return The keyword list. + */ + @Override + public KeywordList getKeywordList() { + return keywordList; + } + + /** + * Gets the original search term for the query, without any escaping or, if + * it is a literal term, the addition of wildcards for a substring search. + * + * @return The original search term. + */ + @Override + public String getQueryString() { + return keyword.getSearchTerm(); + } + + /** + * Indicates whether or not the search term for the query is a literal term + * that needs have wildcards added to it to make the query a substring + * search. + * + * @return True or false. + */ + @Override + public boolean isLiteral() { + return false; + } + + /** + * Adds wild cards to the search term for the query, which makes the query a + * substring search, if it is a literal search term. + */ + @Override + public void setSubstringQuery() { + searchTerm = ".*" + searchTerm + ".*"; + } + + /** + * Escapes the search term for the query. + */ + @Override + public void escape() { + searchTerm = Pattern.quote(keyword.getSearchTerm()); + searchTermIsEscaped = true; + } + + /** + * Indicates whether or not the search term has been escaped yet. + * + * @return True or false. + */ + @Override + public boolean isEscaped() { + return searchTermIsEscaped; + } + + /** + * Gets the escaped search term for the query, assuming it has been escaped + * by a call to TermsComponentQuery.escape. + * + * @return The search term, possibly escaped. + */ + @Override + public String getEscapedQueryString() { + return this.searchTerm; + } + + /** + * Indicates whether or not the search term is a valid regex. + * + * @return True or false. + */ + @Override + public boolean validate() { + if (searchTerm.isEmpty()) { + return false; + } + try { + Pattern.compile(searchTerm); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Does nothing, not applicable to a regex query, which always searches a + * field created specifically for regex sesarches. + * + * @param field The name of a Solr document field to search. + */ + @Override + public void setField(String field) { + } + + /** + * Adds a filter to the query. + * + * @param filter The filter. + */ + // TODO: Document this better. + @Override + public void addFilter(KeywordQueryFilter filter) { + this.filters.add(filter); + } + + /** + * Executes the regex query as a two step operation. In the first step, the + * Solr terms component is used to find any terms in the index that match + * the regex. In the second step, term queries are executed for each matched + * term to produce the set of keyword hits for the regex. + * + * @return A QueryResult object or null. + * + * @throws NoOpenCoreException + */ + // TODO: Make it so this cannot cause NPEs; this method should throw + // exceptions instead of logging them and returning null. + @Override + public QueryResults performQuery() throws NoOpenCoreException { + /* + * Do a query using the Solr terms component to find any terms in the + * index that match the regex. + */ + final SolrQuery termsQuery = new SolrQuery(); + termsQuery.setRequestHandler(SEARCH_HANDLER); + termsQuery.setTerms(true); + termsQuery.setTermsRegexFlag(CASE_INSENSITIVE); + termsQuery.setTermsRegex(searchTerm); + termsQuery.addTermsField(SEARCH_FIELD); + termsQuery.setTimeAllowed(TERMS_SEARCH_TIMEOUT); + termsQuery.setShowDebugInfo(DEBUG_FLAG); + termsQuery.setTermsLimit(MAX_TERMS_QUERY_RESULTS); + List terms = null; + try { + terms = KeywordSearch.getServer().queryTerms(termsQuery).getTerms(SEARCH_FIELD); + } catch (KeywordSearchModuleException ex) { + LOGGER.log(Level.SEVERE, "Error executing the regex terms query: " + keyword.getSearchTerm(), ex); //NON-NLS + //TODO: this is almost certainly wrong and guaranteed to throw a NPE at some point!!!! + } + + /* + * Do a term query for each term that matched the regex. + */ + QueryResults results = new QueryResults(this, keywordList); + for (Term term : terms) { + /* + * If searching for credit card account numbers, do a Luhn check on + * the term and discard it if it does not pass. + */ + if (keyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + Matcher matcher = CREDIT_CARD_NUM_PATTERN.matcher(term.getTerm()); + matcher.find(); + final String ccn = CharMatcher.anyOf(" -").removeFrom(matcher.group("ccn")); + if (false == CREDIT_CARD_NUM_LUHN_CHECK.isValid(ccn)) { + continue; + } + } + + /* + * Do an ordinary query with the escaped term and convert the query + * results into a single list of keyword hits without duplicates. + * + * Note that the filters field appears to be unused. There is an old + * comment here, what does it mean? "Note: we can't set filter query + * on terms query but setting filter query on fileResults query will + * yield the same result." The filter is NOT being added to the term + * query. + */ + String escapedTerm = KeywordSearchUtil.escapeLuceneQuery(term.getTerm()); + LuceneQuery termQuery = new LuceneQuery(keywordList, new Keyword(escapedTerm, true)); + filters.forEach(termQuery::addFilter); // This appears to be unused + QueryResults termQueryResult = termQuery.performQuery(); + Set termHits = new HashSet<>(); + for (Keyword word : termQueryResult.getKeywords()) { + termHits.addAll(termQueryResult.getResults(word)); + } + results.addResult(new Keyword(term.getTerm(), false), new ArrayList<>(termHits)); + } + return results; + } + + /** + * Converts the keyword hits for a given search term into artifacts. + * + * @param searchTerm The search term. + * @param hit The keyword hit. + * @param snippet The document snippet that contains the hit + * @param listName The name of the keyword list that contained the keyword + * for which the hit was found. + * + * + * + * @return An object that wraps an artifact and a mapping by id of its + * attributes. + */ + // TODO: Are we actually making meaningful use of the KeywordCachedArtifact + // class? + @Override + public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(String searchTerm, KeywordHit hit, String snippet, String listName) { + /* + * Create either a "plain vanilla" keyword hit artifact with keyword and + * regex attributes, or a credit card account artifact with attributes + * parsed from from the snippet for the hit and looked up based on the + * parsed bank identifcation number. + */ + BlackboardArtifact newArtifact; + Collection attributes = new ArrayList<>(); + if (keyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, searchTerm)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, keyword.getSearchTerm())); + try { + newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS + return null; + } + } else { + /* + * Parse the credit card account attributes from the snippet for the + * hit. + */ + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, MODULE_NAME, Account.Type.CREDIT_CARD.name())); + Map parsedTrackAttributeMap = new HashMap<>(); + Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack1Data(parsedTrackAttributeMap, matcher); + } + matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack2Data(parsedTrackAttributeMap, matcher); + } + final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); + if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { + if (hit.isArtifactHit()) { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS + } else { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContent().getId())); //NON-NLS + } + return null; + } + attributes.addAll(parsedTrackAttributeMap.values()); + + /* + * Look up the bank name, scheme, etc. attributes for the bank + * indentification number (BIN). + */ + final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); + CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); + if (binInfo != null) { + binInfo.getScheme().ifPresent(scheme + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); + binInfo.getCardType().ifPresent(cardType + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); + binInfo.getBrand().ifPresent(brand + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); + binInfo.getBankName().ifPresent(bankName + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); + binInfo.getBankPhoneNumber().ifPresent(phoneNumber + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); + binInfo.getBankURL().ifPresent(url + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); + binInfo.getCountry().ifPresent(country + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); + binInfo.getBankCity().ifPresent(city + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); + } + + /* + * If the hit is from unused or unallocated space, record the Solr + * document id to support showing just the chunk that contained the + * hit. + */ + if (hit.getContent() instanceof AbstractFile) { + AbstractFile file = (AbstractFile) hit.getContent(); + if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS + || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { + attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); + } + } + + /* + * Create an account artifact. + */ + try { + newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS + return null; + } + } + + if (StringUtils.isNotBlank(listName)) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); + } + if (snippet != null) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + } + if (hit.isArtifactHit()) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID())); + } + + try { + newArtifact.addAttributes(attributes); + KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); + writeResult.add(attributes); + return writeResult; + } catch (TskCoreException e) { + LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS + return null; + } + } + + /** + * Parses the track 2 data from the snippet for a credit card account number + * hit and turns them into artifact attributes. + * + * @param attributesMap A map of artifact attribute objects, used to avoid + * creating duplicate attributes. + * @param matcher A matcher for the snippet. + */ + static private void parseTrack2Data(Map attributesMap, Matcher matcher) { + addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_NUMBER, "accountNumber", matcher); + addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_EXPIRATION, "expiration", matcher); + addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_SERVICE_CODE, "serviceCode", matcher); + addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_DISCRETIONARY, "discretionary", matcher); + addAttributeIfNotAlreadyCaptured(attributesMap, ATTRIBUTE_TYPE.TSK_CARD_LRC, "LRC", matcher); + } + + /** + * Parses the track 1 data from the snippet for a credit card account number + * hit and turns them into artifact attributes. The track 1 data has the + * same fields as the track two data, plus the account holder's name. + * + * @param attributesMap A map of artifact attribute objects, used to avoid + * creating duplicate attributes. + * @param matcher A matcher for the snippet. + */ + static private void parseTrack1Data(Map attributeMap, Matcher matcher) { + parseTrack2Data(attributeMap, matcher); + addAttributeIfNotAlreadyCaptured(attributeMap, ATTRIBUTE_TYPE.TSK_NAME_PERSON, "name", matcher); + } + + /** + * Creates an attribute of the the given type to the given artifact with a + * value parsed from the snippet for a credit account number hit. + * + * @param attributesMap A map of artifact attribute objects, used to avoid + * creating duplicate attributes. + * @param attrType The type of attribute to create. + * @param groupName The group name of the regular expression that was + * used to parse the attribute data. + * @param matcher A matcher for the snippet. + */ + static private void addAttributeIfNotAlreadyCaptured(Map attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) { + BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType); + attributeMap.computeIfAbsent(type, (BlackboardAttribute.Type t) -> { + String value = matcher.group(groupName); + if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) { + value = CharMatcher.anyOf(" -").removeFrom(value); + } + if (StringUtils.isNotBlank(value)) { + return new BlackboardAttribute(attrType, MODULE_NAME, value); + } + return null; + }); + } + +} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/XmlKeywordSearchList.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/XmlKeywordSearchList.java index 2bef2852e9..87d792557f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/XmlKeywordSearchList.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/XmlKeywordSearchList.java @@ -99,7 +99,7 @@ final class XmlKeywordSearchList extends KeywordSearchList { doc.appendChild(rootEl); for (String listName : theLists.keySet()) { - if (theLists.get(listName).isLocked() == true) { + if (theLists.get(listName).isEditable() == true) { continue; } KeywordList list = theLists.get(listName); @@ -123,13 +123,13 @@ final class XmlKeywordSearchList extends KeywordSearchList { for (Keyword keyword : keywords) { Element keywordEl = doc.createElement(KEYWORD_EL); - String literal = keyword.isLiteral() ? "true" : "false"; //NON-NLS + String literal = keyword.searchTermIsLiteral() ? "true" : "false"; //NON-NLS keywordEl.setAttribute(KEYWORD_LITERAL_ATTR, literal); - BlackboardAttribute.ATTRIBUTE_TYPE selectorType = keyword.getType(); + BlackboardAttribute.ATTRIBUTE_TYPE selectorType = keyword.getArtifactAttributeType(); if (selectorType != null) { keywordEl.setAttribute(KEYWORD_SELECTOR_ATTR, selectorType.getLabel()); } - keywordEl.setTextContent(keyword.getQuery()); + keywordEl.setTextContent(keyword.getSearchTerm()); listEl.appendChild(keywordEl); } rootEl.appendChild(listEl); @@ -199,7 +199,7 @@ final class XmlKeywordSearchList extends KeywordSearchList { String selector = wordEl.getAttribute(KEYWORD_SELECTOR_ATTR); if (!selector.equals("")) { BlackboardAttribute.ATTRIBUTE_TYPE selectorType = BlackboardAttribute.ATTRIBUTE_TYPE.fromLabel(selector); - keyword.setType(selectorType); + keyword.setArtifactAttributeType(selectorType); } words.add(keyword); diff --git a/NEWS.txt b/NEWS.txt index 2e33513e91..598ef1d707 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,13 +1,15 @@ ---------------- VERSION 4.2.0 -------------- Improvements: -- Credit card account search -- Encoding/decoding of extracted files to avoid anti-virus alerts/quarantine -- Options panel for managing custom tag names -- Options panel for setting external viewer associations -- Keyboard shortcut for applying Bookmark tags -- Improved PhotoRec carver ingest module cancellation responsiveness -- Results content viewer formats dates instead of showing raw seconds since epoch -- Update to PostgreSQL 9.5 +- Credit card account search. +- Encoding/decoding of extracted files to avoid anti-virus alerts/quarantine. +- Ingest history (start time, end time, status, which versions of which ingest modules were run). +- Ingest history used to warn before doing redundant analysis. +- Options panel for managing custom tag names. +- Options panel for setting external viewer associations. +- Keyboard shortcut for applying Bookmark tags. +- Improved PhotoRec carver ingest module cancellation responsiveness. +- Results content viewer formats dates. +- Update to PostgreSQL 9.5. - Assorted bug fixes and minor enhancements. ---------------- VERSION 4.1.1 -------------- diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index e4717bf430..e67d04fa17 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Wed, 12 Oct 2016 13:08:27 -0400 +#Sat, 22 Oct 2016 14:27:47 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index c4ce9b916d..c59004aab4 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Wed, 12 Oct 2016 13:08:27 -0400 +#Sat, 22 Oct 2016 14:27:47 -0400 CTL_MainWindow_Title=Autopsy 4.2.0 CTL_MainWindow_Title_No_Project=Autopsy 4.2.0 diff --git a/build-windows.xml b/build-windows.xml index 41a96016ab..d922476dd4 100644 --- a/build-windows.xml +++ b/build-windows.xml @@ -43,68 +43,40 @@ - - - - + + - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - @@ -115,57 +87,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build.xml b/build.xml index b1e2fdb14a..b1c6f4149d 100755 --- a/build.xml +++ b/build.xml @@ -92,8 +92,6 @@ - - diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile index d86bbf0ac2..bce35afa67 100755 --- a/docs/doxygen-user/Doxyfile +++ b/docs/doxygen-user/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.1 +PROJECT_NUMBER = 4.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1025,7 +1025,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = 4.1 +HTML_OUTPUT = 4.2 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index f6fb7a7038..e44aee3a51 100755 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -1063,7 +1063,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = api-docs/4.1/ +HTML_OUTPUT = api-docs/4.2/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/nbproject/project.properties b/nbproject/project.properties index 96b1848b29..023ce3ea6a 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -6,8 +6,8 @@ app.name=${branding.token} ### if left unset, version will default to today's date app.version=4.2.0 ### build.type must be one of: DEVELOPMENT, RELEASE -build.type=RELEASE -#build.type=DEVELOPMENT +#build.type=RELEASE +build.type=DEVELOPMENT project.org.sleuthkit.autopsy.imagegallery=ImageGallery update_versions=false diff --git a/thirdparty/crt/update.bat b/thirdparty/crt/update.bat deleted file mode 100644 index 807264ff07..0000000000 --- a/thirdparty/crt/update.bat +++ /dev/null @@ -1,9 +0,0 @@ -REM Updates the 32-bit and 64-bit VS Runtime dlls -REM Needs to be run from a 64-bit command prompt -REM Otherwise Windows will put 32-bit dlls in system32 -copy c:\windows\system32\msvcr100.dll win64 -copy c:\windows\system32\msvcp100.dll win64 -copy c:\windows\system32\msvcr120.dll win64 -copy c:\windows\sysWoW64\msvcr100.dll win32 -copy c:\windows\sysWow64\msvcp100.dll win32 -copy c:\windows\sysWow64\msvcr120.dll win32 diff --git a/thirdparty/crt/win32/msvcp100.dll b/thirdparty/crt/win32/msvcp100.dll deleted file mode 100755 index 8502dfae5e..0000000000 Binary files a/thirdparty/crt/win32/msvcp100.dll and /dev/null differ diff --git a/thirdparty/crt/win32/msvcr100.dll b/thirdparty/crt/win32/msvcr100.dll deleted file mode 100755 index 3e82b1aeac..0000000000 Binary files a/thirdparty/crt/win32/msvcr100.dll and /dev/null differ diff --git a/thirdparty/crt/win64/msvcp100.dll b/thirdparty/crt/win64/msvcp100.dll deleted file mode 100755 index 6f0cdf160a..0000000000 Binary files a/thirdparty/crt/win64/msvcp100.dll and /dev/null differ diff --git a/thirdparty/crt/win64/msvcr100.dll b/thirdparty/crt/win64/msvcr100.dll deleted file mode 100755 index b1c3a5e77c..0000000000 Binary files a/thirdparty/crt/win64/msvcr100.dll and /dev/null differ